global s/frontends/applications/

This commit is contained in:
Christophe Benz 2010-07-06 12:59:06 +02:00
commit 3bf9c2518b
88 changed files with 4387 additions and 28 deletions

View file

@ -18,7 +18,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.frontends.boobank import Boobank
from weboob.applications.boobank import Boobank
if __name__ == '__main__':

View file

@ -18,7 +18,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.frontends.chatoob import Chatoob
from weboob.applications.chatoob import Chatoob
if __name__ == '__main__':

View file

@ -18,7 +18,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.frontends.havesex import HaveSex
from weboob.applications.havesex import HaveSex
if __name__ == '__main__':

View file

@ -18,7 +18,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.frontends.masstransit import Masstransit
from weboob.applications.masstransit import Masstransit
if __name__ == '__main__':

View file

@ -18,7 +18,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.frontends.monboob import Monboob
from weboob.applications.monboob import Monboob
if __name__ == '__main__':

View file

@ -18,7 +18,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.frontends.qboobmsg import QBoobMsg
from weboob.applications.qboobmsg import QBoobMsg
if __name__ == '__main__':

View file

@ -18,7 +18,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.frontends.qhavesex import QHaveSex
from weboob.applications.qhavesex import QHaveSex
if __name__ == '__main__':

View file

@ -18,7 +18,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.frontends.qvideoob import QVideoob
from weboob.applications.qvideoob import QVideoob
if __name__ == '__main__':

View file

@ -18,7 +18,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.frontends.travel import Travel
from weboob.applications.travel import Travel
if __name__ == '__main__':

View file

@ -18,7 +18,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.frontends.videoob import Videoob
from weboob.applications.videoob import Videoob
if __name__ == '__main__':

View file

@ -18,7 +18,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.frontends.videoob_web import VideoobWeb
from weboob.applications.videoob_web import VideoobWeb
if __name__ == '__main__':

View file

@ -18,7 +18,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.frontends.weboobdebug import WeboobDebug
from weboob.applications.weboobdebug import WeboobDebug
if __name__ == '__main__':

View file

@ -18,7 +18,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.frontends.weboobtests import WeboobTests
from weboob.applications.weboobtests import WeboobTests
if __name__ == '__main__':

View file

@ -18,7 +18,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.frontends.weboobcfg import WeboobCfg
from weboob.applications.weboobcfg import WeboobCfg
if __name__ == '__main__':

View file

@ -18,7 +18,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.frontends.weboorrents import Weboorrents
from weboob.applications.weboorrents import Weboorrents
if __name__ == '__main__':

View file

@ -18,7 +18,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.frontends.wetboobs import WetBoobs
from weboob.applications.wetboobs import WetBoobs
if __name__ == '__main__':

View file

@ -5,17 +5,17 @@ COMMAND="$1"
SETUP_PY_LIST="
weboob/setup.py
weboob/backends/setup.py
weboob/frontends/boobank/setup.py
weboob/frontends/masstransit/setup.py
weboob/frontends/monboob/setup.py
weboob/frontends/qboobmsg/setup.py
weboob/frontends/qhavesex/setup.py
weboob/frontends/qvideoob/setup.py
weboob/frontends/travel/setup.py
weboob/frontends/videoob/setup.py
weboob/frontends/videoob_web/setup.py
weboob/frontends/weboorrents/setup.py
weboob/frontends/wetboobs/setup.py
weboob/applications/boobank/setup.py
weboob/applications/masstransit/setup.py
weboob/applications/monboob/setup.py
weboob/applications/qboobmsg/setup.py
weboob/applications/qhavesex/setup.py
weboob/applications/qvideoob/setup.py
weboob/applications/travel/setup.py
weboob/applications/videoob/setup.py
weboob/applications/videoob_web/setup.py
weboob/applications/weboorrents/setup.py
weboob/applications/wetboobs/setup.py
"

View file

@ -0,0 +1 @@
__import__('pkg_resources').declare_namespace(__name__)

View file

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .boobank import Boobank

View file

@ -0,0 +1,70 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: ft=python et softtabstop=4 cinoptions=4 shiftwidth=4 ts=4 ai
# Copyright(C) 2009-2010 Romain Bignon, Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from __future__ import with_statement
import logging
import weboob
from weboob.capabilities.bank import ICapBank, AccountNotFound
from weboob.tools.application import ConsoleApplication
__all__ = ['Boobank']
class Boobank(ConsoleApplication):
APPNAME = 'boobank'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv):
self.load_backends(ICapBank)
return self.process_command(*argv[1:])
@ConsoleApplication.command('List every available accounts')
def command_list(self):
try:
for backend, account in self.weboob.do('iter_accounts'):
self.format(account, backend.name)
except weboob.CallErrors, errors:
for backend, error, backtrace in errors:
if isinstance(error, weboob.tools.browser.BrowserIncorrectPassword):
logging.error(u'Error: Incorrect password for backend %s' % backend.name)
else:
logging.error(u'Error[%s]: %s\n%s' % (backend.name, error, backtrace))
@ConsoleApplication.command('Display all future operations')
def command_coming(self, id):
total = 0.0
def do(backend):
account = backend.get_account(id)
return backend.iter_operations(account)
try:
for backend, operation in self.weboob.do(do):
self.format(operation, backend.name)
total += operation.amount
except weboob.CallErrors, errors:
for backend, error, backtrace in errors:
if isinstance(error, AccountNotFound):
logging.error(u'Error: account %s not found' % id)
else:
logging.error(u'Error[%s]: %s\n%s' % (backend.name, error, backtrace))

View file

@ -0,0 +1,46 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from setuptools import setup
import os
setup(
name='weboob-boobank',
version='0.1',
description='Boobank, the Weboob bank-accounts swiss-knife',
long_description='List your bank accounts and get info about them',
author='Christophe Benz, Romain Bignon',
author_email='weboob@lists.symlink.me',
license='GPLv3',
url='http://weboob.org/Boobank',
namespace_packages = ['weboob', 'weboob.frontends'],
packages=[
'weboob',
'weboob.frontends',
'weboob.frontends.boobank',
],
scripts=[
'scripts/boobank',
],
install_requires=[
'weboob-bank-backends',
],
)

View file

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .chatoob import Chatoob

View file

@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import logging
from weboob.tools.application import ConsoleApplication
from weboob.capabilities.chat import ICapChat
from weboob.capabilities.contact import ICapContact, Contact
__all__ = ['Chatoob']
class Chatoob(ConsoleApplication):
APPNAME = 'chatoob'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Christophe Benz'
def main(self, argv):
self.load_backends(ICapChat)
#for backend, result in self.weboob.do('start_chat_polling', self.on_new_chat_message):
#logging.info(u'Polling chat messages for backend %s' % backend)
return self.process_command(*argv[1:])
def on_new_chat_message(self, message):
print 'on_new_chat_message: %s' % message
@ConsoleApplication.command('exit program')
def command_exit(self):
self.weboob.want_stop()
@ConsoleApplication.command('list online contacts')
def command_list(self):
for backend, contact in self.weboob.do_caps(ICapContact, 'iter_contacts', status=Contact.STATUS_ONLINE):
self.format(contact, backend.name)
@ConsoleApplication.command('get messages')
def command_messages(self):
for backend, message in self.weboob.do('iter_chat_messages'):
self.format(message, backend.name)
@ConsoleApplication.command('send message to contact')
def command_send(self, _id, message):
for backend, result in self.weboob.do('send_chat_message', _id, message):
if not result:
logging.error(u'Failed to send message to contact id="%s" on backend "%s"' % (_id, backend.name))

View file

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .havesex import HaveSex

View file

@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from __future__ import with_statement
import sys
import weboob
from weboob.tools.application import PromptApplication
from weboob.capabilities.dating import ICapDating, OptimizationNotFound
__all__ = ['HaveSex']
class HaveSex(PromptApplication):
APPNAME = 'havesex'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
STORAGE_FILENAME = 'dating.storage'
CONFIG = {'optimizations': ''}
def main(self, argv):
self.load_config()
self.load_backends(ICapDating, storage=self.create_storage(self.STORAGE_FILENAME))
self.weboob.do('init_optimizations').wait()
self.optims('Starting', 'start_optimization', self.config.get('optimizations').split(' '))
return self.loop()
@PromptApplication.command("exit program")
def command_exit(self):
print 'Returning in real-life...'
self.weboob.want_stop()
@PromptApplication.command("show a profile")
def command_profile(self, id):
_id, backend_name = self.parse_id(id)
found = 0
for backend, profile in self.weboob.do_backends(backend_name, 'get_profile', _id):
if profile:
print profile.get_profile_text()
found = 1
if not found:
print >>sys.stderr, 'Profile not found'
return True
def service(self, action, function, *params):
sys.stdout.write('%s:' % action)
for backend, result in self.weboob.do(function, *params):
if result:
sys.stdout.write(' ' + backend.name)
sys.stdout.flush()
sys.stdout.write('.\n')
def optims(self, action, function, optims):
for optim in optims:
try:
self.service('Starting %s' % optim, 'start_optimization', optim)
except weboob.CallErrors, errors:
for backend, error, backtrace in errors:
if isinstance(error, OptimizationNotFound):
print 'Optimization "%s" not found' % optim
@PromptApplication.command("start optimizations")
def command_start(self, *optims):
self.optims('Starting', 'start_optimization', optims)
@PromptApplication.command("stop optimizations")
def command_stop(self, *optims):
self.optims('Stopping', 'stop_optimization', optims)

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# vim: ft=python et softtabstop=4 cinoptions=4 shiftwidth=4 ts=4 ai
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .masstransit import Masstransit

View file

