Compare commits

...

62 Commits
v0.80 ... v0.81

Author SHA1 Message Date
dbbe6c0600 TWBlue 0.81 has been officially released 2016-03-25 01:24:30 -06:00
3caef5fc81 Updated translations 2016-03-24 22:33:53 -06:00
7cd58708cc Added FightingGames as an official soundpack. @deng90 2016-03-24 22:32:28 -06:00
0814af3bf4 Added notes to the Qwitter soundpack 2016-03-24 22:30:52 -06:00
72ba5a74f5 Minor changes before the next release 2016-03-24 22:18:51 -06:00
cbc301141e Updated translation templates 2016-03-24 22:17:54 -06:00
f466516289 Merge pull request #72 from Oliver2213/next-gen-fork
Fix an error where the geolocate dialog wasn't loading properly
2016-03-23 23:44:01 -06:00
4eef236b1c Merge pull request #73 from Oliver2213/shortcutfix
Fix duplicate shortcut in the reply to tweet dialog
2016-03-23 23:43:24 -06:00
Blake Oliver
ce00083aa2 Fix duplicate shortcut in the reply to tweet dialog
Mention all is now alt+m, translate stays alt+t
2016-03-22 18:35:14 -04:00
Blake Oliver
f92e05ce72 Fix an error where the geolocate dialog wasn't loading properly
For some reason, a standard tweet dialog was being used to display this information, which comes along with spell check, translate, etc; functions a simple geolocate display box doesn't need.
The error was happening because the tweet show dialog was accepting (tweet, tweetList, is_tweet=True) as args, and the call to display the geodata was only providing geodata as tweet, and false as tweetList.
This method signature was erroring out because is_tweet was set to True by default; the False was being accepted as tweetList, which the method later tries to call __str__ which it doesn't have.
I could have just added the is_tweet keyword, but then I would need to change the signature so tweetList is set to none by default, update the method, bla, bla, bla. Too much work.
Instead I added another function in wxUI.commonMessageDialogs called view_geodata, which just accepts the data in question as an argument and displays that in a message box.
2016-03-22 11:21:27 -04:00
c02a11f269 Merge pull request #71 from Oliver2213/next-gen-fork
Add hotkeys for some dialogs, making navigation much faster
2016-03-20 10:47:22 -06:00
Blake Oliver
c79e659b74 Add hotkeys for some dialogs, making navigation much faster 2016-03-20 10:26:05 -04:00
da8009aea0 If a config file is bad formed, it should print errors in logs 2016-03-19 20:59:04 -06:00
2778d2e85d Fixed the chicken nugget keymap 2016-03-19 20:58:14 -06:00
ce9a50903c Changed tokens to the stable version 2016-03-19 20:08:48 -06:00
9a7d39c125 Don'w show relationship status if there is any relationship between user 2016-03-19 20:08:20 -06:00
89759e7d49 Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2016-02-20 06:53:02 -06:00
7ca9d42f5f Added a new contributor. Welcome and thanks @ButchullaWoman! 2016-02-20 06:52:39 -06:00
Jose Manuel Delicado
6a6bec880c SessionManager.WXUI: now the dialog title is translatable 2016-02-20 13:51:12 +01:00
7b22c7d0f8 Added relationship information in user details dialogue. 2016-02-20 06:43:56 -06:00
058866831b Fixed an unhandled exception in updater 2016-02-20 06:34:26 -06:00
229f698e72 Added a mirror URL for checking updates if the main URL is not working 2016-02-19 09:15:46 -06:00
32067d3171 Added json files for the updater 2016-02-19 09:08:27 -06:00
a8f7477a1f new snapshot 2016-02-02 09:38:43 -06:00
9b6461e34e Updated translations 2016-02-02 09:37:06 -06:00
eed3f81cb8 Added a changelog html file 2016-02-02 09:30:13 -06:00
e605c750b1 Spell checker: Now it should work with the default language setting 2016-02-02 09:29:35 -06:00
f49be6cfed Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2016-02-02 09:09:56 -06:00
696faed007 Fixed a bug in the spell checker 2016-02-02 09:07:40 -06:00
Jose Manuel Delicado
46b46c4d6d Some important code fixes 2016-01-30 17:14:41 +01:00
Jose Manuel Delicado
9cdccca5ff Updated Windows dependencies with the new pa.c installer 3.1.1 2016-01-21 14:00:21 +01:00
Jose Manuel Delicado
564bee3850 Updated Windows dependencies. Updated readme with information about the new pywin32 and pycurl releases 2016-01-20 20:52:44 +01:00
Jose Manuel Delicado
79e723f740 Updated windows dependencies. Run git submodule update. 2016-01-20 20:28:54 +01:00
Jose Manuel Delicado
08a2eca98d Main.py: check if Uninstall.exe exists before importing commandline and other modules 2016-01-20 09:18:27 +01:00
Jose Manuel Delicado
c830d4b5b4 The -i and -p switches aren't required anymore on installed and portable versions 2016-01-19 19:52:09 +01:00
112391afeb Merge pull request #68 from masonasons/next-gen
Trimmed the sounds in the soundpack.
2016-01-11 13:41:06 -06:00
masonasons
39e0431b97 Trimmed the beginnings of the sounds in the Default soundpack 2016-01-09 01:01:20 -06:00
071d75926b Opus support for TWBlue. Code by @masonasons 2016-01-09 00:46:33 -06:00
56b64e5c6d Added source in view tweet dialogue. @masonasons 2016-01-09 00:45:52 -06:00
b8cfd60c9e bugfix 2016-01-09 00:25:28 -06:00
691a9ae17d Fixed remove_buffer and load_previous_items for friends and users timelines 2015-12-29 08:31:13 -06:00
214b9a8809 Fixed a bug with the restart_stream function 2015-12-28 09:05:09 -06:00
e85f54e15c Merge pull request #67 from Oliver2213/update-readme
Update the "upgrade all" command to include missing dependencies
2015-12-27 11:09:59 -06:00
blake oliver
d2245c33ce Update the "upgrade all" command to include missing dependencies 2015-12-27 11:43:25 -05:00
ff6695bba3 Added ANY in the language list for tweet searches 2015-12-27 00:03:15 -06:00
cdcdc86627 Changed go_page_up and go_page_down in the win10 default keymap 2015-12-26 23:39:31 -06:00
b4addf9329 Add update buffer option to the menu bar 2015-12-26 23:34:19 -06:00
9d0c9cfdb5 Don't get new items for people buffers 2015-12-26 23:28:46 -06:00
0afba81c71 Update buffers by pressing ctrl+win+shift+u; workaround for dm issues 2015-12-26 09:02:08 -06:00
0882e4707d Removed an unneeded import in translator module 2015-12-23 09:52:32 -06:00
78079e142f Changed handlers for sent direct messages 2015-12-23 09:51:09 -06:00
00a4203e1a Switched to the microsoft translator 2015-12-23 09:48:50 -06:00
c4fae3b70b Windows key is no longer required in keymap editor 2015-12-17 08:28:20 -06:00
b8dfa4a5e8 added audio playback to the keymap editor 2015-12-16 20:16:18 -06:00
1ca1862a08 Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2015-12-16 16:40:30 -06:00
ba76c74324 Minor changes in session controller 2015-12-16 16:32:58 -06:00
Jose Manuel Delicado
dda37f0083 Updated windows dependencies. Run git submodule update before submiting new commits. Updated readme.md with information about the portableapps.com format. 2015-12-14 13:46:55 +01:00
65c353450e Improvements in the tweets' searched 2015-12-09 17:25:46 -06:00
2bfb53abe1 Added showing followers and friends timelines 2015-12-09 11:51:37 -06:00
ab08eada81 Fixed TWBlue in non-ascii paths 2015-12-09 10:05:17 -06:00
Jose Manuel Delicado
5fcc1de9a5 Updated windows dependencies, remember to run git submodule update after pulling the repository! Removed wheel information about pycurl in readme and updated download links 2015-11-30 17:38:58 +01:00
Jose Manuel Delicado
baeb0f7ae8 Updated pa.c format to version 0.80, added romanian language to the installer 2015-11-30 11:50:59 +01:00
145 changed files with 10879 additions and 8022 deletions