@ -0,0 +1,239 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Julien Hébert
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.capabilities.travel import ICapTravel
from weboob.tools.application import BaseApplication
import conic
import gtk
import hildon
from logging import debug
__all__ = ['Masstransit']
class MasstransitHildon():
"hildon interface"
def connect_event(self, connection, event=None, c=None, d=None):
debug("DBUS-DEBUG a: %s, b:%s, c:%s,d: %s" % (connection, event, c, d))
status = event.get_status()
if status == conic.STATUS_CONNECTED:
self.connected = True
if self.touch_selector_entry_filled == False:
debug("connected, now fill")
self.fill_touch_selector_entry()
if self.refresh_in_progress:
self.refresh()
elif status == conic.STATUS_DISCONNECTED:
self.connected = False
def __init__(self, weboob):
self.touch_selector_entry_filled = False
self.refresh_in_progress = False
self.connected = False
self.weboob = weboob
self.connection = conic.Connection()
self.connection.connect("connection-event", self.connect_event)
self.connection.set_property("automatic-connection-events", True)
self.connection.request_connection(conic.CONNECT_FLAG_NONE)
main_window = hildon.Window()
main_window.set_title("Horaires des Prochains Trains")
main_window.connect("destroy", self.on_main_window_destroy)
self.refresh_button = hildon.Button(
gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
hildon.BUTTON_ARRANGEMENT_HORIZONTAL,
"Actualiser"
)
self.refresh_button.set_sensitive(False)
self.refresh_button.connect("clicked", self.on_refresh_button_clicked)
self.retour_button = hildon.Button(
gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
hildon.BUTTON_ARRANGEMENT_HORIZONTAL,
"Retour"
)
self.retour_button.set_sensitive(False)
self.retour_button.connect("clicked", self.on_retour_button_clicked)
self.treestore = gtk.TreeStore(str, str, str, str, str)
treeview = gtk.TreeView(self.treestore)
treeview.append_column(
gtk.TreeViewColumn(
'Train',
gtk.CellRendererText(),
text=0
))
treeview.append_column(
gtk.TreeViewColumn(
'Horaire',
gtk.CellRendererText(),
text=1
))
treeview.append_column(
gtk.TreeViewColumn(
'Destination',
gtk.CellRendererText(),
text=2
))
treeview.append_column(
gtk.TreeViewColumn(
'Voie',
gtk.CellRendererText(),
text=3
))
treeview.append_column(
gtk.TreeViewColumn(
'Information',
gtk.CellRendererText(),
text=4
))
self.combo_source = hildon.TouchSelectorEntry(text=True)
self.combo_dest = hildon.TouchSelectorEntry(text=True)
self.picker_button_source = hildon.PickerButton(
gtk.HILDON_SIZE_AUTO,
hildon.BUTTON_ARRANGEMENT_VERTICAL)
self.picker_button_dest = hildon.PickerButton(
gtk.HILDON_SIZE_AUTO,
hildon.BUTTON_ARRANGEMENT_VERTICAL
)
self.picker_button_source.set_sensitive(False)
self.picker_button_dest.set_sensitive(False)
self.picker_button_source.set_title("Gare de Depart")
self.picker_button_dest.set_title("Gare d'arrivee")
self.picker_button_source.set_selector(self.combo_source)
self.picker_button_dest.set_selector(self.combo_dest)
vertical_box = gtk.VBox()
horizontal_box = gtk.HBox()
vertical_box.pack_start(horizontal_box)
horizontal_box.pack_start(self.picker_button_source)
horizontal_box.pack_start(self.picker_button_dest)
horizontal_box.pack_start(self.retour_button)
vertical_box.pack_start(treeview)
vertical_box.pack_start(self.refresh_button)
main_window.add(vertical_box)
main_window.show_all()
self.picker_button_source.connect("value-changed",
self.check_station_input,
self.picker_button_source)
self.picker_button_dest.connect("value-changed",
self.check_station_input,
self.picker_button_dest)
def fill_touch_selector_entry(self):
liste = []
for backend in self.weboob.iter_backends():
for station in backend.iter_station_search(""):
liste.append(station.name.capitalize())
liste.sort()
for station in liste:
self.combo_source.append_text(station)
self.combo_dest.append_text(station)
self.touch_selector_entry_filled = True
self.picker_button_source.set_sensitive(True)
def on_main_window_destroy(self, widget):
"exit application at the window close"
gtk.main_quit()
def on_main_window_show(self, param):
self.fill_touch_selector_entry()
def on_retour_button_clicked(self, widget):
"the button is clicked"
self.refresh_in_progress = True
col_source = self.combo_source.get_active(0)
col_dest = self.combo_dest.get_active(0)
self.combo_source.set_active(0, col_dest)
self.combo_dest.set_active(0, col_source)
self.refresh()
def on_refresh_button_clicked(self, widget):
"the refresh button is clicked"
self.refresh_in_progress = True
self.connection.request_connection(conic.CONNECT_FLAG_NONE)
def check_station_input(self, widget, user_data):
if self.combo_source.get_current_text() is None :
self.picker_button_dest.set_sensitive(False)
self.refresh_button.set_sensitive(False)
self.retour_button.set_sensitive(False)
else:
self.picker_button_dest.set_sensitive(True)
if self.combo_dest.get_current_text() is None:
self.refresh_button.set_sensitive(False)
self.retour_button.set_sensitive(False)
else:
self.refresh_button.set_sensitive(True)
self.retour_button.set_sensitive(True)
def refresh(self):
"update departures"
self.treestore.clear()
for backend in self.weboob.iter_backends():
for station in \
backend.iter_station_search(self.combo_source.get_current_text()):
for arrival in \
backend.iter_station_search(self.combo_dest.get_current_text()):
for departure in \
backend.iter_station_departures(station.id, arrival.id):
self.treestore.append(None,
[departure.type,
departure.time,
departure.arrival_station,
departure.plateform,
departure.information])
self.refresh_in_progress = False
class Masstransit(BaseApplication):
"Application Class"
APPNAME = 'masstransit'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Julien Hébert'
def main(self, argv):
"main fonction"
self.load_modules(ICapTravel)
MasstransitHildon(self.weboob)
gtk.main()

View file

@ -0,0 +1,46 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from setuptools import setup
import os
setup(
name='weboob-masstransit',
version='0.1',
description='Masstransit, the Weboob travel swiss-knife, Hildon version (Maemo OS)',
long_description='Search for train stations and departure timegrids',
author='Julien Hébert',
author_email='weboob@lists.symlink.me',
license='GPLv3',
url='http://weboob.org/Masstransit',
namespace_packages = ['weboob', 'weboob.frontends'],
packages=[
'weboob',
'weboob.frontends',
'weboob.frontends.masstransit',
],
scripts=[
'scripts/masstransit',
],
install_requires=[
'weboob-travel-backends',
],
)

View file

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# vim: ft=python et softtabstop=4 cinoptions=4 shiftwidth=4 ts=4 ai
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .monboob import Monboob

View file

@ -0,0 +1,265 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2009-2010 Romain Bignon, Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from email.mime.text import MIMEText
from smtplib import SMTP
from email.Header import Header, decode_header
from email.Utils import parseaddr, formataddr, formatdate
from email import message_from_file, message_from_string
from smtpd import SMTPServer
import time
import re
import sys
import logging
import asyncore
from weboob.core.engine import Weboob
from weboob.core.scheduler import Scheduler
from weboob.capabilities.messages import ICapMessages, ICapMessagesReply, Message
from weboob.tools.application import ConsoleApplication
from weboob.tools.misc import html2text, get_backtrace, utc2local
__all__ = ['Monboob']
class FakeSMTPD(SMTPServer):
def __init__(self, app, bindaddr, port):
SMTPServer.__init__(self, (bindaddr, port), None)
self.app = app
def process_message(self, peer, mailfrom, rcpttos, data):
msg = message_from_string(data)
self.app.process_incoming_mail(msg)
class MonboobScheduler(Scheduler):
def __init__(self, app):
Scheduler.__init__(self)
self.app = app
def run(self):
if self.app.options.smtpd:
if ':' in self.app.options.smtpd:
host, port = self.app.options.smtpd.split(':', 1)
else:
host = '127.0.0.1'
port = self.app.options.smtpd
FakeSMTPD(self.app, host, int(port))
# XXX Fuck, we shouldn't copy this piece of code from
# weboob.scheduler.Scheduler.run().
try:
while 1:
self.stop_event.wait(0.1)
if self.app.options.smtpd:
asyncore.loop(timeout=0.1, count=1)
except KeyboardInterrupt:
self._wait_to_stop()
raise
else:
self._wait_to_stop()
return True
class Monboob(ConsoleApplication):
APPNAME = 'monboob'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
CONFIG = {'interval': 15,
'domain': 'weboob.example.org',
'recipient': 'weboob@example.org',
'smtp': 'localhost',
'html': 0}
def add_application_options(self, group):
group.add_option('-S', '--smtpd', help='run a fake smtpd server and set the port')
def create_weboob(self):
return Weboob(scheduler=MonboobScheduler(self))
def main(self, argv):
self.load_config()
self.load_backends(ICapMessages, storage=self.create_storage())
return self.process_command(*argv[1:])
def get_email_address_ident(self, msg, header):
s = msg.get(header)
m = re.match('.*<(.*)@(.*)>', s)
if m:
return m.group(1)
else:
try:
return s.split('@')[0]
except IndexError:
return s
@ConsoleApplication.command("pipe with a mail to post message")
def command_post(self):
msg = message_from_file(sys.stdin)
return self.process_incoming_mail(msg)
def process_incoming_mail(self, msg):
to = self.get_email_address_ident(msg, 'To')
reply_to = self.get_email_address_ident(msg, 'In-Reply-To')
if not reply_to:
print >>sys.stderr, 'This is not a reply (no Reply-To field)'
return 1
title = msg.get('Subject')
if title:
new_title = u''
for part in decode_header(title):
if part[1]:
new_title += unicode(part[0], part[1])
else:
new_title += unicode(part[0])
title = new_title
content = u''
for part in msg.walk():
if part.get_content_type() == 'text/plain':
s = part.get_payload(decode=True)
charsets = part.get_charsets() + msg.get_charsets()
for charset in charsets:
try:
content += unicode(s, charset)
except:
continue
else:
break
# remove signature
content = content.split(u'\n-- \n')[0]
bname, id = reply_to.split('.', 1)
# Default use the To header field to know the backend to use.
if to and bname != to:
bname = to
try:
backend = self.weboob.backends[bname]
except KeyError:
print >>sys.stderr, 'Backend %s not found' % bname
return 1
if not backend.has_caps(ICapMessagesReply):
print >>sys.stderr, 'The backend %s does not implement ICapMessagesReply' % bname
return 1
thread_id, msg_id = id.rsplit('.', 1)
try:
backend.post_reply(thread_id, msg_id, title, content)
except Exception, e:
content = u'Unable to send message to %s:\n' % thread_id
content += '\n\t%s\n' % e
if logging.root.level == logging.DEBUG:
content += '\n%s\n' % get_backtrace(e)
self.send_email(backend, Message(thread_id,
0,
title='Unable to send message',
sender='Monboob',
reply_id=msg_id,
content=content))
@ConsoleApplication.command("run daemon")
def command_run(self):
self.weboob.repeat(int(self.config.get('interval')), self.process)
self.weboob.loop()
def process(self):
for backend, message in self.weboob.do('iter_new_messages'):
self.send_email(backend, message)
def send_email(self, backend, mail):
domain = self.config.get('domain')
recipient = self.config.get('recipient')
reply_id = ''
if mail.get_reply_id():
reply_id = u'<%s.%s@%s>' % (backend.name, mail.get_full_reply_id(), domain)
subject = mail.get_title()
sender = u'"%s" <%s@%s>' % (mail.get_from().replace('"', '""'), backend.name, domain)
# assume that get_date() returns an UTC datetime
date = formatdate(time.mktime(utc2local(mail.get_date()).timetuple()), localtime=True)
msg_id = u'<%s.%s@%s>' % (backend.name, mail.get_full_id(), domain)
if int(self.config.get('html')) and mail.is_html:
body = mail.get_content()
content_type = 'html'
else:
if mail.is_html:
body = html2text(mail.get_content())
else:
body = mail.get_content()
content_type = 'plain'
if mail.get_signature():
if int(self.config.get('html')) and mail.is_html:
body += u'<p>-- <br />%s</p>' % mail.get_signature()
else:
body += u'\n\n-- \n'
if mail.is_html:
body += html2text(mail.get_signature())
else:
body += mail.get_signature()
# Header class is smart enough to try US-ASCII, then the charset we
# provide, then fall back to UTF-8.
header_charset = 'ISO-8859-1'
# We must choose the body charset manually
for body_charset in 'US-ASCII', 'ISO-8859-1', 'UTF-8':
try:
body.encode(body_charset)
except UnicodeError:
pass
else:
break
# Split real name (which is optional) and email address parts
sender_name, sender_addr = parseaddr(sender)
recipient_name, recipient_addr = parseaddr(recipient)
# We must always pass Unicode strings to Header, otherwise it will
# use RFC 2047 encoding even on plain ASCII strings.
sender_name = str(Header(unicode(sender_name), header_charset))
recipient_name = str(Header(unicode(recipient_name), header_charset))
# Make sure email addresses do not contain non-ASCII characters
sender_addr = sender_addr.encode('ascii')
recipient_addr = recipient_addr.encode('ascii')
# Create the message ('plain' stands for Content-Type: text/plain)
msg = MIMEText(body.encode(body_charset), content_type, body_charset)
msg['From'] = formataddr((sender_name, sender_addr))
msg['To'] = formataddr((recipient_name, recipient_addr))
msg['Subject'] = Header(unicode(subject), header_charset)
msg['Message-Id'] = msg_id
msg['Date'] = date
if reply_id:
msg['In-Reply-To'] = reply_id
# Send the message via SMTP to localhost:25
smtp = SMTP(self.config.get('smtp'))
print 'Send mail from <%s> to <%s>' % (sender, recipient)
smtp.sendmail(sender, recipient, msg.as_string())
smtp.quit()
return msg['Message-Id']

View file

@ -0,0 +1,45 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from setuptools import setup
import os
setup(
name='weboob-monboob',
version='0.1',
description='Monboob, the Weboob e-mail swiss-knife',
author='Romain Bignon',
author_email='weboob@lists.symlink.me',
license='GPLv3',
url='http://weboob.org/Monboob',
namespace_packages = ['weboob', 'weboob.frontends'],
packages=[
'weboob',
'weboob.frontends',
'weboob.frontends.monboob',
],
scripts=[
'scripts/monboob',
],
install_requires=[
'weboob-messages-backends',
],
)

View file

@ -0,0 +1 @@
from .qboobmsg import QBoobMsg

View file

@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from PyQt4.QtCore import SIGNAL
from weboob.tools.application.qt import QtMainWindow
from weboob.tools.application.qt.backendcfg import BackendCfg
from weboob.capabilities.messages import ICapMessages
from .ui.main_window_ui import Ui_MainWindow
from .messages_manager import MessagesManager
class MainWindow(QtMainWindow):
def __init__(self, config, weboob, parent=None):
QtMainWindow.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.config = config
self.weboob = weboob
self.manager = MessagesManager(weboob, self)
self.setCentralWidget(self.manager)
self.connect(self.ui.actionModules, SIGNAL("triggered()"), self.modulesConfig)
self.connect(self.ui.actionRefresh, SIGNAL("triggered()"), self.refresh)
def modulesConfig(self):
bckndcfg = BackendCfg(self.weboob, (ICapMessages,), self)
bckndcfg.show()
def refresh(self):
self.centralWidget().refresh()

View file

@ -0,0 +1,134 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import time
from PyQt4.QtGui import QWidget, QTreeWidgetItem, QListWidgetItem
from PyQt4.QtCore import SIGNAL, Qt
from weboob.capabilities.messages import ICapMessages
from weboob.tools.application.qt import QtDo
from .ui.messages_manager_ui import Ui_MessagesManager
class MessagesManager(QWidget):
def __init__(self, weboob, parent=None):
QWidget.__init__(self, parent)
self.ui = Ui_MessagesManager()
self.ui.setupUi(self)
self.weboob = weboob
self.ui.backendsList.addItem('(All)')
for backend in self.weboob.iter_backends():
if not backend.has_caps(ICapMessages):
continue
item = QListWidgetItem(backend.name.capitalize())
item.setData(Qt.UserRole, backend)
self.ui.backendsList.addItem(item)
self.ui.backendsList.setCurrentRow(0)
self.backend = None
self.connect(self.ui.backendsList, SIGNAL('itemSelectionChanged()'), self._backendChanged)
self.connect(self.ui.messagesTree, SIGNAL('itemClicked(QTreeWidgetItem *, int)'), self._messageSelected)
self.connect(self.ui.messagesTree, SIGNAL('itemActivated(QTreeWidgetItem *, int)'), self._messageSelected)
self.connect(self, SIGNAL('gotMessage'), self._gotMessage)
def load(self):
self.refresh()
def _backendChanged(self):
selection = self.ui.backendsList.selectedItems()
if not selection:
self.backend = None
return
self.backend = selection[0].data(Qt.UserRole).toPyObject()
self.ui.messagesTree.clear()
self.refresh()
def refresh(self):
if self.ui.messagesTree.topLevelItemCount() > 0:
command = 'iter_new_messages'
else:
command = 'iter_messages'
self.ui.backendsList.setEnabled(False)
self.process = QtDo(self.weboob, self._gotMessage)
if self.backend:
self.process.do_backends(self.backend.name, command)
else:
self.process.do_caps(ICapMessages, command)
def _gotMessage(self, backend, message):
if message is None:
self.ui.backendsList.setEnabled(True)
return
item = QTreeWidgetItem(None, [time.strftime('%Y-%m-%d %H:%M:%S', message.get_date().timetuple()),
message.sender, message.title])
item.setData(0, Qt.UserRole, message)
root = self.ui.messagesTree.invisibleRootItem()
# try to find a message which would be my parent.
# if no one is found, insert it on top level.
if not self._insertMessage(root, item):
self.ui.messagesTree.addTopLevelItem(item)
# Check orphaned items which are child of this new one to put
# in.
to_remove = []
for i in xrange(root.childCount()):
sub = root.child(i)
sub_message = sub.data(0, Qt.UserRole).toPyObject()
if sub_message.thread_id == message.thread_id and sub_message.reply_id == message.id:
# do not remove it now because childCount() would change.
to_remove.append(sub)
for sub in to_remove:
root.removeChild(sub)
item.addChild(sub)
def _insertMessage(self, top, item):
top_message = top.data(0, Qt.UserRole).toPyObject()
item_message = item.data(0, Qt.UserRole).toPyObject()
if top_message and top_message.thread_id == item_message.thread_id and top_message.id == item_message.reply_id:
# it's my parent
top.addChild(item)
return True
else:
# check the children
for i in xrange(top.childCount()):
sub = top.child(i)
if self._insertMessage(sub, item):
return True
return False
def _messageSelected(self, item, column):
message = item.data(0, Qt.UserRole).toPyObject()
self.ui.messageBody.setText("<h1>%s</h1>"
"<b>Date</b>: %s<br />"
"<b>From</b>: %s<br />"
"<p>%s</p>"
% (message.title, str(message.date), message.sender, message.content))