View File

@@ -1,4 +1,4 @@
TWBlue -
TWBlue -
======
Copyright (C) 2015. [Technow S.L.](https://www.technow.es)
@@ -32,12 +32,11 @@ Although most dependencies can be found in the windows-dependencies directory, w
#### Dependencies packaged in windows installers
* [Python,](http://python.org) version 2.7.10
* [Python,](http://python.org) version 2.7.11
If you want to build both x86 and x64 binaries, you can install python x86 to C:\python27 and python x64 to C:\python27x64, for example.
* [wxPython](http://www.wxpython.org) for Python 2.7, version 3.0.2.0
* [Python windows extensions (pywin32)](http://www.sourceforge.net/projects/pywin32/) for python 2.7, build 219
* [Pycurl](http://pycurl.sourceforge.net) 7.19.5.1 for Python 2.7: [32-bit downloads,](https://pypi.python.org/pypi/pycurl/7.19.5.1) [64-bit downloads](http://www.lfd.uci.edu/~gohlke/pythonlibs/)
Note: the x64 version is in wheel format instead of executable installer, so you have to install it using pip. For example: C:\python27x64\scripts\pip install pycurl-7.19.5.1-cp27-none-win_amd64.whl
* [Python windows extensions (pywin32)](http://www.sourceforge.net/projects/pywin32/) for python 2.7, build 220
* [Pycurl](http://pycurl.sourceforge.net) 7.21.5 for Python 2.7: [downloads](https://pypi.python.org/pypi/pycurl/7.21.5)
* [PyEnchant,](http://pythonhosted.org/pyenchant/) version 1.6.6.
x64 version has been built by TWBlue developers, so you only will find it in windows-dependencies folder
@@ -68,14 +67,14 @@ setuptools install a script, called easy_install. You can find it in the python
* future
* pygeocoder
* suds
* arrow
* goslate
* arrow==0.6
* markdown
* pocket
* winpaths
* microsofttranslator
easy_install will automatically get the additional libraries that these packages need to work properly.
Run the following command to quickly install and upgrade all packages and their dependencies:
easy_install -Z --upgrade six configobj goslate markdown future pocket suds requests oauthlib requests-oauthlib pypubsub pygeocoder arrow python-dateutil futures
easy_install -Z --upgrade six configobj goslate markdown future suds requests oauthlib requests-oauthlib pypubsub pygeocoder arrow python-dateutil futures markdown microsofttranslator
#### Other dependencies
@@ -90,6 +89,11 @@ This dependency has been built using pure basic 4.61. Its source can be found at
* [NSIS unicode,](http://www.scratchpaper.com/) version 2.46.5
#### Dependencies required to build the portableApps.com format archive
* [PortableApps.com Launcher,](http://portableapps.com/apps/development/portableapps.com_launcher) version 2.2
* [PortableApps.com Installer,](http://portableapps.com/apps/development/portableapps.com_installer) version 3.0.20
### Running TW Blue from source
Now that you have installed all these packages, you can run TW Blue from source using a command prompt. Navigate to the repo's src directory, and type the following command:
@@ -129,3 +133,15 @@ If you want to install TWBlue in your computer, you must create the installer fi
### How to generate a translation template
Run the gen_pot.bat file, located in the tools directory. Your python installation must be in your path environment variable. The pot file will appear in the tools directory.
### How to build the portableApps.com archive
If you want to have TWBlue in your PortableApps.com platform, follow these steps:
* Navigate to the src directory, and create a binary version for x86: C:\python27\python setup.py py2exe
* Move the dist directory to the misc\pa.c format\app folder in this repo, and rename it to twblue
* Repeat these steps with Python for x64: C:\python27x64\python setup.py py2exe
* Move the new dist directory to the misc\pa.c format\app folder, and rename it to twblue64
* Run the PortableApps.com Launcher Generator, and follow the wizard. Choose the pa.c format folder and continue to generate the launcher. If the wizard is completed, you will see a file named TWBlue portable.exe inside the pa.c format folder.
* Run the PortableApps.com Installer, and follow the wizard. As in the above step, choose the pa.c format folder. When it completes, you will see a file named TWBluePortable_x.y.paf.exe inside the misc folder, where x.y is the version number.

View File

@@ -34,4 +34,6 @@ Holly Scott-Gardner
Anibal Hernández
Sussan Leiva
Brian Hartgen
PEDRO REINA COLOBON
PEDRO REINA COLOBON
Moora-Moora Arrilla
Blake Oliver

View File

@@ -1,7 +1,7 @@
[Launch]
ProgramExecutable=TWBlue\TWBlue.exe
ProgramExecutable64=TWBlue64\TWBlue.exe
CommandLineArguments=-p -d "%PAL:DataDir%"
CommandLineArguments=-d "%PAL:DataDir%"
SinglePortableAppInstance=true
MinOS=XP
SingleAppInstance=false

View File

@@ -3,9 +3,9 @@ Type=PortableApps.comFormat
Version=3.0
[Details]
Name=TWBlue portable
Name=tw blue portable
AppID=TWBluePortable
Publisher=jmdaweb & TWBlue & PortableApps.com
Publisher=jmdaweb & TW blue & PortableApps.com
Homepage=PortableApps.com/TWBluePortable
Category=Internet
Description=A portable, fast and accessible Twitter client with many options.

View File

@@ -8,10 +8,14 @@ FINNISH=true
FRENCH=true
GALICIAN=true
GERMAN=true
CROATIAN=true
HUNGARIAN=true
ITALIAN=true
JAPANESE=true
POLISH=true
PORTUGUESEBR=true
ROMANIAN=true
RUSSIAN=true
SERBIAN=true
SPANISHINTERNATIONAL=true
TURKISH=true
TURKISH=true

View File

@@ -2,7 +2,7 @@
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title>TWBlue Portable Help</title>
<title>tw blue Portable Help</title>
<link rel="alternate" href="http://portableapps.com/feeds/general" type="application/rss+xml" title="PortableApps.com">
<link rel="shortcut icon" href="Other/Help/Images/Favicon.ico">
<style type="text/css">
@@ -125,13 +125,13 @@
<body>
<div class="logo"><a href="http://portableapps.com/"><img src="Other/Help/Images/Help_Logo_Top.png" alt="PortableApps.com - Your Digital Life, Anywhere"></a></div>
<div class="content">
<h1 class="hastagline">TWBlue Portable Help</h1>
<h1 class="hastagline">tw blue Portable Help</h1>
<h2 class="tagline">A powerful and accessible Twitter client</h2>
<p>TWBlue Portable is the TWBlue whatever it is packaged with a PortableApps.com launcher as a <a href="http://portableapps.com/about/what_is_a_portable_app">portable app</a>, so you can view and send tweets on your iPod, USB flash drive, portable hard drive, etc. It has all the same features as TWBlue, plus, it leaves no personal information behind on the machine you run it on, so you can take it with you wherever you go. <a href="http://twblue.es">Learn more about TWBlue...</a></p>
<p>tw blue Portable is the tw blue whatever it is packaged with a PortableApps.com launcher as a <a href="http://portableapps.com/about/what_is_a_portable_app">portable app</a>, so you can view and send tweets on your iPod, USB flash drive, portable hard drive, etc. It has all the same features as tw blue, plus, it leaves no personal information behind on the machine you run it on, so you can take it with you wherever you go. <a href="http://twblue.es">Learn more about tw blue...</a></p>
<p><a href="http://portableapps.com/donate"><img src="Other/Help/Images/Donation_Button.png" style="vertical-align:middle" alt="Make a Donation"></a> - Support PortableApps.com's Hosting and Development</p>
<p><a href="http://portableapps.com/node/*Node ID*">Go to the TWBlue Portable Homepage &gt;&gt;</a></p>
<p><a href="http://portableapps.com/node/*Node ID*">Go to the tw blue Portable Homepage &gt;&gt;</a></p>
<p><a href="http://portableapps.com/">Get more portable apps at PortableApps.com</a></p>
<p>This software is OSI Certified Open Source Software. OSI Certified is a certification mark of the Open Source Initiative.</p>

View File

@@ -12,10 +12,10 @@ SetCompress auto
SetCompressor /solid lzma
SetDatablockOptimize on
VIAddVersionKey ProductName "TWBlue"
VIAddVersionKey LegalCopyright "Copyright 2015 Manuel Cortéz."
VIAddVersionKey ProductVersion "0.80"
VIAddVersionKey FileVersion "0.80"
VIProductVersion "0.80.0.0"
VIAddVersionKey LegalCopyright "Copyright 2016 Manuel Cortéz."
VIAddVersionKey ProductVersion "0.81"
VIAddVersionKey FileVersion "0.81"
VIProductVersion "0.81.0.0"
!insertmacro MUI_PAGE_WELCOME
!define MUI_LICENSEPAGE_RADIOBUTTONS
!insertmacro MUI_PAGE_LICENSE "license.txt"
@@ -26,7 +26,6 @@ var StartMenuFolder
!define MUI_FINISHPAGE_LINK "Visit TWBlue website"
!define MUI_FINISHPAGE_LINK_LOCATION "http://twblue.es"
!define MUI_FINISHPAGE_RUN "$INSTDIR\TWBlue.exe"
!define MUI_FINISHPAGE_RUN_PARAMETERS "-i"
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
@@ -48,6 +47,7 @@ var StartMenuFolder
!insertmacro MUI_LANGUAGE "Croatian"
!insertmacro MUI_LANGUAGE "Japanese"
!insertmacro MUI_LANGUAGE "SerbianLatin"
!insertmacro MUI_LANGUAGE "Romanian"
!insertmacro MUI_RESERVEFILE_LANGDLL
Section
SetShellVarContext All
@@ -57,10 +57,10 @@ File /r TWBlue64\*
${Else}
File /r TWBlue\*
${EndIf}
CreateShortCut "$DESKTOP\TWBlue.lnk" "$INSTDIR\TWBlue.exe" "-i"
CreateShortCut "$DESKTOP\TWBlue.lnk" "$INSTDIR\TWBlue.exe"
!insertmacro MUI_STARTMENU_WRITE_BEGIN startmenu
CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TWBlue.lnk" "$INSTDIR\TWBlue.exe" "-i"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TWBlue.lnk" "$INSTDIR\TWBlue.exe"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TWBlue on the web.lnk" "http://twblue.es"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
!insertmacro MUI_STARTMENU_WRITE_END

View File

@@ -28,6 +28,8 @@ timelines = list(default=list())
tweet_searches = list(default=list())
lists = list(default=list())
favourites_timelines = list(default=list())
followers_timelines = list(default=list())
friends_timelines = list(default=list())
trending_topic_buffers = list(default=list())
muted_buffers = list(default=list())
autoread_buffers = list(default=list(mentions, direct_messages, events))

View File

@@ -2,11 +2,13 @@
name = 'TWBlue'
snapshot = False
if snapshot == False:
version = "0.80"
version = "0.81"
update_url = 'http://twblue.es/updates/twblue_ngen.json'
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/stable.json'
else:
version = "10.97"
version = "10.99"
update_url = 'http://twblue.es/updates/snapshots_ngen.json'
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/snapshots.json'
author = u"Manuel Cortéz"
authorEmail = "manuel@manuelcortez.net"
copyright = u"Copyright (C) 2015, Technow S.L. \nCopyright (C) 2013-2015, Manuel cortéz."

View File

@@ -6,13 +6,7 @@ import application
log = logging.getLogger("commandlineLauncher")
parser = argparse.ArgumentParser(description=application.name+" command line launcher")
group = parser.add_mutually_exclusive_group()
group.add_argument("-p", "--portable", help="Use " + application.name + " as a portable application.", action="store_true", default=True)
group.add_argument("-i", "--installed", help="Use " + application.name + " as an installed application. Config files will be saved in the user data directory", action="store_true")
parser.add_argument("-d", "--data-directory", action="store", dest="directory", help="Specifies the directory where " + application.name + " saves userdata.")
args = parser.parse_args()
log.debug("Starting " + application.name + " with the following arguments: installed = %s, portable = %s and directory = %s" % (args.installed, args.portable, args.directory))
if args.installed == True: paths.mode = "installed"
elif args.portable == True:
paths.mode = "portable"
if args.directory != None: paths.directory = args.directory
log.debug("Starting " + application.name + " with the following arguments: directory = %s" % (args.directory))
if args.directory != None: paths.directory = args.directory

View File

@@ -3,6 +3,9 @@ from configobj import ConfigObj, ParseError
from validate import Validator, ValidateError
import os
import string
from logging import getLogger
log = getLogger("config_utils")
class ConfigLoadError(Exception): pass
def load_config(config_path, configspec_path=None, *args, **kwargs):
@@ -14,10 +17,12 @@ def load_config(config_path, configspec_path=None, *args, **kwargs):
except ParseError:
raise ConfigLoadError("Unable to load %r" % config_path)
validator = Validator()
validated = config.validate(validator, copy=True)
validated = config.validate(validator, preserve_errors=False, copy=True)
if validated == True:
config.write()
return config
else:
log.exception("Error in config file: {0}".format(validated,))
def is_blank(arg):
"Check if a line is blank."
@@ -25,6 +30,7 @@ def is_blank(arg):
if c not in string.whitespace:
return False
return True
def get_keys(path):
"Gets the keys of a configobj config file."
res=[]

View File

@@ -84,7 +84,9 @@ class bufferController(object):
sound.URLPlayer.stream.volume = self.session.settings["sound"]["volume"]
self.session.sound.play("volume_changed.ogg")
def start_stream(self):
def start_stream(self, mandatory=False):
if mandatory == True:
output.speak(_(u"Unable to update this buffer."))
pass
def get_more_items(self):
@@ -272,10 +274,10 @@ class baseBufferController(bufferController):
tweetsList.append(tweet)
return (tweet, tweetsList)
def start_stream(self):
def start_stream(self, mandatory=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180:
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
self.execution_time = current_time
log.debug("Starting stream for buffer %s, account %s and type %s" % (self.name, self.account, self.type))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
@@ -283,9 +285,9 @@ class baseBufferController(bufferController):
number_of_items = self.session.order_buffer(self.name, val)
log.debug("Number of items retrieved: %d" % (number_of_items,))
self.put_items_on_list(number_of_items)
if self.sound == None: return
if number_of_items > 0 and self.name != "sent_tweets" and self.name != "sent_direct_messages":
if number_of_items > 0 and self.name != "sent_tweets" and self.name != "sent_direct_messages" and self.sound != None:
self.session.sound.play(self.sound)
return number_of_items
def get_more_items(self):
elements = []
@@ -481,7 +483,13 @@ class baseBufferController(bufferController):
users = utils.get_all_users(tweet, self.session.db)
dm = messages.dm(self.session, _(u"Direct message to %s") % (screen_name,), _(u"New direct message"), users)
if dm.message.get_response() == widgetUtils.OK:
call_threaded(self.session.api_call, call_name="send_direct_message", text=dm.message.get_text(), screen_name=dm.message.get("cb"))
val = self.session.api_call(call_name="send_direct_message", text=dm.message.get_text(), screen_name=dm.message.get("cb"))
if val != None:
if self.session.settings["general"]["reverse_timelines"] == False:
self.session.db["sent_direct_messages"].append(val)
else:
self.session.db["sent_direct_messages"].insert(0, val)
pub.sendMessage("sent-dm", data=val, user=self.session.db["user_name"])
if hasattr(dm.message, "destroy"): dm.message.destroy()
@_tweets_exist
@@ -638,9 +646,9 @@ class listBufferController(baseBufferController):
self.list_id = list_id
self.kwargs["list_id"] = list_id
def start_stream(self):
def start_stream(self, mandatory=False):
self.get_user_ids()
super(listBufferController, self).start_stream()
super(listBufferController, self).start_stream(mandatory)
def get_user_ids(self):
self.users = []
@@ -714,7 +722,7 @@ class eventsBufferController(bufferController):
class peopleBufferController(baseBufferController):
def __init__(self, parent, function, name, sessionObject, account, bufferType=None, *args, **kwargs):
super(peopleBufferController, self).__init__(parent, function, name, sessionObject, account, bufferType="peoplePanel")
super(peopleBufferController, self).__init__(parent, function, name, sessionObject, account, bufferType="peoplePanel", *args, **kwargs)
log.debug("Initializing buffer %s, account %s" % (name, account,))
self.compose_function = compose.compose_followers_list
log.debug("Compose_function: %s" % (self.compose_function,))
@@ -722,7 +730,25 @@ class peopleBufferController(baseBufferController):
self.url = self.interact
def remove_buffer(self):
return False
if "-followers" in self.name:
dlg = commonMessageDialogs.remove_buffer()
if dlg == widgetUtils.YES:
if self.name[:-10] in self.session.settings["other_buffers"]["followers_timelines"]:
self.session.settings["other_buffers"]["followers_timelines"].remove(self.name[:-10])
return True
elif dlg == widgetUtils.NO:
return False
elif "-friends" in self.name:
dlg = commonMessageDialogs.remove_buffer()
if dlg == widgetUtils.YES:
if self.name[:-8] in self.session.settings["other_buffers"]["friends_timelines"]:
self.session.settings["other_buffers"]["friends_timelines"].remove(self.name[:-8])
return True
elif dlg == widgetUtils.NO:
return False
else:
output.speak(_(u"This buffer is not a timeline; it can't be deleted."), True)
return False
def onFocus(self, ev):
pass
@@ -744,19 +770,20 @@ class peopleBufferController(baseBufferController):
call_threaded(self.session.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", status=message.message.get_text(), media=message.file)
if hasattr(message.message, "destroy"): message.message.destroy()
def start_stream(self):
def start_stream(self, mandatory=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180:
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
self.execution_time = current_time
log.debug("Starting stream for %s buffer, %s account" % (self.name, self.account,))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
val = self.session.get_cursored_stream(self.name, self.function, *self.args, **self.kwargs)
self.put_items_on_list(val)
return val
def get_more_items(self):
try:
items = self.session.get_more_items(self.function, users=True, name=self.name, count=self.session.settings["general"]["max_tweets_per_call"], cursor=self.session.db[self.name]["cursor"])
items = self.session.get_more_items(self.function, users=True, name=self.name, count=self.session.settings["general"]["max_tweets_per_call"], cursor=self.session.db[self.name]["cursor"], *self.args, **self.kwargs)
except TwythonError as e:
output.speak(e.message, True)
return
@@ -841,10 +868,10 @@ class peopleBufferController(baseBufferController):
pub.sendMessage("execute-action", action="user_details")
class searchBufferController(baseBufferController):
def start_stream(self):
def start_stream(self, mandatory=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180:
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
self.execution_time = current_time
log.debug("Starting stream for %s buffer, %s account and %s type" % (self.name, self.account, self.type))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
@@ -857,6 +884,7 @@ class searchBufferController(baseBufferController):
self.put_items_on_list(num)
if num > 0:
self.session.sound.play("search_updated.ogg")
return num
def remove_buffer(self):
dlg = commonMessageDialogs.remove_buffer()
@@ -879,10 +907,10 @@ class searchPeopleBufferController(peopleBufferController):
self.kwargs = kwargs
self.function = function
def start_stream(self):
def start_stream(self, mandatory=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180:
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
self.execution_time = current_time
log.debug("starting stream for %s buffer, %s account and %s type" % (self.name, self.account, self.type))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
@@ -896,6 +924,7 @@ class searchPeopleBufferController(peopleBufferController):
self.put_items_on_list(number_of_items)
if number_of_items > 0:
self.session.sound.play("search_updated.ogg")
return number_of_items
def remove_buffer(self):
dlg = commonMessageDialogs.remove_buffer()
@@ -1052,13 +1081,14 @@ class conversationBufferController(searchBufferController):
self.put_items_on_list(number_of_items)
if number_of_items > 0:
self.session.sound.play("search_updated.ogg")
return number_of_items
def remove_buffer(self):
dlg = commonMessageDialogs.remove_buffer()
if dlg == widgetUtils.YES:
self.timer.cancel()
return True
elif dlg == WidgetUtils.NO:
elif dlg == widgetUtils.NO:
return False
class pocketBufferController(baseBufferController):

View File

@@ -118,6 +118,7 @@ class Controller(object):
pub.subscribe(self.manage_unblocked_user, "unblocked-user")
pub.subscribe(self.manage_item_in_timeline, "item-in-timeline")
pub.subscribe(self.manage_item_in_list, "item-in-list")
pub.subscribe(self.restart_streams_, "restart_streams")
widgetUtils.connect_event(self.view, widgetUtils.CLOSE_EVENT, self.exit_)
def bind_other_events(self):
@@ -177,8 +178,10 @@ class Controller(object):
widgetUtils.connect_event(self.view.nb, widgetUtils.NOTEBOOK_PAGE_CHANGED, self.buffer_changed)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.report_error, self.view.reportError)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_documentation, self.view.doc)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_changelog, self.view.changelog)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.add_to_list, self.view.addToList)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_from_list, self.view.removeFromList)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.update_buffer, self.view.update_buffer)
def set_systray_icon(self):
self.systrayIcon = sysTrayIcon.SysTrayIcon()
@@ -344,6 +347,24 @@ class Controller(object):
self.view.insert_buffer(tl.buffer, name=_(u"Likes for {}").format(i,), pos=self.view.search("favs_timelines", session.db["user_name"]))
tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start()
followers_timelines = buffersController.emptyPanel(self.view.nb, "followers_timelines", session.db["user_name"])
self.buffers.append(followers_timelines)
self.view.insert_buffer(followers_timelines.buffer , name=_(u"Followers' Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["followers_timelines"]:
tl = buffersController.peopleBufferController(self.view.nb, "get_followers_list", "%s-followers" % (i,), session, session.db["user_name"], screen_name=i)
self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"Followers for {}").format(i,), pos=self.view.search("favs_timelines", session.db["user_name"]))
tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start()
friends_timelines = buffersController.emptyPanel(self.view.nb, "friends_timelines", session.db["user_name"])
self.buffers.append(friends_timelines)
self.view.insert_buffer(friends_timelines.buffer , name=_(u"Friends' Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["friends_timelines"]:
tl = buffersController.peopleBufferController(self.view.nb, "get_friends_list", "%s-friends" % (i,), session, session.db["user_name"], screen_name=i)
self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"Friends for {}").format(i,), pos=self.view.search("favs_timelines", session.db["user_name"]))
tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start()
lists = buffersController.emptyPanel(self.view.nb, "lists", session.db["user_name"])
self.buffers.append(lists)
self.view.insert_buffer(lists.buffer , name=_(u"Lists"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
@@ -410,13 +431,14 @@ class Controller(object):
if dlg.get("tweets") == True:
if term not in buffer.session.settings["other_buffers"]["tweet_searches"]:
buffer.session.settings["other_buffers"]["tweet_searches"].append(term)
search = buffersController.searchBufferController(self.view.nb, "search", "%s-searchterm" % (term,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", q=term, count=buffer.session.settings["general"]["max_tweets_per_call"])
args = {"lang": dlg.get_language(), "result_type": dlg.get_result_type()}
search = buffersController.searchBufferController(self.view.nb, "search", "%s-searchterm" % (term,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", q=term, count=buffer.session.settings["general"]["max_tweets_per_call"], **args)
else:
log.error("A buffer for the %s search term is already created. You can't create a duplicate buffer." % (term,))
return
elif dlg.get("users") == True:
search = buffersController.searchPeopleBufferController(self.view.nb, "search_users", "%s-searchUser" % (term,), buffer.session, buffer.session.db["user_name"], bufferType=None, q=term)
search.start_stream()
search.start_stream(mandatory=True)
pos=self.view.search("searches", buffer.session.db["user_name"])
self.insert_buffer(search, pos)
self.view.insert_buffer(search.buffer, name=_(u"Search for {}").format(term), pos=pos)
@@ -759,7 +781,8 @@ class Controller(object):
return
answer = commonMessageDialogs.protected_user()
if answer == widgetUtils.NO: return
if dlg.get_action() == "tweets":
tl_type = dlg.get_action()
if tl_type == "tweets":
if usr["statuses_count"] == 0:
commonMessageDialogs.no_tweets()
return
@@ -775,7 +798,7 @@ class Controller(object):
buff.session.settings["other_buffers"]["timelines"].append(dlg.get_user())
pub.sendMessage("restart-streams", streams=["timelinesStream"], session=buff.session)
buff.session.sound.play("create_timeline.ogg")
else:
elif tl_type == "favourites":
if usr["favourites_count"] == 0:
commonMessageDialogs.no_favs()
return
@@ -792,6 +815,40 @@ class Controller(object):
tl.timer.start()
buff.session.settings["other_buffers"]["favourites_timelines"].append(dlg.get_user())
buff.session.sound.play("create_timeline.ogg")
elif tl_type == "followers":
if usr["followers_count"] == 0:
commonMessageDialogs.no_followers()
return
if dlg.get_user() in buff.session.settings["other_buffers"]["followers_timelines"]:
commonMessageDialogs.timeline_exist()
return
tl = buffersController.peopleBufferController(self.view.nb, "get_followers_list", "%s-followers" % (dlg.get_user(),), buff.session, buff.session.db["user_name"], screen_name=dlg.get_user())
pos=self.view.search("followers_timelines", buff.session.db["user_name"])
self.insert_buffer(tl, pos+1)
# self.buffers.insert(pos+1, tl)
self.view.insert_buffer(buffer=tl.buffer, name=_(u"Followers for {}").format(dlg.get_user()), pos=pos)
tl.start_stream()
tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start()
buff.session.settings["other_buffers"]["followers_timelines"].append(dlg.get_user())
buff.session.sound.play("create_timeline.ogg")
elif tl_type == "friends":
if usr["friends_count"] == 0:
commonMessageDialogs.no_friends()
return
if dlg.get_user() in buff.session.settings["other_buffers"]["friends_timelines"]:
commonMessageDialogs.timeline_exist()
return
tl = buffersController.peopleBufferController(self.view.nb, "get_friends_list", "%s-friends" % (dlg.get_user(),), buff.session, buff.session.db["user_name"], screen_name=dlg.get_user())
pos=self.view.search("friends_timelines", buff.session.db["user_name"])
self.insert_buffer(tl, pos+1)
self.view.insert_buffer(buffer=tl.buffer, name=_(u"Friends for {}").format(dlg.get_user()), pos=pos)
tl.start_stream()
tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start()
buff.session.settings["other_buffers"]["friends_timelines"].append(dlg.get_user())
buff.session.sound.play("create_timeline.ogg")
else:
commonMessageDialogs.user_not_exist()
@@ -866,7 +923,7 @@ class Controller(object):
x = tweet["coordinates"]["coordinates"][0]
y = tweet["coordinates"]["coordinates"][1]
address = geocoder.reverse_geocode(y, x)
dlg = messages.viewTweet(address[0].__str__(), False)
dlg = commonMessageDialogs.view_geodata(address[0].__str__())
else:
output.speak(_(u"There are no coordinates in this tweet"))
except GeocoderError:
@@ -1391,6 +1448,11 @@ class Controller(object):
webbrowser.open("manual.html")
os.chdir("../../")
def view_changelog(self, *args, **kwargs):
os.chdir("documentation")
webbrowser.open("changelog.html")
os.chdir("../")
def insert_buffer(self, buffer, position):
self.buffers.insert(position, buffer)
@@ -1405,5 +1467,21 @@ class Controller(object):
if hasattr(self, action):
getattr(self, action)()
def restart_streams_(self, session):
for i in self.buffers:
if i.session != None and i.session.session_id == session:
i.start_stream()
def __del__(self):
config.app.write()
config.app.write()
def update_buffer(self, *args, **kwargs):
bf = self.get_current_buffer()
if not hasattr(bf, "start_stream"):
output.speak(_(u"Unable to update this buffer."))
return
else:
output.speak(_(u"Updating buffer..."))
n = bf.start_stream(mandatory=True)
if n != None:
output.speak(_(u"{0} items retrieved").format(n,))

View File

@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
import re
import platform
system = platform.system()
import widgetUtils
@@ -35,7 +36,7 @@ class basicTweet(object):
def translate(self, event=None):
dlg = translator.gui.translateDialog()
if dlg.get_response() == widgetUtils.OK:
text_to_translate = self.message.get_text().encode("utf-8")
text_to_translate = self.message.get_text()
source = [x[0] for x in translator.translator.available_languages()][dlg.get("source_lang")]
dest = [x[0] for x in translator.translator.available_languages()][dlg.get("dest_lang")]
msg = translator.translator.translate(text=text_to_translate, source=source, target=dest)
@@ -173,13 +174,14 @@ class viewTweet(basicTweet):
text = text + "@%s: %s\n" % (tweetList[i]["user"]["screen_name"], tweetList[i]["text"])
rt_count = str(tweet["retweet_count"])
favs_count = str(tweet["favorite_count"])
source = str(re.sub(r"(?s)<.*?>", "", tweet["source"]))
if text == "":
if tweet.has_key("retweeted_status"):
text = "rt @%s: %s" % (tweet["retweeted_status"]["user"]["screen_name"], tweet["retweeted_status"]["text"])
else:
text = tweet["text"]
text = self.clear_text(text)
self.message = message.viewTweet(text, rt_count, favs_count)
self.message = message.viewTweet(text, rt_count, favs_count,source)
self.message.set_title(len(text))
else:
text = tweet

View File

@@ -261,24 +261,24 @@ class accountSettingsController(globalSettingsController):
if change == True:
self.dialog.buffers.change_selected_item()
def manage_pocket(self, *args, **kwargs):
if self.dialog.services.get_pocket_status() == _(u"Connect your Pocket account"):
self.connect_pocket()
else:
self.disconnect_pocket()
# def manage_pocket(self, *args, **kwargs):
# if self.dialog.services.get_pocket_status() == _(u"Connect your Pocket account"):
# self.connect_pocket()
# else:
# self.disconnect_pocket()
def connect_pocket(self):
dlg = self.dialog.services.show_pocket_dialog()
if dlg == widgetUtils.YES:
request_token = pocket.Pocket.get_request_token(consumer_key=keys.keyring.get("pocket_consumer_key"), redirect_uri="http://127.0.0.1:8080")
auth_url = pocket.Pocket.get_auth_url(code=request_token, redirect_uri="http://127.0.0.1:8080")
webbrowser.open_new_tab(auth_url)
httpd = BaseHTTPServer.HTTPServer(('127.0.0.1', 8080), authorisationHandler.handler)
while authorisationHandler.logged == False:
httpd.handle_request()
user_credentials = pocket.Pocket.get_credentials(consumer_key=keys.keyring.get("pocket_consumer_key"), code=request_token)
self.dialog.services.set_pocket(True)
self.config["services"]["pocket_access_token"] = user_credentials["access_token"]
# def connect_pocket(self):
# dlg = self.dialog.services.show_pocket_dialog()
# if dlg == widgetUtils.YES:
# request_token = pocket.Pocket.get_request_token(consumer_key=keys.keyring.get("pocket_consumer_key"), redirect_uri="http://127.0.0.1:8080")
# auth_url = pocket.Pocket.get_auth_url(code=request_token, redirect_uri="http://127.0.0.1:8080")
# webbrowser.open_new_tab(auth_url)
# httpd = BaseHTTPServer.HTTPServer(('127.0.0.1', 8080), authorisationHandler.handler)
# while authorisationHandler.logged == False:
# httpd.handle_request()
# user_credentials = pocket.Pocket.get_credentials(consumer_key=keys.keyring.get("pocket_consumer_key"), code=request_token)
# self.dialog.services.set_pocket(True)
# self.config["services"]["pocket_access_token"] = user_credentials["access_token"]
def disconnect_dropbox(self):
self.config["services"]["pocket_access_token"] = ""

View File

@@ -31,6 +31,8 @@ class profileController(object):
def get_data(self, screen_name):
self.data = self.session.twitter.twitter.show_user(screen_name=screen_name)
if screen_name != self.session.db["user_name"]:
self.friendship_status = self.session.twitter.twitter.show_friendship(source_screen_name=self.session.db["user_name"], target_screen_name=screen_name)
def fill_profile_fields(self):
self.dialog.set_name(self.data["name"])
@@ -90,6 +92,17 @@ class profileController(object):
if self.data["protected"] == True: protected = _(u"Yes")
else: protected = _(u"No")
string = string+ _(u"Protected: %s\n") % (protected)
if hasattr(self, "friendship_status"):
relation = False
friendship = "Relationship: "
if self.friendship_status["relationship"]["target"]["followed_by"]:
friendship += _(u"You follow {0}. ").format(self.data["name"],)
relation = True
if self.friendship_status["relationship"]["target"]["following"]:
friendship += _(u"{0} is following you.").format(self.data["name"],)
relation = True
if relation == True:
string = string+friendship+"\n"
string = string+_(u"Followers: %s\n Friends: %s\n") % (self.data["followers_count"], self.data["friends_count"])
if self.data["verified"] == True: verified = _(u"Yes")
else: verified = _(u"No")

View File

@@ -1,153 +1,10 @@
# encoding: utf-8
#
# Copyright (C) 2013 Mesar Hameed <mhameed@src.gnome.org>
# This file is covered by the GNU General Public License.
# -*- coding: utf-8 -*-
from microsofttranslator import Translator
import os
import re
import sys
import threading
from time import sleep
from random import randint
import logging
log = logging.getLogger("translator")
import urllib2
def translate(text="", source="auto", target="en"):
t = Translator("twblue", "4KZA26GYIfmVAqQA/z16Hlucbg64hVSDTIpRjT2FqIU=")
return t.translate(text, target)
# Each group has to be a class of possible breaking points for the writing script.
# Usually this is the major syntax marks, such as:
# full stop, comma, exclaim, question, etc.
arabicBreaks = u'[،؛؟]'
# Thanks to Talori in the NVDA irc room:
# U+3000 to U+303F, U+FE10 to U+FE1F, U+FE30 to U+FE6F, U+FF01 to U+FF60
chineseBreaks = u'[ -〿︐-︟︰-﹯!-⦆]'
latinBreaks = r'[.,!?;:\n]'
splitReg = re.compile(u"{arabic}|{chinese}|{latin}".format(arabic=arabicBreaks, chinese=chineseBreaks, latin=latinBreaks))
def translate(text, source="auto", target="en"):
if source == "": source = "auto"
t = Translator(lang_from=source, lang_to=target, text=text)
t.start()
while t.isAlive():
sleep(0.1)
t.join()
return t.translation
def splitChunks(text, chunksize):
pos = 0
potentialPos = 0
for splitMark in splitReg.finditer(text):
if (splitMark.start() - pos +1) < chunksize:
potentialPos = splitMark.start()
continue
else:
yield text[pos:potentialPos+1]
pos = potentialPos + 1
potentialPos = splitMark.start()
yield text[pos:]
class Translator(threading.Thread):
def __init__(self, lang_from, lang_to, text, lang_swap=None, chunksize=350, *args, **kwargs):
super(Translator, self).__init__(*args, **kwargs)
self._stop = threading.Event()
self.text = text
self.chunksize = chunksize
self.lang_to = lang_to
self.lang_from = lang_from
self.lang_swap = lang_swap
self.translation = ''
self.lang_translated = ''
self.firstChunk = True
def stop(self):
self._stop.set()
def run(self):
for chunk in splitChunks(self.text, self.chunksize):
# Make sure we don't send requests to google too often.
# Try to simulate a human.
if not self.firstChunk:
sleep(randint(1, 10))
req = self.buildRequest(chunk, self.lang_from, self.lang_to)
try:
response = urllib2.urlopen(req)
translation, lang_translated = self.parseData(response)
if self.firstChunk and self.lang_from == "auto" and lang_translated == self.lang_to and self.lang_swap is not None:
self.lang_to = self.lang_swap
self.firstChunk = False
req = self.buildRequest(chunk.encode('utf-8'), self.lang_from, self.lang_to)
response = urllib2.urlopen(req)
translation, lang_translated = self.parseData(response)
except Exception as e:
log.exception("Can not translate text '%s'" %chunk)
# We have probably been blocked, so stop trying to translate.
raise e
self.translation += translation
# some adjustment, better to do on full text
self.translation = self.fixNewlines(self.translation)
self.lang_translated = lang_translated
def buildRequest(self, text, lang_from, lang_to):
"""Build POST request which will be sent to Google."""
urlTemplate = 'http://translate.google.com/translate_a/single?client=t&sl={lang_from}&tl={lang_to}&ie=utf-8&oe=utf-8&dt=t&dt=bd&tk='
url = urlTemplate.format(lang_from=lang_from, lang_to=lang_to)
header = {'User-agent': 'Mozilla/5.0', 'Content-Type': 'application/x-www-form-urlencoded'}
data = 'text=%s' %urllib2.quote(text)
req = urllib2.Request(url, data, header)
return req
def parseData(self, response):
"""Parse unstructured response."""
data = response.readlines()[0]
# get segments with couples ["translation","original text"]
l1, l2 = data.split(']],', 1)
translation = l1[3:]
if l2.startswith('[[\"'):
# get list of synonyms
syn = l2[l2.find(',[')+1:l2.find(']')].split(',')
temp = ', '.join([x.replace('\"', '') for x in syn])
else:
# get a list with each couple as item
sentences = translation.split('],[')
temp = ''
# get translation, removing first char (quote symbol)
for item in sentences:
item = item.split('\",\"', 1)[0][1:]
# join all translations
temp = ' '.join([temp, item])
translation = temp.decode('string-escape').decode('utf-8')
translation = self.fixPunctuation(translation)
# get the language of original text
tempLang = data.partition(']],,\"')[2]
lang = tempLang[:tempLang.find('\"')]
if lang == '':
lang = _("unavailable")
return translation, lang
def fixPunctuation(self, translation):
"""Clean text from space before punctuation symbol."""
# list of potentially positions of spaces to remove
spacePos = []
for puncMark in splitReg.finditer(translation):
spacePos.append(puncMark.start()-1)
if len(spacePos) == 0:
return translation
fixedTranslation = ''
for n in xrange(0,len(translation)):
temp = translation[n]
if n in spacePos and temp == ' ':
continue
else:
fixedTranslation += temp
return fixedTranslation
def fixNewlines(self, translation):
"""Adjust newlines and (subsequent or double) spaces."""
fixes = [('\r\n ', '\r\n'), ('\n ', '\r\n'), (' ', ' ')]
for fix in fixes:
translation = translation.replace(fix[0], fix[1])
# first char is a space, so...
return translation[1:]
languages = {
"af": _(u"Afrikaans"),
@@ -244,8 +101,8 @@ languages = {
}
def available_languages():
l = languages.keys()
d = languages.values()
l.insert(0, '')
d.insert(0, _(u"autodetect"))
return sorted(zip(l, d))
l = languages.keys()
d = languages.values()
l.insert(0, '')
d.insert(0, _(u"autodetect"))
return sorted(zip(l, d))

View File

@@ -17,7 +17,7 @@ send_dm = string(default="control+win+d")
user_details = string(default="control+win+shift+u")
exit = string(default="control+win+q")
open_timeline = string(default="control+win+u")
remove_buffer = string(default="control+win+backspace")
remove_buffer = string(default="control+win+back")
audio = string(default="control+win+return")
url = string(default="control+win+b")
go_home = string(default="control+win+home")
@@ -28,7 +28,8 @@ repeat_item = string(default="control+win+space")
copy_to_clipboard = string(default="control+win+shift+c")
search = string(default="control+win+/")
find = string(default="control+win+shift+/")
check_for_updates = string(default="alt+win+u)
check_for_updates = string(default="alt+win+u")
list_manager = string(default="control+win+shift+l")
configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+win+shift+u")

View File

@@ -50,4 +50,5 @@ get_trending_topics = string(default="control+win+shift+t")
check_for_updates = string(default="control+win+u")
list_manager = string(default="control+win+shift+l")
configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+win+shift+u")

View File

@@ -30,8 +30,8 @@ volume_up = string(default="alt+win+shift+up")
go_home = string(default="alt+win+home")
volume_down = string(default="alt+win+shift+down")
go_end = string(default="alt+win+end")
go_page_up = string(default="alt+win+pageup")
go_page_down = string(default="alt+win+pagedown")
go_page_up = string(default="control+win+pageup")
go_page_down = string(default="control+win+pagedown")
update_profile = string(default="alt+win+p")
delete = string(default="alt+win+delete")
clear_buffer = string(default="alt+win+shift+delete")
@@ -52,4 +52,5 @@ get_trending_topics = string(default="alt+win+t")
check_for_updates = string(default="alt+win+u")
list_manager = string(default="alt+win+shift+l")
configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+alt+shift+u")

View File

@@ -53,4 +53,5 @@ get_trending_topics = string(default="control+win+t")
check_for_updates = string(default="control+win+u")
list_manager = string(default="control+win+shift+l")
configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+win+shift+u")

View File

@@ -53,4 +53,5 @@ find = string(default="control+win+{")
check_for_updates = string(default="control+win+u")
list_manager = string(default="control+win+shift+l")
configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+win+shift+u")

View File

@@ -5,16 +5,16 @@ import exceptions
from ctypes import c_char_p
from libloader import load_library
import paths
if application.snapshot == True:
if platform.architecture()[0][:2] == "32":
lib = load_library("snapshot_api_keys32", x86_path=paths.app_path("keys/lib"))
else:
lib = load_library("snapshot_api_keys64", x64_path=paths.app_path("keys/lib"))
#if application.snapshot == True:
# if platform.architecture()[0][:2] == "32":
# lib = load_library("snapshot_api_keys32", x86_path=paths.app_path("keys/lib"))
# else:
# lib = load_library("snapshot_api_keys64", x64_path=paths.app_path("keys/lib"))
#else:
if platform.architecture()[0][:2] == "32":
lib = load_library("stable_api_keys32", x86_path=paths.app_path("keys/lib"))
else:
if platform.architecture()[0][:2] == "32":
lib = load_library("stable_api_keys32", x86_path=paths.app_path("keys/lib"))
else:
lib = load_library("stable_api_keys64", x64_path=paths.app_path("keys/lib"))
lib = load_library("stable_api_keys64", x64_path=paths.app_path("keys/lib"))
# import linuxKeys
# lib = linuxKeys

View File

@@ -50,4 +50,6 @@ actions = {
"lists_manager": _(u"Opens the list manager, which allows you to create, edit, delete and open lists in buffers."),
"configuration": _(u"Opens the global settings dialogue"),
"accountConfiguration": _(u"Opens the account settings dialogue"),
"audio": _(u"Try to play an audio file"),
"update_buffer": _(u"Updates the buffer and retrieves possible lost items there."),
}

View File

@@ -37,13 +37,10 @@ class KeystrokeEditor(object):
def get_edited_keystroke(self, dialog):
keys = []
if dialog.get("win") == False:
wx_ui.no_win_message()
return
if dialog.get("control") == True:
keys.append("control")
# if dialog.get("win") == True:
keys.append("win")
if dialog.get("win") == True:
keys.append("win")
if dialog.get("alt") == True:
keys.append("alt")
if dialog.get("shift") == True:

View File

@@ -32,7 +32,7 @@ def load_library(library, x86_path='.', x64_path='.', *args, **kwargs):
loaded = _do_load(lib, *args, **kwargs)
if loaded is not None:
return loaded
raise LibraryLoadError('unable to load %r. Provided library path: %r' % (library, path))
raise LibraryLoadError('unable to load %r. Provided library path: %r' % (library, lib))
def _do_load(file, *args, **kwargs):
loader = TYPES[platform.system()]['loader']

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,9 @@ if system == "Windows":
sys.stderr = open(os.path.join(os.getenv("temp"), "stderr.log"), "w")
import languageHandler
import paths
#check if TWBlue is installed (Windows only)
if os.path.exists(paths.app_path(u"Uninstall.exe")):
paths.mode="installed"
import commandline
import config
import sound

View File

@@ -7,7 +7,7 @@ from platform_utils import paths as paths_
from functools import wraps
mode = None
mode = "portable"
directory = None
log = logging.getLogger("paths")
@@ -55,7 +55,8 @@ def data_path(app_name='TW blue'):
# data_path = os.path.join(shlobj.SHGetFolderPath(0, shlobj.CSIDL_APPDATA), app_name)
# else:
if platform.system() == "Windows":
data_path = os.path.join(os.getenv("AppData"), app_name)
import winpaths
data_path = os.path.join(winpaths.get_appdata(), app_name)
else:
data_path = os.path.join(os.environ['HOME'], ".%s" % app_name)
if not os.path.exists(data_path):

View File

@@ -32,7 +32,7 @@ def load_library(library, x86_path='.', x64_path='.', *args, **kwargs):
loaded = _do_load(lib, *args, **kwargs)
if loaded is not None:
return loaded
raise LibraryLoadError('unable to load %r. Provided library path: %r' % (library, path))
raise LibraryLoadError('unable to load %r. Provided library path: %r' % (library, lib))
def _do_load(file, *args, **kwargs):
loader = TYPES[platform.system()]['loader']

View File

@@ -194,6 +194,7 @@ class Session(object):
if report_success:
output.speak(_("%s succeeded.") % action)
if _sound != None: self.sound.play(_sound)
return val
def search(self, name, *args, **kwargs):
tl = self.twitter.twitter.search(*args, **kwargs)
@@ -278,8 +279,7 @@ class Session(object):
tl = self.call_paged(function, sinze_id=last_id, *args, **kwargs)
self.order_buffer(name, tl)
@_require_login
def get_cursored_stream(self, name, function, items="users", *args, **kwargs):
def get_cursored_stream(self, name, function, items="users", get_previous=False, *args, **kwargs):
""" Gets items for API calls that require using cursors to paginate the results.
name str: Name to save it in the database.
@@ -289,7 +289,7 @@ class Session(object):
items_ = []
try:
if self.db[name].has_key("cursor"):
if self.db[name].has_key("cursor") and get_previous:
cursor = self.db[name]["cursor"]
else:
cursor = -1
@@ -352,7 +352,7 @@ class Session(object):
self.logged = False
self.twitter = twitter.twitter.twitter()
self.login(False)
# pub.sendMessage("streamError", session=self.session_id)
pub.sendMessage("restart_streams", session=self.session_id)
if self.reconnection_function_active == True: return
self.reconnection_function_active = True
if not hasattr(self, "main_stream"):

View File

@@ -2,6 +2,7 @@
import shutil
import widgetUtils
import platform
import output
if platform.system() == "Windows":
import wxUI as view
from controller import settings

View File

@@ -4,7 +4,7 @@ from multiplatform_widgets import widgets
import application
class sessionManagerWindow(wx.Dialog):
def __init__(self):
super(sessionManagerWindow, self).__init__(parent=None, title="Session manager", size=wx.DefaultSize)
super(sessionManagerWindow, self).__init__(parent=None, title=_(u"Session manager"), size=wx.DefaultSize)
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
label = wx.StaticText(panel, -1, _(u"Accounts list"), size=wx.DefaultSize)

View File

@@ -28,22 +28,22 @@ from glob import glob
import wx
def get_architecture_files():
if platform.architecture()[0][:2] == "32":
return [
if platform.architecture()[0][:2] == "32":
return [
("", ["../windows-dependencies/x86/oggenc2.exe", "../windows-dependencies/x86/bootstrap.exe"]),
("Microsoft.VC90.CRT", glob("../windows-dependencies/x86/Microsoft.VC90.CRT/*")),
("Microsoft.VC90.MFC", glob("../windows-dependencies/x86/Microsoft.VC90.MFC/*")),]
elif platform.architecture()[0][:2] == "64":
return [
elif platform.architecture()[0][:2] == "64":
return [
("", ["../windows-dependencies/x64/oggenc2.exe", "../windows-dependencies/x86/bootstrap.exe"]),
("Microsoft.VC90.CRT", glob("../windows-dependencies/x64/Microsoft.VC90.CRT/*")),
("Microsoft.VC90.MFC", glob("../windows-dependencies/x64/Microsoft.VC90.MFC/*")),]
def get_data():
import accessible_output2
import sound_lib
import enchant
return [
import accessible_output2
import sound_lib
import enchant
return [
("", ["conf.defaults", "app-configuration.defaults", "icon.ico"]),
("requests", ["cacert.pem"]),
("accessible_output2/lib", glob("accessible_output2/lib/*.dll")),
@@ -52,33 +52,33 @@ def get_data():
]+get_sounds()+get_locales()+get_documentation()+sound_lib.find_datafiles()+accessible_output2.find_datafiles()+enchant.utils.win32_data_files()+get_architecture_files()+wx_files()
def get_documentation ():
answer = []
depth = 6
for root, dirs, files in os.walk('documentation'):
if depth == 0:
break
new = (root, glob(os.path.join(root, "*.html")))
answer.append(new)
depth -= 1
return answer
answer = []
depth = 6
for root, dirs, files in os.walk('documentation'):
if depth == 0:
break
new = (root, glob(os.path.join(root, "*.html")))
answer.append(new)
depth -= 1
return answer
def get_sounds():
answer = []
depth = 6
for root, dirs, files in os.walk('sounds'):
if depth == 0:
break
new = (root, glob(os.path.join(root, "*.ogg")))
answer.append(new)
depth -= 1
return answer
answer = []
depth = 6
for root, dirs, files in os.walk('sounds'):
if depth == 0:
break
new = (root, glob(os.path.join(root, "*.ogg")))
answer.append(new)
depth -= 1
return answer
def get_locales():
answer = []
for root, dirs, files in os.walk('locales'):
new = (root, glob(os.path.join(root, '*.mo')))
answer.append(new)
return answer
answer = []
for root, dirs, files in os.walk('locales'):
new = (root, glob(os.path.join(root, '*.mo')))
answer.append(new)
return answer
def wx_files():
wxDir=wx.__path__[0]
@@ -99,7 +99,7 @@ def wx_files():
return list(localeMoFiles)
if __name__ == '__main__':
setup(
setup(
name = application.name,
author = application.author,
author_email = application.authorEmail,

View File

@@ -14,6 +14,8 @@ system = platform.system()
from mysc.repeating_timer import RepeatingTimer
from mysc.thread_utils import call_threaded
import application
import tempfile
import glob
URLPlayer = None
def setup():

View File

@@ -4,5 +4,6 @@ import platform
if platform.system() != 'Darwin':
import sound_lib.external.pybass_aac
import sound_lib.external.pybass_alac
import sound_lib.external.pybassopus
import sound_lib.external.pybassflac
import sound_lib.external.pybassmidi

47
src/sound_lib/external/pybassopus.py vendored Normal file
View File

@@ -0,0 +1,47 @@
# Copyright(c) Max Kolosov 2009 maxkolosov@inbox.ru
# http://vosolok2008.narod.ru
# BSD license
__version__ = '0.1'
__versionTime__ = '2009-11-15'
__author__ = 'Max Kolosov <maxkolosov@inbox.ru>'
__doc__ = '''
pybassflac.py - is ctypes python module for
BASSFLAC - extension to the BASS audio library,
enabling the playing of FLAC (Free Lossless Audio Codec) encoded files.
'''
import os, sys, ctypes, pybass
from paths import x86_path, x64_path
import libloader
bassopus_module = libloader.load_library('bassopus', x86_path=x86_path, x64_path=x64_path)
func_type = libloader.get_functype()
#Register the plugin with the Bass plugin system.
pybass.BASS_PluginLoad(libloader.find_library_path('bassopus', x86_path=x86_path, x64_path=x64_path), 0)
QWORD = pybass.QWORD
HSTREAM = pybass.HSTREAM
DOWNLOADPROC = pybass.DOWNLOADPROC
BASS_FILEPROCS = pybass.BASS_FILEPROCS
# BASS_CHANNELINFO type
BASS_CTYPE_STREAM_OPUS = 0x11200
#HSTREAM BASSOPUSDEF(BASS_OPUS_StreamCreateFile)(BOOL mem, const void *file, QWORD offset, QWORD length, DWORD flags);
BASS_OPUS_StreamCreateFile = func_type(HSTREAM, ctypes.c_byte, ctypes.c_void_p, QWORD, QWORD, ctypes.c_ulong)(('BASS_OPUS_StreamCreateFile', bassopus_module))
#HSTREAM BASSFLACDEF(BASS_FLAC_StreamCreateURL)(const char *url, DWORD offset, DWORD flags, DOWNLOADPROC *proc, void *user);
BASS_OPUS_StreamCreateURL = func_type(HSTREAM, ctypes.c_char_p, ctypes.c_ulong, ctypes.c_ulong, DOWNLOADPROC, ctypes.c_void_p)(('BASS_OPUS_StreamCreateURL', bassopus_module))
#HSTREAM BASSFLACDEF(BASS_FLAC_StreamCreateFileUser)(DWORD system, DWORD flags, const BASS_FILEPROCS *procs, void *user);
BASS_OPUS_StreamCreateFileUser = func_type(HSTREAM, ctypes.c_ulong, ctypes.c_ulong, ctypes.POINTER(BASS_FILEPROCS), ctypes.c_void_p)(('BASS_OPUS_StreamCreateFileUser', bassopus_module))
if __name__ == "__main__":
if not pybass.BASS_Init(-1, 44100, 0, 0, 0):
print 'BASS_Init error', pybass.get_error_description(pybass.BASS_ErrorGetCode())
else:
handle = BASS_OPUS_StreamCreateFile(False, 'test.opus', 0, 0, 0)
pybass.play_handle(handle)
if not pybass.BASS_Free():
print 'BASS_Free error', pybass.get_error_description(pybass.BASS_ErrorGetCode())

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -5,14 +5,14 @@ from .channel import Channel
class Music(Channel):
def __init__(self, mem=False, file=None, offset=0, length=0, flags=0, freq=0):
handle = BASS_MusicLoad(mem, file, offset, length, flags, freq)
handle = pybass.BASS_MusicLoad(mem, file, offset, length, flags, freq)
super(Music, self).__init__(handle)
self.add_attributes_to_mapping(
music_amplify=pybass.BASS_ATTRIB_MUSIC_AMPLIFY,
music_bpm = BASS_ATTRIB_MUSIC_BPM,
music_pansep=BASS_ATTRIB_MUSIC_PANSEP,
music_speed=BASS_ATTRIB_MUSIC_SPEED,
music_vol_chan=BASS_ATTRIB_MUSIC_VOL_CHAN,
music_vol_global=BASS_ATTRIB_MUSIC_VOL_GLOBAL,
music_vol_inst=BASS_ATTRIB_MUSIC_VOL_INST,
music_bpm = pybass.BASS_ATTRIB_MUSIC_BPM,
music_pansep=pybass.BASS_ATTRIB_MUSIC_PANSEP,
music_speed=pybass.BASS_ATTRIB_MUSIC_SPEED,
music_vol_chan=pybass.BASS_ATTRIB_MUSIC_VOL_CHAN,
music_vol_global=pybass.BASS_ATTRIB_MUSIC_VOL_GLOBAL,
music_vol_inst=pybass.BASS_ATTRIB_MUSIC_VOL_INST,
)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
All the sounds used in the soundpack do not belong to us. They belong to the origenal creaters.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More