View file

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.capabilities.messages import ICapMessages
from weboob.tools.application import QtApplication
from .main_window import MainWindow
class QBoobMsg(QtApplication):
APPNAME = 'qboobmsg'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv):
self.load_backends(ICapMessages, storage=self.create_storage())
self.main_window = MainWindow(self.config, self.weboob)
self.main_window.show()
return self.weboob.loop()

View file

@ -0,0 +1,49 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from setuptools import setup
import os
os.system('make -C %s/ui' % os.path.dirname(__file__))
setup(
name='weboob-qboobmsg',
version='0.1',
description='QBoobMsg, the Weboob e-mail swiss-knife, Qt version',
author='Romain Bignon',
author_email='weboob@lists.symlink.me',
license='GPLv3',
url='http://weboob.org/QBoobMsg',
namespace_packages = ['weboob', 'weboob.frontends'],
packages=[
'weboob',
'weboob.frontends',
'weboob.frontends.qboobmsg',
'weboob.frontends.qboobmsg.ui',
],
scripts=[
'scripts/qboobmsg',
],
install_requires=[
'weboob-core-qt',
'weboob-messages-backends',
],
)

View file

@ -0,0 +1,13 @@
UI_FILES = $(wildcard *.ui)
UI_PY_FILES = $(UI_FILES:%.ui=%_ui.py)
PYUIC = pyuic4
all: $(UI_PY_FILES)
%_ui.py: %.ui
$(PYUIC) -o $@ $^
clean:
rm -f *.pyc
rm -f $(UI_PY_FILES)

View file

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>763</width>
<height>580</height>
</rect>
</property>
<property name="windowTitle">
<string>QBoobMsg</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout"/>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>763</width>
<height>24</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionModules"/>
<addaction name="actionRefresh"/>
<addaction name="separator"/>
<addaction name="actionQuit"/>
</widget>
<addaction name="menuFile"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionModules"/>
<addaction name="actionRefresh"/>
</widget>
<action name="actionModules">
<property name="text">
<string>Modules</string>
</property>
</action>
<action name="actionQuit">
<property name="text">
<string>Quit</string>
</property>
<property name="toolTip">
<string>Quit</string>
</property>
</action>
<action name="actionRefresh">
<property name="text">
<string>Refresh</string>
</property>
</action>
</widget>
<resources/>
<connections>
<connection>
<sender>actionQuit</sender>
<signal>triggered()</signal>
<receiver>MainWindow</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>381</x>
<y>289</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -0,0 +1,192 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MessagesManager</class>
<widget class="QWidget" name="MessagesManager">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>767</width>
<height>591</height>
</rect>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSplitter" name="splitter_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QListWidget" name="backendsList">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
<widget class="QSplitter" name="splitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>2</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QTreeWidget" name="messagesTree">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="tabKeyNavigation">
<bool>true</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="animated">
<bool>true</bool>
</property>
<property name="headerHidden">
<bool>false</bool>
</property>
<attribute name="headerDefaultSectionSize">
<number>100</number>
</attribute>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>true</bool>
</attribute>
<attribute name="headerStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="headerDefaultSectionSize">
<number>100</number>
</attribute>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>true</bool>
</attribute>
<attribute name="headerStretchLastSection">
<bool>true</bool>
</attribute>
<column>
<property name="text">
<string>Date</string>
</property>
<property name="textAlignment">
<set>AlignLeft|AlignVCenter</set>
</property>
</column>
<column>
<property name="text">
<string>From</string>
</property>
</column>
<column>
<property name="text">
<string>Title</string>
</property>
</column>
</widget>
<widget class="QTextEdit" name="messageBody">
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</widget>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QToolButton" name="expandButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="collapseButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string></string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>expandButton</sender>
<signal>clicked()</signal>
<receiver>messagesTree</receiver>
<slot>expandAll()</slot>
<hints>
<hint type="sourcelabel">
<x>733</x>
<y>31</y>
</hint>
<hint type="destinationlabel">
<x>527</x>
<y>150</y>
</hint>
</hints>
</connection>
<connection>
<sender>collapseButton</sender>
<signal>clicked()</signal>
<receiver>messagesTree</receiver>
<slot>collapseAll()</slot>
<hints>
<hint type="sourcelabel">
<x>733</x>
<y>60</y>
</hint>
<hint type="destinationlabel">
<x>527</x>
<y>150</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -0,0 +1 @@
from .qhavesex import QHaveSex

View file

@ -0,0 +1,203 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import urllib2
import time
import logging
from PyQt4.QtGui import QWidget, QListWidgetItem, QImage, QIcon, QPixmap, QFrame, QMessageBox
from PyQt4.QtCore import SIGNAL, Qt
from weboob.tools.application.qt import QtDo, HTMLDelegate
from weboob.capabilities.contact import ICapContact, Contact
from weboob.capabilities.chat import ICapChat
from weboob.capabilities.messages import ICapMessages
from .ui.contacts_ui import Ui_Contacts
from .ui.contact_thread_ui import Ui_ContactThread
from .ui.thread_message_ui import Ui_ThreadMessage
class ThreadMessage(QFrame):
def __init__(self, message, parent=None):
QFrame.__init__(self, parent)
self.ui = Ui_ThreadMessage()
self.ui.setupUi(self)
self.date = message.get_date()
self.ui.nameLabel.setText(message.sender)
self.ui.headerLabel.setText(time.strftime('%Y-%m-%d %H:%M:%S', message.get_date().timetuple()))
if message.is_html:
content = message.content
else:
content = message.content.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('\n', '<br />')
self.ui.contentLabel.setText(content)
class ContactThread(QWidget):
def __init__(self, weboob, contact, parent=None):
QWidget.__init__(self, parent)
self.ui = Ui_ContactThread()
self.ui.setupUi(self)
self.weboob = weboob
self.contact = contact
self.messages = []
self.connect(self.ui.sendButton, SIGNAL('clicked()'), self.postReply)
self.refreshMessages()
def refreshMessages(self):
if self.ui.scrollAreaContent.layout().count() > 0:
command = 'iter_new_messages'
else:
command = 'iter_messages'
self.process = QtDo(self.weboob, self.gotMessage)
self.process.do_backends(self.contact.backend, command, thread=self.contact.id)
def gotMessage(self, backend, message):
if not message:
v = self.ui.scrollArea.verticalScrollBar()
print v.minimum(), v.value(), v.maximum(), v.sliderPosition()
self.ui.scrollArea.verticalScrollBar().setValue(self.ui.scrollArea.verticalScrollBar().maximum())
return
widget = ThreadMessage(message)
for i, m in enumerate(self.messages):
if widget.date < m.date:
self.ui.scrollAreaContent.layout().insertWidget(i, widget)
self.messages.insert(i, widget)
return
self.ui.scrollAreaContent.layout().addWidget(widget)
self.messages.append(widget)
def postReply(self):
text = unicode(self.ui.textEdit.toPlainText())
self.ui.textEdit.setEnabled(False)
self.ui.sendButton.setEnabled(False)
self.process = QtDo(self.weboob, self.replyPosted, self.replyNotPosted)
self.process.do_backends(self.contact.backend, 'post_reply', self.contact.id, 0, '', text)
def replyPosted(self, backend, ignored):
self.ui.textEdit.clear()
self.ui.textEdit.setEnabled(True)
self.ui.sendButton.setEnabled(True)
self.refreshMessages()
def replyNotPosted(self, backend, error, backtrace):
content = unicode(self.tr('Unable to send message:\n%s\n')) % error
if logging.root.level == logging.DEBUG:
content += '\n%s\n' % backtrace
QMessageBox.critical(self, self.tr('Error while posting reply'),
content, QMessageBox.Ok)
class IGroup(object):
def __init__(self, weboob, id, name):
self.id = id
self.name = name
self.weboob = weboob
def iter_contacts(self, cb):
raise NotImplementedError()
class MetaGroup(IGroup):
def iter_contacts(self, cb):
if self.id == 'online':
status = Contact.STATUS_ONLINE
elif self.id == 'offline':
status = Contact.STATUS_OFFLINE
else:
status = Contact.STATUS_ALL
self.process = QtDo(self.weboob, lambda b, d: self.cb(cb, b, d))
self.process.do_caps(ICapContact, 'iter_contacts', status)
def cb(self, cb, backend, contact):
if contact:
contact.backend = backend
cb(contact)
class ContactsWidget(QWidget):
def __init__(self, weboob, parent=None):
QWidget.__init__(self, parent)
self.ui = Ui_Contacts()
self.ui.setupUi(self)
self.weboob = weboob
self.contact = None
self.ui.contactList.setItemDelegate(HTMLDelegate())
self.ui.groupBox.addItem('All', MetaGroup(self.weboob, 'all', self.tr('All')))
self.ui.groupBox.addItem('Onlines', MetaGroup(self.weboob, 'online', self.tr('Online')))
self.ui.groupBox.addItem('Offlines', MetaGroup(self.weboob, 'offline', self.tr('Offline')))
self.connect(self.ui.groupBox, SIGNAL('currentIndexChanged(int)'), self.groupChanged)
self.connect(self.ui.contactList, SIGNAL('currentItemChanged(QListWidgetItem*, QListWidgetItem*)'), self.contactChanged)
self.connect(self.ui.refreshButton, SIGNAL('clicked()'), self.refreshContactList)
def load(self):
self.ui.groupBox.setCurrentIndex(1)
def groupChanged(self, i):
self.refreshContactList()
def refreshContactList(self):
self.ui.contactList.clear()
i = self.ui.groupBox.currentIndex()
group = self.ui.groupBox.itemData(i).toPyObject()
group.iter_contacts(self.addContact)
def addContact(self, contact):
if not contact:
return
data = urllib2.urlopen(contact.thumbnail_url).read()
img = QImage.fromData(data)
status = ''
if contact.status == Contact.STATUS_ONLINE:
status = 'Online'
status_color = 0x00aa00
elif contact.status == Contact.STATUS_OFFLINE:
status = 'Offline'
status_color = 0xff0000
item = QListWidgetItem()
item.setText('<h2>%s</h2><font color="#%06X">%s</font><br /><i>%s</i>' % (contact.name, status_color, status, contact.backend.name))
item.setIcon(QIcon(QPixmap.fromImage(img)))
item.setData(Qt.UserRole, contact)
self.ui.contactList.addItem(item)
def contactChanged(self, current, previous):
self.ui.tabWidget.clear()
self.contact = None
if not current:
return
self.contact = current.data(Qt.UserRole).toPyObject()
self.ui.tabWidget.addTab(QWidget(), self.tr('Profile'))
if self.contact.backend.has_caps(ICapMessages):
self.ui.tabWidget.addTab(ContactThread(self.weboob, self.contact), self.tr('Messages'))
if self.contact.backend.has_caps(ICapChat):
self.ui.tabWidget.addTab(QWidget(), self.tr('Chat'))
self.ui.tabWidget.addTab(QWidget(), self.tr('Calendar'))
self.ui.tabWidget.addTab(QWidget(), self.tr('Notes'))

View file

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from PyQt4.QtGui import QWidget
from PyQt4.QtCore import SIGNAL
from weboob.tools.application.qt import QtMainWindow
from weboob.tools.application.qt.backendcfg import BackendCfg
from weboob.capabilities.dating import ICapDating
from weboob.frontends.qboobmsg.messages_manager import MessagesManager
from .ui.main_window_ui import Ui_MainWindow
from .status import AccountsStatus
from .contacts import ContactsWidget
class MainWindow(QtMainWindow):
def __init__(self, config, weboob, parent=None):
QtMainWindow.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.config = config
self.weboob = weboob
self.loaded_tabs = {}
self.ui.tabWidget.addTab(AccountsStatus(self.weboob), self.tr('Status'))
self.ui.tabWidget.addTab(MessagesManager(self.weboob), self.tr('Messages'))
self.ui.tabWidget.addTab(ContactsWidget(self.weboob), self.tr('Contacts'))
self.ui.tabWidget.addTab(QWidget(), self.tr('Calendar'))
self.connect(self.ui.actionModules, SIGNAL("triggered()"), self.modulesConfig)
self.connect(self.ui.tabWidget, SIGNAL('currentChanged(int)'), self.tabChanged)
def modulesConfig(self):
bckndcfg = BackendCfg(self.weboob, (ICapDating,), self)
bckndcfg.show()
def tabChanged(self, i):
widget = self.ui.tabWidget.currentWidget()
if hasattr(widget, 'load') and not i in self.loaded_tabs:
widget.load()
self.loaded_tabs[i] = True

View file

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.capabilities.dating import ICapDating
from weboob.tools.application import QtApplication
from .main_window import MainWindow
class QHaveSex(QtApplication):
APPNAME = 'qhavesex'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
STORAGE_FILENAME = 'dating.storage'
def main(self, argv):
self.load_backends(ICapDating, storage=self.create_storage())
self.main_window = MainWindow(self.config, self.weboob)
self.main_window.show()
return self.weboob.loop()

View file

@ -0,0 +1,50 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from setuptools import setup
import os
os.system('make -C %s/ui' % os.path.dirname(__file__))
setup(
name='weboob-qhavesex',
version='0.1',
description='QHaveSex, the Weboob sexual life swiss-knife, Qt version',
long_description='Optimize your probabilities to have sex on dating websites',
author='Romain Bignon',
author_email='weboob@lists.symlink.me',
license='GPLv3',
url='http://weboob.org/QHaveSex',
namespace_packages = ['weboob', 'weboob.frontends'],
packages=[
'weboob',
'weboob.frontends',
'weboob.frontends.qhavesex',
'weboob.frontends.qhavesex.ui',
],
scripts=[
'scripts/qhavesex',
],
install_requires=[
'weboob-core-qt',
'weboob-dating-backends',
],
)

View file

@ -0,0 +1,102 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from __future__ import with_statement
from PyQt4.QtGui import QWidget, QHBoxLayout, QVBoxLayout, QFrame, QLabel, QImage, QPixmap
from PyQt4.QtCore import SIGNAL, QTimer
from weboob.capabilities.dating import StatusField
class Account(QFrame):
def __init__(self, backend, parent=None):
QFrame.__init__(self, parent)
self.setFrameShape(QFrame.StyledPanel)
self.setFrameShadow(QFrame.Raised)
self.backend = backend
self.setLayout(QVBoxLayout())
head = QHBoxLayout()
headw = QWidget()
headw.setLayout(head)
self.title = QLabel(u'<h1>%s%s</h1>' % (backend.name, backend.DESCRIPTION))
if backend.ICON:
self.icon = QLabel()
img = QImage(backend.ICON)
self.icon.setPixmap(QPixmap.fromImage(img))
head.addWidget(self.icon)
head.addWidget(self.title)
head.addStretch()
self.body = QLabel()
self.layout().addWidget(headw)
self.layout().addWidget(self.body)
self.timer = QTimer()
self.timer.setSingleShot(False)
self.timer.setInterval(60)
self.connect(self.timer, SIGNAL('timeout()'), self.updateStats)
self.updateStats()
def updateStats(self):
with self.backend:
body = u''
in_p = False
for field in self.backend.get_status():
if field.flags & StatusField.FIELD_HTML:
value = field.value.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
else:
value = '%s' % field.value
if field.flags & StatusField.FIELD_TEXT:
if in_p:
body += '</p>'
body += '<p>%s</p>' % value
in_p = False
else:
if not in_p:
body += "<p>"
in_p = True
else:
body += "<br />"
body += '<b>%s</b>: %s' % (field.label, field.value)
if in_p:
body += "</p>"
self.body.setText(body)
class AccountsStatus(QWidget):
def __init__(self, weboob, parent=None):
QWidget.__init__(self, parent)
self.weboob = weboob
self.setLayout(QVBoxLayout())
for backend in self.weboob.iter_backends():
account = Account(backend)
self.layout().addWidget(account)
self.layout().addStretch()

View file

@ -0,0 +1,13 @@
UI_FILES = $(wildcard *.ui)
UI_PY_FILES = $(UI_FILES:%.ui=%_ui.py)
PYUIC = pyuic4
all: $(UI_PY_FILES)
%_ui.py: %.ui
$(PYUIC) -o $@ $^
clean:
rm -f *.pyc
rm -f $(UI_PY_FILES)

View file

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ContactThread</class>
<widget class="QWidget" name="ContactThread">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>578</width>
<height>429</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
<widget class="QWidget" name="scrollAreaContent">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>556</width>
<height>187</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">QWidget#scrollAreaContent {
background-color: rgb(255, 255, 255);
}</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2"/>
</widget>
</widget>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QTextEdit" name="textEdit"/>
</item>
<item>
<widget class="QPushButton" name="sendButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Send</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Contacts</class>
<widget class="QWidget" name="Contacts">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>482</width>
<height>320</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="groupBox"/>
</item>
<item>
<widget class="QToolButton" name="refreshButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../../../../../../usr/share/icons/oxygen/16x16/actions/view-refresh.png</normaloff>../../../../../../../../usr/share/icons/oxygen/16x16/actions/view-refresh.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QListWidget" name="contactList">
<property name="iconSize">
<size>
<width>120</width>
<height>120</height>
</size>
</property>
<property name="spacing">
<number>1</number>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="selectionRectVisible">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>763</width>
<height>580</height>
</rect>
</property>
<property name="windowTitle">
<string>QHaveSex</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>763</width>
<height>24</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionModules"/>
<addaction name="separator"/>
<addaction name="actionQuit"/>
</widget>
<addaction name="menuFile"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionModules"/>
</widget>
<action name="actionModules">
<property name="text">
<string>Modules</string>
</property>
</action>
<action name="actionQuit">
<property name="text">
<string>Quit</string>
</property>
<property name="toolTip">
<string>Quit</string>
</property>
</action>
</widget>
<resources/>
<connections>
<connection>
<sender>actionQuit</sender>
<signal>triggered()</signal>
<receiver>MainWindow</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>381</x>
<y>289</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ThreadMessage</class>
<widget class="QFrame" name="ThreadMessage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>552</width>
<height>76</height>
</rect>
</property>
<property name="windowTitle">
<string>Frame</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="imageLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="nameLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="headerLabel">
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="contentLabel">
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1 @@
from .qvideoob import QVideoob

View file

@ -0,0 +1,116 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from PyQt4.QtCore import SIGNAL
from weboob.tools.application.qt import QtMainWindow, QtDo
from weboob.frontends.qvideoob.ui.main_window_ui import Ui_MainWindow
from .video import Video
from .minivideo import MiniVideo
class MainWindow(QtMainWindow):
def __init__(self, config, weboob, parent=None):
QtMainWindow.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.config = config
self.weboob = weboob
self.minivideos = []
self.ui.backendEdit.addItem('All backends', '')
for i, backend in enumerate(self.weboob.iter_backends()):
self.ui.backendEdit.addItem(backend.name, backend.name)
if backend.name == self.config.get('settings', 'backend'):
self.ui.backendEdit.setCurrentIndex(i+1)
self.ui.sortbyEdit.setCurrentIndex(int(self.config.get('settings', 'sortby')))
self.ui.nsfwCheckBox.setChecked(int(self.config.get('settings', 'nsfw')))
self.ui.sfwCheckBox.setChecked(int(self.config.get('settings', 'sfw')))
self.connect(self.ui.searchEdit, SIGNAL("returnPressed()"), self.search)
self.connect(self.ui.urlEdit, SIGNAL("returnPressed()"), self.openURL)
self.connect(self.ui.nsfwCheckBox, SIGNAL("stateChanged(int)"), self.nsfwChanged)
self.connect(self.ui.sfwCheckBox, SIGNAL("stateChanged(int)"), self.sfwChanged)
def nsfwChanged(self, state):
self.config.set('settings', 'nsfw', int(self.ui.nsfwCheckBox.isChecked()))
self.updateVideosDisplay()
def sfwChanged(self, state):
self.config.set('settings', 'sfw', int(self.ui.sfwCheckBox.isChecked()))
self.updateVideosDisplay()
def updateVideosDisplay(self):
for minivideo in self.minivideos:
if (minivideo.video.nsfw and self.ui.nsfwCheckBox.isChecked() or
not minivideo.video.nsfw and self.ui.sfwCheckBox.isChecked()):
minivideo.show()
else:
minivideo.hide()
def search(self):
pattern = unicode(self.ui.searchEdit.text())
if not pattern:
return
for minivideo in self.minivideos:
self.ui.scrollAreaContent.layout().removeWidget(minivideo)
minivideo.hide()
self.minivideos = []
self.ui.searchEdit.setEnabled(False)
backend_name = str(self.ui.backendEdit.itemData(self.ui.backendEdit.currentIndex()).toString())
self.process = QtDo(self.weboob, self.addVideo)
if backend_name:
self.process.do_backends(backend_name, 'iter_search_results', pattern, self.ui.sortbyEdit.currentIndex(), nsfw=True)
else:
self.process.do('iter_search_results', pattern, self.ui.sortbyEdit.currentIndex(), nsfw=True)
def addVideo(self, backend, video):
if not backend:
self.ui.searchEdit.setEnabled(True)
return
minivideo = MiniVideo(backend, video)
self.ui.scrollAreaContent.layout().addWidget(minivideo)
self.minivideos.append(minivideo)
if (video.nsfw and not self.ui.nsfwCheckBox.isChecked() or
not video.nsfw and not self.ui.sfwCheckBox.isChecked()):
minivideo.hide()
def openURL(self):
url = unicode(self.ui.urlEdit.text())
if not url:
return
for backend in self.weboob.iter_backends():
video = backend.get_video(url)
if video:
video_widget = Video(video, self)
video_widget.show()
self.ui.urlEdit.clear()
def closeEvent(self, ev):
self.config.set('settings', 'backend', str(self.ui.backendEdit.itemData(self.ui.backendEdit.currentIndex()).toString()))
self.config.set('settings', 'sortby', self.ui.sortbyEdit.currentIndex())
self.config.save()
ev.accept()

View file

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import urllib2
from PyQt4.QtGui import QFrame, QImage, QPixmap
from weboob.frontends.qvideoob.ui.minivideo_ui import Ui_MiniVideo
from .video import Video
class MiniVideo(QFrame):
def __init__(self, backend, video, parent=None):
QFrame.__init__(self, parent)
self.ui = Ui_MiniVideo()
self.ui.setupUi(self)
self.backend = backend
self.video = video
self.ui.titleLabel.setText(video.title)
self.ui.backendLabel.setText(backend.name)
self.ui.durationLabel.setText('%d:%02d:%02d' % (video.duration/3600, (video.duration%3600)/60, video.duration%60))
self.ui.authorLabel.setText(unicode(video.author))
self.ui.dateLabel.setText(video.date and unicode(video.date) or '')
if video.rating_max:
self.ui.ratingLabel.setText('%s / %s' % (video.rating, video.rating_max))
else:
self.ui.ratingLabel.setText('%s' % video.rating)
if video.thumbnail_url:
data = urllib2.urlopen(video.thumbnail_url).read()
img = QImage.fromData(data)
self.ui.imageLabel.setPixmap(QPixmap.fromImage(img))
def enterEvent(self, event):
self.setFrameShadow(self.Sunken)
QFrame.enterEvent(self, event)
def leaveEvent(self, event):
self.setFrameShadow(self.Raised)
QFrame.leaveEvent(self, event)
def mousePressEvent(self, event):
QFrame.mousePressEvent(self, event)
video = self.backend.get_video(self.video.id)
if video:
video_widget = Video(video, self)
video_widget.show()

View file

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.capabilities.video import ICapVideo
from weboob.tools.application import QtApplication
from .main_window import MainWindow
class QVideoob(QtApplication):
APPNAME = 'qvideoob'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
CONFIG = {'settings': {'nsfw': 1,
'sfw': 1,
'sortby': 0,
'backend': ''
}
}
def main(self, argv):
self.load_modules(ICapVideo)
self.load_config()
self.main_window = MainWindow(self.config, self.weboob)
self.main_window.show()
return self.weboob.loop()

View file

@ -0,0 +1,50 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from setuptools import setup
import os
os.system('make -C %s/ui' % os.path.dirname(__file__))
setup(
name='weboob-qvideoob',
version='0.1',
description='QVideoob, the Weboob video swiss-knife, Qt version',
long_description='Search for videos on many websites, and get info about them',
author='Romain Bignon',
author_email='weboob@lists.symlink.me',
license='GPLv3',
url='http://weboob.org/QVideoob',
namespace_packages = ['weboob', 'weboob.frontends'],
packages=[
'weboob',
'weboob.frontends',
'weboob.frontends.qvideoob',
'weboob.frontends.qvideoob.ui',
],
scripts=[
'scripts/qvideoob',
],
install_requires=[
'weboob-core-qt',
'weboob-video-backends',
],
)

View file

@ -0,0 +1,13 @@
UI_FILES = $(wildcard *.ui)
UI_PY_FILES = $(UI_FILES:%.ui=%_ui.py)
PYUIC = pyuic4
all: $(UI_PY_FILES)
%_ui.py: %.ui
$(PYUIC) -o $@ $^
clean:
rm -f *.pyc
rm -f $(UI_PY_FILES)

View file

@ -0,0 +1,193 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>785</width>
<height>594</height>
</rect>
</property>
<property name="windowTitle">
<string>QVideoob</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Search: </string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="searchEdit"/>
</item>
<item>
<widget class="QComboBox" name="sortbyEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Relevance</string>
</property>
</item>
<item>
<property name="text">
<string>Rating</string>
</property>
</item>
<item>
<property name="text">
<string>Duration</string>
</property>
</item>
<item>
<property name="text">
<string>Date</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QComboBox" name="backendEdit"/>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>10</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Display:</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="sfwCheckBox">
<property name="text">
<string>SFW</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="nsfwCheckBox">
<property name="text">
<string>NSFW</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaContent">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>763</width>
<height>391</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">QWidget#scrollAreaContent {
background-color: rgb(255, 255, 255);
}</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2"/>
</widget>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>URL: </string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="urlEdit"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>785</width>
<height>25</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MiniVideo</class>
<widget class="QFrame" name="MiniVideo">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>464</width>
<height>132</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="imageLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Title</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="titleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>50</weight>
<italic>true</italic>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_8">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Duration</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="durationLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Author</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="authorLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Date</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="dateLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Rating</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="ratingLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Where</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="backendLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,224 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Video</class>
<widget class="QDialog" name="Video">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>647</width>
<height>404</height>
</rect>
</property>
<property name="windowTitle">
<string>Video</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="titleLabel">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="Phonon::VideoPlayer" name="videoPlayer">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="Phonon::SeekSlider" name="seekSlider">
<property name="iconVisible">
<bool>true</bool>
</property>
<property name="tracking">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>URL</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="urlEdit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="cursor">
<cursorShape>ArrowCursor</cursorShape>
</property>
<property name="mouseTracking">
<bool>true</bool>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_8">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Duration</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="durationLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Author</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="authorLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Date</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="dateLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Rating</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="ratingLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Phonon::VideoPlayer</class>
<extends>QWidget</extends>
<header location="global">phonon/videoplayer.h</header>
</customwidget>
<customwidget>
<class>Phonon::SeekSlider</class>
<extends>QWidget</extends>
<header location="global">phonon/seekslider.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from PyQt4.QtCore import QUrl
from PyQt4.QtGui import QDialog
from PyQt4.phonon import Phonon
from weboob.frontends.qvideoob.ui.video_ui import Ui_Video
class Video(QDialog):
def __init__(self, video, parent=None):
QDialog.__init__(self, parent)
self.ui = Ui_Video()
self.ui.setupUi(self)
self.video = video
self.setWindowTitle("Video - %s" % video.title)
self.ui.urlEdit.setText(video.url)
self.ui.titleLabel.setText(video.title)
self.ui.durationLabel.setText('%d:%02d:%02d' % (video.duration/3600, (video.duration%3600)/60, video.duration%60))
self.ui.authorLabel.setText(unicode(video.author))
self.ui.dateLabel.setText(unicode(video.date))
if video.rating_max:
self.ui.ratingLabel.setText('%s / %s' % (video.rating, video.rating_max))
else:
self.ui.ratingLabel.setText('%s' % video.rating)
self.ui.seekSlider.setMediaObject(self.ui.videoPlayer.mediaObject())
self.ui.videoPlayer.load(Phonon.MediaSource(QUrl(video.url)))
self.ui.videoPlayer.play()
def closeEvent(self, event):
self.ui.videoPlayer.stop()
event.accept()

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# vim: ft=python et softtabstop=4 cinoptions=4 shiftwidth=4 ts=4 ai
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .application import Travel

View file

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon, Julien Hébert
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.capabilities.travel import ICapTravel
from weboob.tools.application import ConsoleApplication
__all__ = ['Travel']
class Travel(ConsoleApplication):
APPNAME = 'travel'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv):
self.load_modules(ICapTravel)
return self.process_command(*argv[1:])
@ConsoleApplication.command('Search stations')
def command_stations(self, pattern):
for backend, station in self.weboob.do('iter_station_search', pattern):
self.format(station, backend.name)
@ConsoleApplication.command('List all departures for a given station')
def command_departures(self, station, arrival=None):
for backend, departure in self.weboob.do('iter_station_departures', station, arrival):
self.format(departure, backend.name)

View file

@ -0,0 +1,46 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from setuptools import setup
import os
setup(
name='weboob-travel',
version='0.1',
description='The Weboob travel swiss-knife',
long_description='Search for train stations and departure timegrids',
author='Romain Bignon',
author_email='weboob@lists.symlink.me',
license='GPLv3',
url='http://weboob.org/Travel',
namespace_packages = ['weboob', 'weboob.frontends'],
packages=[
'weboob',
'weboob.frontends',
'weboob.frontends.travel',
],
scripts=[
'scripts/travel',
],
install_requires=[
'weboob-travel-backends',
],
)

View file

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .videoob import Videoob

View file

@ -0,0 +1,46 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from setuptools import setup
import os
setup(
name='weboob-videoob',
version='0.1',
description='Videoob, the Weboob video swiss-knife',
long_description='Search for videos on many websites, and get info about them',
author='Christophe Benz',
author_email='weboob@lists.symlink.me',
license='GPLv3',
url='http://weboob.org/Videoob',
namespace_packages = ['weboob', 'weboob.frontends'],
packages=[
'weboob',
'weboob.frontends',
'weboob.frontends.videoob',
],
scripts=[
'scripts/videoob',
],
install_requires=[
'weboob-video-backends',
],
)

View file

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz, Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.capabilities.video import ICapVideo
from weboob.tools.application import ConsoleApplication
__all__ = ['Videoob']
class Videoob(ConsoleApplication):
APPNAME = 'videoob'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Christophe Benz, Romain Bignon'
CONFIG = {}
def add_application_options(self, group):
group.add_option('--nsfw', action='store_true', help='enable non-suitable for work videos')
def main(self, argv):
return self.process_command(*argv[1:])
@ConsoleApplication.command('Get video information (accept ID or URL)')
def command_info(self, _id):
_id, backend_name = self.parse_id(_id)
names = (backend_name,) if backend_name is not None else None
self.load_modules(ICapVideo, names=names)
for backend, video in self.weboob.do('get_video', _id):
if video is None:
continue
self.format(video, backend.name)
@ConsoleApplication.command('Search videos')
def command_search(self, pattern=None):
self.load_modules(ICapVideo)
self.set_header(u'Search pattern: %s' % pattern if pattern else u'Last videos')
for backend, video in self.weboob.do('iter_search_results', pattern=pattern, nsfw=self.options.nsfw):
self.format(video, backend.name)

View file

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .videoob_web import VideoobWeb

View file

@ -0,0 +1,5 @@
.video-item
{
margin-bottom: 5ex;
margin-left: 2em;
}

View file

@ -0,0 +1,48 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from setuptools import setup
import os
setup(
name='weboob-videoob-web',
version='0.1',
description='Videoob-web, the Weboob video swiss-knife, web server version',
long_description='Search for videos on many websites, and get info about them',
author='Christophe Benz',
author_email='weboob@lists.symlink.me',
license='GPLv3',
url='http://weboob.org/VideoobWeb',
namespace_packages = ['weboob', 'weboob.frontends'],
packages=[
'weboob',
'weboob.frontends',
'weboob.frontends.videoob_web',
'weboob.frontends.videoob_web.public',
'weboob.frontends.videoob_web.templates',
],
scripts=[
'scripts/videoob-web-server',
],
install_requires=[
'weboob-video-backends',
],
)

View file

@ -0,0 +1,17 @@
## -*- coding: utf-8 -*-
<%def name="title()" filter="trim">
Videoob Web
</%def>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html>
<head>
<title>${self.title()}</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
${next.css()}
</head>
<body>
${next.body()}
</body>
</html>

View file

@ -0,0 +1,43 @@
## -*- coding: utf-8 -*-
<%inherit file="base.mako"/>
<%def name="css()" filter="trim">
<link rel="stylesheet" type="text/css" href="style.css"/>
</%def>
<%def name="video_item(item)">
<div class="video-item">
<a href="${item['page_url']}">
<img src="${item['thumbnail_url']}" alt="${item['title']}"/>
<br/>
${item['title']}
</a>
## (<a href="${item['url']}"><em>download</em></a>)
</div>
</%def>
<%def name="body()">
<h1>Videoob Web</h1>
<div id="search">
<form action="/" method="get">
<label for="q">Search pattern:</label>
<input id="q" type="text" name="q" value="${form_data['q']}" />
<input type="submit" value="Search" />
</form>
</div>
<div id="results">
% if merge:
% for item in results:
${video_item(item)}
% endfor
% else:
% for backend, items in sorted(results.iteritems()):
<h2>${backend}</h2>
% for item in items:
${video_item(item)}
% endfor
% endfor
% endif
</div>
</%def>

View file

@ -0,0 +1,110 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import os
from mako.lookup import TemplateLookup
from mako.runtime import Context
from routes import Mapper
from StringIO import StringIO
from webob.dec import wsgify
from webob import exc
from wsgiref.simple_server import make_server
from weboob.capabilities.video import ICapVideo
from weboob.tools.application import BaseApplication
__all__ = ['VideoobWeb']
template_lookup = TemplateLookup(directories=[os.path.join(os.path.dirname(__file__), 'templates')],
output_encoding='utf-8', encoding_errors='replace')
class VideoobWeb(BaseApplication):
APPNAME = 'videoob-web'
CONFIG = dict(host='localhost', port=8080)
@wsgify
def make_app(self, req):
map = Mapper()
map.connect('index', '/', method='index')
results = map.routematch(environ=req.environ)
if results:
match, route = results
req.urlvars = ((), match)
kwargs = match.copy()
method = kwargs.pop('method')
return getattr(self, method)(req, **kwargs)
else:
public_path = os.path.join(os.path.dirname(__file__), 'public')
if not os.path.exists(public_path):
return exc.HTTPNotFound()
path = req.path
if path.startswith('/'):
path = path[1:]
public_file_path = os.path.join(public_path, path)
if os.path.exists(public_file_path):
if path.endswith('.css'):
req.response.content_type = 'text/css'
elif path.endswith('.js'):
req.response.content_type = 'text/javascript'
return open(public_file_path, 'r').read().strip()
else:
return exc.HTTPNotFound()
def main(self, argv):
self.load_config()
self.weboob.load_modules(ICapVideo)
print 'Web server created. Listening on http://%s:%s' % (
self.config.get('host'), int(self.config.get('port')))
srv = make_server(self.config.get('host'), int(self.config.get('port')), self.make_app)
srv.serve_forever()
def index(self, req):
c = {}
nsfw = req.params.get('nsfw')
nsfw = False if not nsfw or nsfw == '0' else True
q = req.params.get('q', u'')
merge = req.params.get('merge')
merge = False if not merge or merge == '0' else True
c['merge'] = merge
c['form_data'] = dict(q=q)
c['results'] = [] if merge else {}
if q:
for backend in self.weboob.iter_backends():
videos = [dict(title=video.title,
page_url=video.page_url,
url=video.url if video.url else '/download?id=%s' % video.id,
thumbnail_url=video.thumbnail_url,
) \
for video in backend.iter_search_results(pattern=q, nsfw=nsfw)]
if videos:
if merge:
c['results'].extend(videos)
else:
c['results'][backend.name] = videos
if merge:
c['results'] = sorted(c['results'], key=lambda video: video['title'].lower())
template = template_lookup.get_template('index.mako')
buf = StringIO()
ctx = Context(buf, **c)
template.render_context(ctx)
return buf.getvalue().strip()

View file

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .weboobcfg import WeboobCfg

View file

@ -0,0 +1,178 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon, Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import ConfigParser
import logging
import os
import subprocess
import re
from weboob.tools.application import ConsoleApplication
__all__ = ['WeboobCfg']
class WeboobCfg(ConsoleApplication):
APPNAME = 'weboobcfg'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv):
return self.process_command(*argv[1:])
def caps_included(self, modcaps, caps):
modcaps = [x.__name__ for x in modcaps]
for cap in caps:
if not cap in modcaps:
return False
return True
@ConsoleApplication.command('List modules')
def command_modules(self, *caps):
print ' Name Capabilities Description '
print '+--------------+----------------------+----------------------------------------+'
self.weboob.modules_loader.load()
for name, module in self.weboob.modules_loader.modules.iteritems():
if caps and not self.caps_included(module.iter_caps(), caps):
continue
first_line = True
for cap in module.iter_caps():
if first_line:
print ' %-14s %-21s %s' % (name, cap.__name__, module.get_description())
first_line = False
else:
print ' %s' % cap.__name__
@ConsoleApplication.command('List applications')
def command_applications(self, *caps):
import weboob.frontends
path = weboob.frontends.__path__[0]
frontends = []
regexp = re.compile('^%s/([\w\d_]+)$' % path)
for root, dirs, files in os.walk(path):
m = regexp.match(root)
if m and '__init__.py' in files:
frontends.append(m.group(1))
print ' '.join(frontends)
@ConsoleApplication.command('Display a module')
def command_modinfo(self, name):
try:
module = self.weboob.modules_loader.get_or_load_module(name)
except KeyError:
logging.error('No such module: %s' % name)
return 1
print '.------------------------------------------------------------------------------.'
print '| Module %-69s |' % module.get_name()
print "+-----------------.------------------------------------------------------------'"
print '| Version | %s' % module.get_version()
print '| Maintainer | %s' % module.get_maintainer()
print '| License | %s' % module.get_license()
print '| Description | %s' % module.get_description()
print '| Capabilities | %s' % ', '.join([cap.__name__ for cap in module.iter_caps()])
first = True
for key, field in module.get_config().iteritems():
value = field.description
if not field.default is None:
value += ' (default: %s)' % field.default
if first:
print '| | '
print '| Configuration | %s: %s' % (key, value)
first = False
else:
print '| | %s: %s' % (key, value)
print "'-----------------' "
@ConsoleApplication.command('Add a configured backend')
def command_add(self, name, *options):
self.weboob.modules_loader.load()
if name not in [module_name for module_name, module in self.weboob.modules_loader.modules.iteritems()]:
logging.error(u'Backend "%s" does not exist.' % name)
return 1
params = {}
# set backend params from command-line arguments
for option in options:
try:
key, value = option.split('=', 1)
except ValueError:
logging.error(u'Parameters have to be formatted "key=value"')
return 1
params[key] = value
# ask for params non-specified on command-line arguments
module = self.weboob.modules_loader.get_or_load_module(name)
asked_config = False
for key, value in module.get_config().iteritems():
if not asked_config:
asked_config = True
print u'Configuration of backend'
print u'------------------------'
if key not in params:
params[key] = self.ask(' [%s] %s' % (key, value.description),
default=value.default,
masked=value.is_masked,
regexp=value.regexp)
else:
print u' [%s] %s: %s' % (key, value.description, '(masked)' if value.is_masked else params[key])
if asked_config:
print u'------------------------'
try:
self.weboob.backends_config.add_backend(name, name, params)
print u'Backend "%s" successfully added to file "%s".\n'\
'Please check configuration parameters values with "weboobcfg edit".' % (
name, self.weboob.backends_config.confpath)
except ConfigParser.DuplicateSectionError:
print u'Backend "%s" is already configured in file "%s"' % (name, self.weboob.backends_config.confpath)
response = raw_input(u'Add new instance of "%s" backend? [yN] ' % name)
if response.lower() == 'y':
while True:
new_name = raw_input(u'Please give new instance name (could be "%s_1"): ' % name)
if not new_name:
continue
try:
self.weboob.backends_config.add_backend(new_name, name, params)
print u'Backend "%s" successfully added to file "%s".\n'\
'Please check configuration parameters values with "weboobcfg edit".' % (
name, self.weboob.backends_config.confpath)
break
except ConfigParser.DuplicateSectionError:
print u'Instance "%s" already exists for backend "%s".' % (new_name, name)
@ConsoleApplication.command('List backends')
def command_list(self):
print ' Instance Name Name Params '
print '+---------------+--------------+------------------------------------------------+'
for instance_name, name, params in self.weboob.backends_config.iter_backends():
print ' %-15s %-14s %-47s' % (instance_name, name, ', '.join('%s=%s' % (key, value) for key, value in params.iteritems()))
@ConsoleApplication.command('Remove a backend')
def command_remove(self, instance_name):
try:
self.weboob.backends_config.remove_backend(instance_name)
except ConfigParser.NoSectionError:
logging.error("Backend '%s' does not exist" % instance_name)
return 1
@ConsoleApplication.command('Edit configuration file')
def command_edit(self):
subprocess.call([os.environ.get('EDITOR', 'vi'), self.weboob.backends_config.confpath])

View file

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .weboobdebug import WeboobDebug

View file

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import logging
from weboob.tools.application import ConsoleApplication
class WeboobDebug(ConsoleApplication):
APPNAME = 'weboobdebug'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Christophe Benz'
def main(self, argv):
return self.process_command(*argv[1:])
@ConsoleApplication.command('Debug backend')
def command_shell(self, backend_name):
try:
backend = self.weboob.load_modules(names=[backend_name])[backend_name]
except KeyError:
logging.error(u'Unable to load backend "%s"' % backend_name)
return 1
browser = backend.browser
from IPython.Shell import IPShellEmbed
shell = IPShellEmbed(argv=[])
locs = dict(backend=backend, browser=browser, frontend=self, weboob=self.weboob)
banner = 'Weboob debug shell\nBackend "%s" loaded.\nAvailable variables: %s' % (backend_name, locs)
shell.set_banner(shell.IP.BANNER + '\n\n' + banner)
shell(local_ns=locs, global_ns={})

View file

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .weboobtests import WeboobTests

View file

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from nose import run
from weboob.tools.application import ConsoleApplication
__all__ = ['WeboobTests']
class WeboobTests(ConsoleApplication):
APPNAME = 'weboobtests'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv):
return self.process_command(*argv[1:])
@ConsoleApplication.command('Run tests')
def command_run(self):
self.load_modules()
self.load_backends()
suite = []
for backend in self.weboob.iter_backends():
t = backend.get_test()
if t:
suite.append(t)
return run(suite=suite)

View file

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .weboorrents import Weboorrents

View file

@ -0,0 +1,46 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from setuptools import setup
import os
setup(
name='weboob-weboorrents',
version='0.1',
description='Weboorrents, the Weboob bittorrent swiss-knife',
long_description='Search for torrents on many websites, and get info about them',
author='Romain Bignon',
author_email='weboob@lists.symlink.me',
license='GPLv3',
url='http://weboob.org/Weboorrents',
namespace_packages = ['weboob', 'weboob.frontends'],
packages=[
'weboob',
'weboob.frontends',
'weboob.frontends.weboorrents',
],
scripts=[
'scripts/weboorrents',
],
install_requires=[
'weboob-torrent-backends',
],
)

View file

@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from __future__ import with_statement
import sys
from weboob.capabilities.torrent import ICapTorrent
from weboob.tools.application import ConsoleApplication
__all__ = ['Weboorrents']
class Weboorrents(ConsoleApplication):
APPNAME = 'weboorrents'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
CONFIG = {}
def main(self, argv):
self.load_backends(ICapTorrent)
return self.process_command(*argv[1:])
@ConsoleApplication.command('Get information about a torrent')
def command_info(self, id):
_id, backend_name = self.parse_id(id)
found = 0
for backend, torrent in self.weboob.do_backends(backend_name, 'get_torrent', _id):
if torrent:
self.format(torrent, backend.name)
found = 1
if not found:
print >>sys.stderr, 'Torrent "%s" not found' % id
@ConsoleApplication.command('Get the torrent file')
def command_getfile(self, id, dest):
_id, backend_name = self.parse_id(id)
for backend, buf in self.weboob.do_backends(backend_name, 'get_torrent_file', _id):
if buf:
if dest == '-':
print buf
else:
with open(dest, 'w') as f:
f.write(buf)
return
print >>sys.stderr, 'Torrent "%s" not found' % id
@ConsoleApplication.command('Search torrents')
def command_search(self, pattern=None):
self.set_header(u'Search pattern: %s' % pattern if pattern else u'Last torrents')
for backend, torrent in self.weboob.do('iter_torrents', pattern=pattern):
self.format(torrent, backend.name)

View file

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .wetboobs import WetBoobs

View file

@ -0,0 +1,45 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from setuptools import setup
import os
setup(
name='weboob-wetboobs',
version='0.1',
description='Wetboobs, the Weboob weather forecast swiss-knife',
author='Romain Bignon',
author_email='weboob@lists.symlink.me',
license='GPLv3',
url='http://weboob.org/Wetboobs',
namespace_packages = ['weboob', 'weboob.frontends'],
packages=[
'weboob',
'weboob.frontends',
'weboob.frontends.wetboobs',
],
scripts=[
'scripts/wetboobs',
],
install_requires=[
'weboob-weather-backends',
],
)

View file

@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import logging
from weboob.core import CallErrors
from weboob.capabilities.weather import ICapWeather, CityNotFound
from weboob.tools.application import ConsoleApplication
__all__ = ['WetBoobs']
class WetBoobs(ConsoleApplication):
APPNAME = 'wetboobs'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv):
self.load_modules(ICapWeather)
return self.process_command(*argv[1:])
@ConsoleApplication.command('search cities')
def command_search(self, pattern):
for backend, city in self.weboob.do('iter_city_search', pattern):
self.format(city, backend.name)
@ConsoleApplication.command('get current weather')
def command_current(self, city):
try:
for backend, current in self.weboob.do('get_current', city):
self.format(current, backend.name)
except CallErrors, e:
for error in e:
if isinstance(error, CityNotFound):
logging.error('City "%s" not found' % city)
else:
raise error
@ConsoleApplication.command('get forecasts')
def command_forecasts(self, city):
try:
for backend, forecast in self.weboob.do('iter_forecast', city):
self.format(forecast, backend.name)
except CallErrors, e:
for error in e:
if isinstance(error, CityNotFound):
logging.error('City "%s" not found' % city)
else:
raise error

View file

@ -216,7 +216,7 @@ class ConsoleApplication(BaseApplication):
loaded_backends = BaseApplication.load_backends(self, caps, names, *args, **kwargs)
if not loaded_backends:
logging.error(u'Cannot start application: no configured backend was found.\nHere is a list of all available backends:')
from weboob.frontends.weboobcfg import WeboobCfg
from weboob.applications.weboobcfg import WeboobCfg
weboobcfg = WeboobCfg()
if caps is not None:
if not isinstance(caps, (list, tuple, set)):