pax_global_header 0000666 0000000 0000000 00000000064 11430720725 0014513 g ustar 00root root 0000000 0000000 52 comment=92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/ 0000775 0000000 0000000 00000000000 11430720725 0021067 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/ 0000775 0000000 0000000 00000000000 11430720725 0022344 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/__init__.py 0000664 0000000 0000000 00000000070 11430720725 0024452 0 ustar 00root root 0000000 0000000 __import__('pkg_resources').declare_namespace(__name__)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/ 0000775 0000000 0000000 00000000000 11430720725 0025032 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/__init__.py 0000664 0000000 0000000 00000000070 11430720725 0027140 0 ustar 00root root 0000000 0000000 __import__('pkg_resources').declare_namespace(__name__)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/boobank/ 0000775 0000000 0000000 00000000000 11430720725 0026445 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/boobank/__init__.py 0000664 0000000 0000000 00000001354 11430720725 0030561 0 ustar 00root root 0000000 0000000 # -*- 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
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/boobank/boobank.py 0000664 0000000 0000000 00000005156 11430720725 0030441 0 ustar 00root root 0000000 0000000 #!/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.console import ConsoleApplication
__all__ = ['Boobank']
class Boobank(ConsoleApplication):
APPNAME = 'boobank'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv):
self.load_configured_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.core.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.core.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))
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/chatoob/ 0000775 0000000 0000000 00000000000 11430720725 0026451 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/chatoob/__init__.py 0000664 0000000 0000000 00000001353 11430720725 0030564 0 ustar 00root root 0000000 0000000 # -*- 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
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/chatoob/chatoob.py 0000664 0000000 0000000 00000004432 11430720725 0030445 0 ustar 00root root 0000000 0000000 # -*- 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.console 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_configured_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('iter_contacts', status=Contact.STATUS_ONLINE, caps=ICapContact):
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))
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/geolooc/ 0000775 0000000 0000000 00000000000 11430720725 0026461 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/geolooc/__init__.py 0000664 0000000 0000000 00000001456 11430720725 0030600 0 ustar 00root root 0000000 0000000 # -*- 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 .geolooc import Geolooc
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/geolooc/geolooc.py 0000664 0000000 0000000 00000002467 11430720725 0030473 0 ustar 00root root 0000000 0000000 # -*- 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.capabilities.geolocip import ICapGeolocIp
from weboob.tools.application.console import ConsoleApplication
__all__ = ['Geolooc']
class Geolooc(ConsoleApplication):
APPNAME = 'geolooc'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv):
if len(argv) < 2:
print >>sys.stderr, 'Syntax: %s ipaddr' % argv[0]
return 1
self.load_configured_backends(ICapGeolocIp)
for backend, location in self.weboob.do('get_location', argv[1]):
self.format(location, backend.name)
return 0
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/havesex/ 0000775 0000000 0000000 00000000000 11430720725 0026475 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/havesex/__init__.py 0000664 0000000 0000000 00000001352 11430720725 0030607 0 ustar 00root root 0000000 0000000 # -*- 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
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/havesex/havesex.py 0000664 0000000 0000000 00000010705 11430720725 0030515 0 ustar 00root root 0000000 0000000 # -*- 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 logging
import sys
import weboob
from weboob.tools.application.prompt 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_configured_backends(ICapDating, storage=self.create_storage(self.STORAGE_FILENAME))
self.weboob.do('init_optimizations').wait()
optimizations = self.config.get('optimizations')
if optimizations:
optimizations_list = optimizations.strip().split(' ')
if optimizations_list:
self.optims('Starting', 'start_optimization', optimizations_list)
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)
def print_node(node, level=1):
if node.flags & node.SECTION:
print '\t' * level + node.label
for sub in node.value:
print_node(sub, level+1)
else:
if isinstance(node.value, (tuple,list)):
value = ','.join([unicode(v) for v in node.value])
else:
value = node.value
print '\t' * level + '%-20s %s' % (node.label + ':', value)
found = 0
for backend, contact in self.weboob.do('get_contact', _id, backends=backend_name):
if contact:
print 'Nickname:', contact.name
if contact.status & contact.STATUS_ONLINE:
s = 'online'
elif contact.status & contact.STATUS_OFFLINE:
s = 'offline'
elif contact.status & contact.STATUS_AWAY:
s = 'away'
else:
s = 'unknown'
print 'Status: %s (%s)' % (s, contact.status_msg)
print 'Photos:'
for name, photo in contact.photos.iteritems():
print '\t%s' % photo
print 'Profile:'
for head in contact.profile:
print_node(head)
print 'Description:'
print '\n'.join(['\t%s' % s for s in contact.summary.split('\n')])
found = 1
if not found:
logging.error(u'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.core.CallErrors, errors:
for backend, error, backtrace in errors:
if isinstance(error, OptimizationNotFound):
logging.error(u'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)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/masstransit/ 0000775 0000000 0000000 00000000000 11430720725 0027402 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/masstransit/__init__.py 0000664 0000000 0000000 00000001466 11430720725 0031522 0 ustar 00root root 0000000 0000000 # -*- 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
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/masstransit/masstransit.py 0000664 0000000 0000000 00000020461 11430720725 0032327 0 ustar 00root root 0000000 0000000 # -*- 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.base 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):
self.load_backends(ICapTravel)
MasstransitHildon(self.weboob)
gtk.main()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/monboob/ 0000775 0000000 0000000 00000000000 11430720725 0026465 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/monboob/__init__.py 0000664 0000000 0000000 00000001455 11430720725 0030603 0 ustar 00root root 0000000 0000000 # -*- 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
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/monboob/monboob.py 0000664 0000000 0000000 00000022662 11430720725 0030502 0 ustar 00root root 0000000 0000000 # -*- 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.ouiboube import Weboob
from weboob.core.scheduler import Scheduler
from weboob.capabilities.messages import ICapMessages, ICapMessagesReply, Message
from weboob.tools.application.console 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_configured_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.backend_instances[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'
-- %s
' % 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']
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qboobmsg/ 0000775 0000000 0000000 00000000000 11430720725 0026643 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qboobmsg/__init__.py 0000664 0000000 0000000 00000000037 11430720725 0030754 0 ustar 00root root 0000000 0000000 from .qboobmsg import QBoobMsg
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qboobmsg/main_window.py 0000664 0000000 0000000 00000003264 11430720725 0031535 0 ustar 00root root 0000000 0000000 # -*- 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.actionBackends, SIGNAL("triggered()"), self.backendsConfig)
self.connect(self.ui.actionRefresh, SIGNAL("triggered()"), self.refresh)
def backendsConfig(self):
bckndcfg = BackendCfg(self.weboob, (ICapMessages,), self)
bckndcfg.show()
def refresh(self):
self.centralWidget().refresh()
messages_manager.py 0000664 0000000 0000000 00000011557 11430720725 0032450 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qboobmsg # -*- 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)
self.process.do(command, backends=self.backend, caps=ICapMessages)
def _gotMessage(self, backend, message):
if message is None:
self.ui.backendsList.setEnabled(True)
self.process = None
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("
%s
"
"Date: %s "
"From: %s "
"
%s
"
% (message.title, str(message.date), message.sender, message.content))
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qboobmsg/qboobmsg.py 0000664 0000000 0000000 00000002316 11430720725 0031030 0 ustar 00root root 0000000 0000000 # -*- 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.qt 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_configured_backends(ICapMessages, storage=self.create_storage())
self.main_window = MainWindow(self.config, self.weboob)
self.main_window.show()
return self.weboob.loop()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qboobmsg/ui/ 0000775 0000000 0000000 00000000000 11430720725 0027260 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qboobmsg/ui/Makefile 0000664 0000000 0000000 00000000265 11430720725 0030723 0 ustar 00root root 0000000 0000000 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)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qboobmsg/ui/__init__.py 0000664 0000000 0000000 00000000000 11430720725 0031357 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qboobmsg/ui/main_window.ui 0000664 0000000 0000000 00000004175 11430720725 0032141 0 ustar 00root root 0000000 0000000
MainWindow00763580QBoobMsg0076324FiletoolBarTopToolBarAreafalseBackendsQuitQuitRefreshactionQuittriggered()MainWindowclose()-1-1381289
messages_manager.ui 0000664 0000000 0000000 00000012316 11430720725 0033044 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qboobmsg/ui
MessagesManager00767591Qt::Horizontal0020Qt::VerticalQAbstractItemView::NoEditTriggerstruetruetruefalse100truetrue100truetrueDateAlignLeft|AlignVCenterFromTitletrueQt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouseQFrame::StyledPanelQFrame::Raised00+00−Qt::Vertical2040expandButtonclicked()messagesTreeexpandAll()73331527150collapseButtonclicked()messagesTreecollapseAll()73360527150
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qhavesex/ 0000775 0000000 0000000 00000000000 11430720725 0026656 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qhavesex/__init__.py 0000664 0000000 0000000 00000000037 11430720725 0030767 0 ustar 00root root 0000000 0000000 from .qhavesex import QHaveSex
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qhavesex/contacts.py 0000664 0000000 0000000 00000027247 11430720725 0031062 0 ustar 00root root 0000000 0000000 # -*- 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
import logging
from PyQt4.QtGui import QWidget, QListWidgetItem, QImage, QIcon, QPixmap, \
QFrame, QMessageBox, QTabWidget, QVBoxLayout, \
QFormLayout, QLabel
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
from .ui.profile_ui import Ui_Profile
class ThreadMessage(QFrame):
"""
This class represents a message in the thread tab.
"""
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('&', '&').replace('<', '<').replace('>', '>').replace('\n', ' ')
self.ui.contentLabel.setText(content)
class ContactThread(QWidget):
"""
The thread of the selected contact.
"""
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_msg = QtDo(self.weboob, self.gotMessage)
self.process_msg.do(command, thread=self.contact.id, backends=self.contact.backend)
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())
self.process_msg = None
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_reply = QtDo(self.weboob, self._postReply_cb, self._postReply_eb)
self.process_reply.do('post_reply', self.contact.id, 0, '', text, backends=self.contact.backend)
def _postReply_cb(self, backend, ignored):
self.ui.textEdit.clear()
self.ui.textEdit.setEnabled(True)
self.ui.sendButton.setEnabled(True)
self.refreshMessages()
self.process_reply = None
def _postReply_eb(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)
self.process_reply = None
class ContactProfile(QWidget):
def __init__(self, weboob, contact, parent=None):
QWidget.__init__(self, parent)
self.ui = Ui_Profile()
self.ui.setupUi(self)
self.weboob = weboob
self.contact = contact
if self.gotProfile(contact.backend, contact):
self.process_contact = QtDo(self.weboob, self.gotProfile)
self.process_contact.do('fillobj', self.contact, ['photos', 'profile'], backends=self.contact.backend)
def gotProfile(self, backend, contact):
if not backend:
return
first = True
for photo in contact.photos.itervalues():
photo = contact.photos.values()[0]
if first and photo.data:
img = QImage.fromData(photo.data)
self.ui.photoLabel.setPixmap(QPixmap.fromImage(img))
first = False
else:
# TODO display thumbnails
pass
self.ui.nicknameLabel.setText('
' % (contact.summary.replace('\n', ' ') or 'Receiving...'))
if not contact.profile:
return True
for head in contact.profile:
if head.flags & head.HEAD:
widget = self.ui.headFrame
else:
widget = self.ui.profileTab
self.process_node(head, widget)
return False
def process_node(self, node, widget):
# Set the value widget
value = None
if node.flags & node.SECTION:
value = QWidget()
value.setLayout(QFormLayout())
for sub in node.value:
self.process_node(sub, value)
elif isinstance(node.value, list):
value = QLabel(' '.join([unicode(s) for s in node.value]))
value.setWordWrap(True)
elif isinstance(node.value, tuple):
value = QLabel(', '.join([unicode(s) for s in node.value]))
value.setWordWrap(True)
elif isinstance(node.value, (str,unicode,int,long,float)):
value = QLabel(unicode(node.value))
else:
logging.warning('Not supported value: %r' % node.value)
return
# Insert the value widget into the parent widget, depending
# of its type.
if isinstance(widget, QTabWidget):
widget.addTab(value, node.label)
elif isinstance(widget.layout(), QFormLayout):
label = QLabel(u'%s: ' % node.label)
widget.layout().addRow(label, value)
elif isinstance(widget.layout(), QVBoxLayout):
widget.layout().addWidget(value)
else:
logging.warning('Not supported widget: %r' % widget)
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|Contact.STATUS_AWAY
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('iter_contacts', status, caps=ICapContact)
def cb(self, cb, backend, contact):
if contact:
contact.backend = backend
cb(contact)
elif not backend:
self.process = None
cb(None)
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.ui.groupBox.setCurrentIndex(1)
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.refreshContactList()
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 setPhoto(self, contact, item):
if not contact:
return
img = None
for photo in contact.photos.itervalues():
if photo.thumbnail_data:
img = QImage.fromData(photo.thumbnail_data)
break
if img:
item.setIcon(QIcon(QPixmap.fromImage(img)))
def addContact(self, contact):
if not contact:
return
status = ''
if contact.status == Contact.STATUS_ONLINE:
status = u'Online'
status_color = 0x00aa00
elif contact.status == Contact.STATUS_OFFLINE:
status = u'Offline'
status_color = 0xff0000
elif contact.status == Contact.STATUS_AWAY:
status = u'Away'
status_color = 0xffad16
else:
status = u'Unknown'
status_color = 0xaaaaaa
if contact.status_msg:
status += u' — %s' % contact.status_msg
item = QListWidgetItem()
item.setText('
%s
%s %s' % (contact.name, status_color, status, contact.backend.name))
item.setData(Qt.UserRole, contact)
process = QtDo(self.weboob, lambda b, c: self.setPhoto(c, item))
process.do('fillobj', contact, ['photos'], backends=contact.backend)
for i in xrange(self.ui.contactList.count()):
if self.ui.contactList.item(i).data(Qt.UserRole).toPyObject().status > contact.status:
self.ui.contactList.insertItem(i, item)
return
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()
if not self.contact:
return
self.ui.tabWidget.addTab(ContactProfile(self.weboob, self.contact), 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'))
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qhavesex/main_window.py 0000664 0000000 0000000 00000005012 11430720725 0031541 0 ustar 00root root 0000000 0000000 # -*- 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
try:
from weboob.applications.qboobmsg.messages_manager import MessagesManager
HAVE_BOOBMSG = True
except ImportError:
HAVE_BOOBMSG = False
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.connect(self.ui.actionBackends, SIGNAL("triggered()"), self.backendsConfig)
self.connect(self.ui.tabWidget, SIGNAL('currentChanged(int)'), self.tabChanged)
self.ui.tabWidget.addTab(AccountsStatus(self.weboob), self.tr('Status'))
if HAVE_BOOBMSG:
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.ui.tabWidget.addTab(QWidget(), self.tr('Optimizations'))
def backendsConfig(self):
bckndcfg = BackendCfg(self.weboob, (ICapDating,), self)
if bckndcfg.run():
self.loaded_tabs.clear()
widget = self.ui.tabWidget.widget(self.ui.tabWidget.currentIndex())
widget.load()
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
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qhavesex/qhavesex.py 0000664 0000000 0000000 00000002404 11430720725 0031054 0 ustar 00root root 0000000 0000000 # -*- 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.qt 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.create_storage(self.STORAGE_FILENAME)
self.load_configured_backends(ICapDating)
self.main_window = MainWindow(self.config, self.weboob)
self.main_window.show()
return self.weboob.loop()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qhavesex/status.py 0000664 0000000 0000000 00000007436 11430720725 0030565 0 ustar 00root root 0000000 0000000 # -*- 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 weboob.capabilities.dating import StatusField
from weboob.tools.application.qt import QtDo
class Account(QFrame):
def __init__(self, weboob, backend, parent=None):
QFrame.__init__(self, parent)
self.setFrameShape(QFrame.StyledPanel)
self.setFrameShadow(QFrame.Raised)
self.weboob = weboob
self.backend = backend
self.setLayout(QVBoxLayout())
head = QHBoxLayout()
headw = QWidget()
headw.setLayout(head)
self.title = QLabel(u'
%s — %s
' % (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(u'Waiting...')
self.layout().addWidget(headw)
self.layout().addWidget(self.body)
self.weboob.repeat(60, self.updateStats)
def updateStats(self):
self.process = QtDo(self.weboob, self.updateStats_cb, self.updateStats_eb)
self.process.body = u''
self.process.in_p = False
self.process.do('get_status', backends=self.backend)
def updateStats_cb(self, backend, field):
if not field:
if self.process.in_p:
self.process.body += u""
self.body.setText(self.process.body)
self.process = None
return
if field.flags & StatusField.FIELD_HTML:
value = u'%s' % field.value
else:
value = (u'%s' % field.value).replace('&', '&').replace('<', '<').replace('>', '>')
if field.flags & StatusField.FIELD_TEXT:
if self.process.in_p:
self.process.body += u''
self.process.body += u'
%s
' % value
self.process.in_p = False
else:
if not self.process.in_p:
self.process.body += u"
"
self.process.in_p = True
else:
self.process.body += u" "
self.process.body += u'%s: %s' % (field.label, field.value)
def updateStats_eb(self, backend, err, backtrace):
self.body.setText(u'Unable to connect: %s' % unicode(err))
self.title.setText(u'%s' % unicode(self.title.text()))
class AccountsStatus(QWidget):
def __init__(self, weboob, parent=None):
QWidget.__init__(self, parent)
self.weboob = weboob
self.setLayout(QVBoxLayout())
def load(self):
while self.layout().count() > 0:
item = self.layout().takeAt(0)
if item.widget():
item.widget().hide()
item.widget().deleteLater()
for backend in self.weboob.iter_backends():
account = Account(self.weboob, backend)
self.layout().addWidget(account)
self.layout().addStretch()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qhavesex/ui/ 0000775 0000000 0000000 00000000000 11430720725 0027273 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qhavesex/ui/Makefile 0000664 0000000 0000000 00000000265 11430720725 0030736 0 ustar 00root root 0000000 0000000 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)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qhavesex/ui/__init__.py 0000664 0000000 0000000 00000000000 11430720725 0031372 0 ustar 00root root 0000000 0000000 contact_thread.ui 0000664 0000000 0000000 00000005011 11430720725 0032532 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qhavesex/ui
ContactThread00578429FormQt::VerticalQFrame::StyledPanelQFrame::Raised00Send01Qt::ScrollBarAsNeededtrueQt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft00556187QWidget#scrollAreaContent {
background-color: rgb(255, 255, 255);
}
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qhavesex/ui/contacts.ui 0000664 0000000 0000000 00000005045 11430720725 0031454 0 ustar 00root root 0000000 0000000
Contacts00482320FormQt::Horizontal00QFrame::StyledPanelQFrame::Raised../../../../../../../../usr/share/icons/oxygen/16x16/actions/view-refresh.png../../../../../../../../usr/share/icons/oxygen/16x16/actions/view-refresh.png1201201truefalse10
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qhavesex/ui/main_window.ui 0000664 0000000 0000000 00000004166 11430720725 0032154 0 ustar 00root root 0000000 0000000
MainWindow00763580QHaveSex-10076324FiletoolBarTopToolBarAreafalseBackendsQuitQuitactionQuittriggered()MainWindowclose()-1-1381289
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qhavesex/ui/profile.ui 0000664 0000000 0000000 00000012433 11430720725 0031275 0 ustar 00root root 0000000 0000000
Profile00460320Form0QFrame::NoFrameQFrame::Plaintrue0046032000QFrame::StyledPanelQFrame::RaisedQFrame::StyledPanelQFrame::Raised<i>Receiving...</i>Qt::Vertical0000QFrame::StyledPanelQFrame::Raised<h1>Nickname</h1>statusQt::Vertical200Qt::Horizontal00true20-1
thread_message.ui 0000664 0000000 0000000 00000005367 11430720725 0032541 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qhavesex/ui
ThreadMessage0055276FrameQFrame::StyledPanelQFrame::RaisedQt::Vertical20100trueQt::LinksAccessibleByMouse|Qt::TextSelectableByMousetrueQt::LinksAccessibleByMouse|Qt::TextSelectableByMouseQt::Vertical201
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qvideoob/ 0000775 0000000 0000000 00000000000 11430720725 0026642 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qvideoob/__init__.py 0000664 0000000 0000000 00000000037 11430720725 0030753 0 ustar 00root root 0000000 0000000 from .qvideoob import QVideoob
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qvideoob/main_window.py 0000664 0000000 0000000 00000010674 11430720725 0031537 0 ustar 00root root 0000000 0000000 # -*- 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.applications.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()
minivideo.deleteLater()
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)
self.process.do('iter_search_results', pattern, self.ui.sortbyEdit.currentIndex(), nsfw=True, backends=backend_name)
def addVideo(self, backend, video):
if not backend:
self.ui.searchEdit.setEnabled(True)
self.process = None
return
minivideo = MiniVideo(self.weboob, 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()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qvideoob/minivideo.py 0000664 0000000 0000000 00000004747 11430720725 0031213 0 ustar 00root root 0000000 0000000 # -*- 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 QFrame, QImage, QPixmap
from weboob.tools.application.qt import QtDo
from weboob.applications.qvideoob.ui.minivideo_ui import Ui_MiniVideo
from .video import Video
class MiniVideo(QFrame):
def __init__(self, weboob, backend, video, parent=None):
QFrame.__init__(self, parent)
self.ui = Ui_MiniVideo()
self.ui.setupUi(self)
self.weboob = weboob
self.backend = backend
self.video = video
self.ui.titleLabel.setText(video.title)
self.ui.backendLabel.setText(backend.name)
self.ui.durationLabel.setText(unicode(video.duration))
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)
self.process_thumbnail = QtDo(self.weboob, self.gotThumbnail)
self.process_thumbnail.do('fillobj', self.video, ['thumbnail'], backends=backend)
def gotThumbnail(self, backend, video):
if not backend:
return
if video.thumbnail.data:
img = QImage.fromData(video.thumbnail.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()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qvideoob/qvideoob.py 0000664 0000000 0000000 00000002574 11430720725 0031034 0 ustar 00root root 0000000 0000000 # -*- 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.qt 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_backends(ICapVideo)
self.load_config()
self.main_window = MainWindow(self.config, self.weboob)
self.main_window.show()
return self.weboob.loop()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qvideoob/ui/ 0000775 0000000 0000000 00000000000 11430720725 0027257 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qvideoob/ui/Makefile 0000664 0000000 0000000 00000000265 11430720725 0030722 0 ustar 00root root 0000000 0000000 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)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qvideoob/ui/__init__.py 0000664 0000000 0000000 00000000000 11430720725 0031356 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qvideoob/ui/main_window.ui 0000664 0000000 0000000 00000011757 11430720725 0032144 0 ustar 00root root 0000000 0000000
MainWindow00785594QVideoobQFrame::StyledPanelQFrame::RaisedSearch: 00RelevanceRatingDurationDate10000Display:SFWtrueNSFWtrueQt::Horizontal4020true00763391QWidget#scrollAreaContent {
background-color: rgb(255, 255, 255);
}QFrame::StyledPanelQFrame::RaisedURL: 0078525
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qvideoob/ui/minivideo.ui 0000664 0000000 0000000 00000011337 11430720725 0031606 0 ustar 00root root 0000000 0000000
MiniVideo00464132FormQFrame::StyledPanelQFrame::Raised95500QFormLayout::ExpandingFieldsGrow75trueTitle0050truefalseTextLabel75trueDurationTextLabel75trueAuthorTextLabel75trueDateTextLabel75trueRatingTextLabel75trueWhereTextLabel
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qvideoob/ui/video.ui 0000664 0000000 0000000 00000013711 11430720725 0030727 0 ustar 00root root 0000000 0000000
Video00647404Video1275trueQFrame::BoxQFrame::RaisedTextLabelQt::AlignCenter00background-color: rgb(255, 255, 255);QFrame::StyledPanelQFrame::Sunken00truetrueQFrame::StyledPanelQFrame::RaisedQFormLayout::ExpandingFieldsGrow75trueURLtrueArrowCursortruefalsetrue75trueDurationTextLabel75trueAuthorTextLabel75trueDateTextLabel75trueRatingTextLabelPhonon::VideoPlayerQWidgetphonon/videoplayer.hPhonon::SeekSliderQWidgetphonon/seekslider.h
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qvideoob/video.py 0000664 0000000 0000000 00000003501 11430720725 0030321 0 ustar 00root root 0000000 0000000 # -*- 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.applications.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(unicode(video.duration))
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()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qweboobcfg/ 0000775 0000000 0000000 00000000000 11430720725 0027150 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qweboobcfg/__init__.py 0000664 0000000 0000000 00000001360 11430720725 0031261 0 ustar 00root root 0000000 0000000 # -*- 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 .qweboobcfg import QWeboobCfg
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/qweboobcfg/qweboobcfg.py 0000664 0000000 0000000 00000002227 11430720725 0031643 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
# -*- 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 weboob.tools.application.qt import BackendCfg, QtApplication
class QWeboobCfg(QtApplication):
APPNAME = 'qweboobcfg'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv):
self.load_configured_backends()
self.dlg = BackendCfg(self.weboob)
self.dlg.show()
return self.weboob.loop()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/traveloob/ 0000775 0000000 0000000 00000000000 11430720725 0027027 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/traveloob/__init__.py 0000664 0000000 0000000 00000001462 11430720725 0031143 0 ustar 00root root 0000000 0000000 # -*- 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 .traveloob import Traveloob
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/traveloob/traveloob.py 0000664 0000000 0000000 00000004323 11430720725 0031400 0 ustar 00root root 0000000 0000000 # -*- 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.
import logging
from weboob.capabilities.travel import ICapTravel
from weboob.tools.application.console import ConsoleApplication
__all__ = ['Traveloob']
class Traveloob(ConsoleApplication):
APPNAME = 'traveloob'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv):
return self.process_command(*argv[1:])
@ConsoleApplication.command('Search stations')
def command_stations(self, pattern):
self.load_backends(ICapTravel)
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):
station_id, backend_name = self.parse_id(station)
if arrival:
arrival_id, backend_name2 = self.parse_id(arrival)
if backend_name and backend_name2 and backend_name != backend_name2:
logging.error('Departure and arrival aren\'t on the same backend')
return 1
else:
arrival_id = backend_name2 = None
if backend_name:
backends = [backend_name]
elif backend_name2:
backends = [backend_name2]
else:
backends = None
self.load_backends(ICapTravel, names=backends)
for backend, departure in self.weboob.do('iter_station_departures', station_id, arrival_id):
self.format(departure, backend.name)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/videoob/ 0000775 0000000 0000000 00000000000 11430720725 0026461 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/videoob/__init__.py 0000664 0000000 0000000 00000001354 11430720725 0030575 0 ustar 00root root 0000000 0000000 # -*- 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
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/videoob/videoob.py 0000664 0000000 0000000 00000004140 11430720725 0030461 0 ustar 00root root 0000000 0000000 # -*- 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.console import ConsoleApplication
__all__ = ['Videoob']
class Videoob(ConsoleApplication):
APPNAME = 'videoob'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Christophe Benz, Romain Bignon'
def add_application_options(self, group):
group.add_option('--nsfw', action='store_true', help='enable non-suitable for work videos')
def main(self, argv):
if self.options.backends:
self.options.nsfw = True
return self.process_command(*argv[1:])
@ConsoleApplication.command('Get information about a video (accepts 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_backends(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 for videos')
def command_search(self, pattern=None):
self.load_backends(ICapVideo)
self.set_formatter_header(u'Search pattern: %s' % pattern if pattern else u'Last videos')
for backend, video in self.do('iter_search_results', pattern=pattern, nsfw=self.options.nsfw):
self.format(video, backend.name)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/videoob_web/ 0000775 0000000 0000000 00000000000 11430720725 0027316 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/videoob_web/__init__.py 0000664 0000000 0000000 00000001363 11430720725 0031432 0 ustar 00root root 0000000 0000000 # -*- 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
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/videoob_web/public/ 0000775 0000000 0000000 00000000000 11430720725 0030574 5 ustar 00root root 0000000 0000000 style.css 0000664 0000000 0000000 00000000076 11430720725 0032372 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/videoob_web/public .video-item
{
margin-bottom: 5ex;
margin-left: 2em;
}
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/videoob_web/templates/ 0000775 0000000 0000000 00000000000 11430720725 0031314 5 ustar 00root root 0000000 0000000 base.mako 0000664 0000000 0000000 00000000622 11430720725 0033020 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/videoob_web/templates ## -*- coding: utf-8 -*-
<%def name="title()" filter="trim">
Videoob Web
%def>
% if merge:
% for item in results:
${video_item(item)}
% endfor
% else:
% for backend, items in sorted(results.iteritems()):
${backend}
% for item in items:
${video_item(item)}
% endfor
% endfor
% endif
%def>
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/videoob_web/videoob_web.py 0000664 0000000 0000000 00000010160 11430720725 0032152 0 ustar 00root root 0000000 0000000 # -*- 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.base 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_backends(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()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/weboobcfg/ 0000775 0000000 0000000 00000000000 11430720725 0026767 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/weboobcfg/__init__.py 0000664 0000000 0000000 00000001356 11430720725 0031105 0 ustar 00root root 0000000 0000000 # -*- 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
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/weboobcfg/weboobcfg.py 0000664 0000000 0000000 00000017415 11430720725 0031306 0 ustar 00root root 0000000 0000000 # -*- 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.console import ConsoleApplication
from weboob.tools.ordereddict import OrderedDict
__all__ = ['WeboobCfg']
class WeboobCfg(ConsoleApplication):
APPNAME = 'weboob-config'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Christophe Benz, 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('Add a configured backend')
def command_add(self, name, *options):
self.weboob.backends_loader.load_all()
if name not in [_name for _name, backend in self.weboob.backends_loader.loaded.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
backend = self.weboob.backends_loader.get_or_load_backend(name)
asked_config = False
for key, value in backend.config.iteritems():
if not asked_config:
asked_config = True
print 'Configuration of backend'
print '------------------------'
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 ' [%s] %s: %s' % (key, value.description, '(masked)' if value.is_masked else params[key])
if asked_config:
print '------------------------'
try:
self.weboob.backends_config.add_backend(name, name, params)
print 'Backend "%s" successfully added to file "%s".\n'\
'Please check configuration parameters values with "weboob-config edit".' % (
name, self.weboob.backends_config.confpath)
except ConfigParser.DuplicateSectionError:
print '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 'Backend "%s" successfully added to file "%s".\n'\
'Please check configuration parameters values with "weboob-config edit".' % (
name, self.weboob.backends_config.confpath)
break
except ConfigParser.DuplicateSectionError:
print 'Instance "%s" already exists for backend "%s".' % (new_name, name)
@ConsoleApplication.command('Show configured backends')
def command_listconfigured(self):
self.set_default_formatter('table')
for instance_name, name, params in sorted(self.weboob.backends_config.iter_backends()):
row = OrderedDict([('Instance name', instance_name),
('Backend name', name),
('Configuration', ', '.join('%s=%s' % (key, value) for key, value in params.iteritems())),
])
self.format(row)
@ConsoleApplication.command('Remove a configured backend')
def command_remove(self, instance_name):
try:
self.weboob.backends_config.remove_backend(instance_name)
except ConfigParser.NoSectionError:
logging.error('Backend instance "%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])
@ConsoleApplication.command('Show available backends')
def command_backends(self, *caps):
self.set_default_formatter('table')
self.weboob.backends_loader.load_all()
for name, backend in sorted(self.weboob.backends_loader.loaded.iteritems()):
if caps and not self.caps_included(backend.iter_caps(), caps):
continue
row = OrderedDict([('Name', name),
('Capabilities', ', '.join(cap.__name__ for cap in backend.iter_caps())),
('Description', backend.description),
])
self.format(row)
@ConsoleApplication.command('Display information about a backend')
def command_info(self, name):
try:
backend = self.weboob.backends_loader.get_or_load_backend(name)
except KeyError:
logging.error('No such backend: "%s"' % name)
return 1
print '.------------------------------------------------------------------------------.'
print '| Backend %-68s |' % backend.name
print "+-----------------.------------------------------------------------------------'"
print '| Version | %s' % backend.version
print '| Maintainer | %s' % backend.maintainer
print '| License | %s' % backend.license
print '| Description | %s' % backend.description
print '| Capabilities | %s' % ', '.join([cap.__name__ for cap in backend.iter_caps()])
first = True
for key, field in backend.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('Show applications')
def command_applications(self, *caps):
applications = set()
import weboob.applications
for path in weboob.applications.__path__:
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:
applications.add(m.group(1))
print ' '.join(sorted(applications)).encode('utf-8')
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/weboobcli/ 0000775 0000000 0000000 00000000000 11430720725 0026777 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/weboobcli/__init__.py 0000664 0000000 0000000 00000001356 11430720725 0031115 0 ustar 00root root 0000000 0000000 # -*- 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 .weboobcli import WeboobCli
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/weboobcli/weboobcli.py 0000664 0000000 0000000 00000003064 11430720725 0031321 0 ustar 00root root 0000000 0000000 # -*- 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 sys
from weboob.tools.application.console import ConsoleApplication
__all__ = ['WeboobCli']
class WeboobCli(ConsoleApplication):
APPNAME = 'weboob-cli'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def add_application_options(self, group):
group.add_option('-C', '--configured', action='store_true', help='load configured backends')
def main(self, argv):
if len(argv) < 3:
print >>sys.stderr, "Syntax: %s capability command [args ..]" % argv[0]
return 1
cap_s = argv[1]
cmd = argv[2]
args = argv[3:]
if self.options.configured:
self.load_configured_backends(cap_s)
else:
self.load_backends(cap_s)
for backend, obj in self.weboob.do(cmd, *args):
self.format(obj, backend.name)
return 0
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/weboobdebug/ 0000775 0000000 0000000 00000000000 11430720725 0027316 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/weboobdebug/__init__.py 0000664 0000000 0000000 00000001363 11430720725 0031432 0 ustar 00root root 0000000 0000000 # -*- 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
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/weboobdebug/weboobdebug.py 0000664 0000000 0000000 00000003321 11430720725 0032153 0 ustar 00root root 0000000 0000000 # -*- 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.console 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_backends(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, application=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={})
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/weboobtests/ 0000775 0000000 0000000 00000000000 11430720725 0027372 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/weboobtests/__init__.py 0000664 0000000 0000000 00000001361 11430720725 0031504 0 ustar 00root root 0000000 0000000 # -*- 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
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/weboobtests/weboobtests.py 0000664 0000000 0000000 00000002550 11430720725 0032306 0 ustar 00root root 0000000 0000000 # -*- 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.console 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_backends()
self.load_configured_backends()
suite = []
for backend in self.weboob.iter_backends():
test = backend.get_test()
if test:
suite.append(test)
return run(suite=suite)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/weboorrents/ 0000775 0000000 0000000 00000000000 11430720725 0027403 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/weboorrents/__init__.py 0000664 0000000 0000000 00000001362 11430720725 0031516 0 ustar 00root root 0000000 0000000 # -*- 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
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/weboorrents/weboorrents.py 0000664 0000000 0000000 00000004621 11430720725 0032331 0 ustar 00root root 0000000 0000000 # -*- 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.console import ConsoleApplication
__all__ = ['Weboorrents']
class Weboorrents(ConsoleApplication):
APPNAME = 'weboorrents'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv):
self.load_configured_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('get_torrent', _id, backends=backend_name):
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('get_torrent_file', _id, backends=backend_name):
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_formatter_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)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/wetboobs/ 0000775 0000000 0000000 00000000000 11430720725 0026656 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/wetboobs/__init__.py 0000664 0000000 0000000 00000001354 11430720725 0030772 0 ustar 00root root 0000000 0000000 # -*- 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
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/applications/wetboobs/wetboobs.py 0000664 0000000 0000000 00000004366 11430720725 0031065 0 ustar 00root root 0000000 0000000 # -*- 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.console import ConsoleApplication
__all__ = ['WetBoobs']
class WetBoobs(ConsoleApplication):
APPNAME = 'wetboobs'
VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv):
self.load_backends(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
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/ 0000775 0000000 0000000 00000000000 11430720725 0024116 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/__init__.py 0000664 0000000 0000000 00000000070 11430720725 0026224 0 ustar 00root root 0000000 0000000 __import__('pkg_resources').declare_namespace(__name__)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/arte/ 0000775 0000000 0000000 00000000000 11430720725 0025051 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/arte/__init__.py 0000664 0000000 0000000 00000001355 11430720725 0027166 0 ustar 00root root 0000000 0000000 # -*- 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 .backend import ArteBackend
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/arte/backend.py 0000664 0000000 0000000 00000003633 11430720725 0027017 0 ustar 00root root 0000000 0000000 # -*- 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 weboob.capabilities.video import ICapVideo
from weboob.tools.backend import BaseBackend
from .browser import ArteBrowser
from .video import ArteVideo
__all__ = ['ArteBackend']
class ArteBackend(BaseBackend, ICapVideo):
NAME = 'arte'
MAINTAINER = 'Romain Bignon'
EMAIL = 'romain@peerfuse.org'
VERSION = '0.2'
DESCRIPTION = 'Arte french TV'
LICENSE = 'GPLv3'
BROWSER = ArteBrowser
def get_video(self, _id):
with self.browser:
return self.browser.get_video(_id)
def iter_search_results(self, pattern=None, sortby=ICapVideo.SEARCH_RELEVANCE, nsfw=False):
with self.browser:
return self.browser.iter_search_results(pattern)
def fill_video(self, video, fields):
if fields != ['thumbnail']:
# if we don't want only the thumbnail, we probably want also every fields
with self.browser:
video = self.browser.get_video(ArteVideo.id2url(video.id), video)
if 'thumbnail' in fields:
with self.browser:
video.thumbnail.data = self.browser.readurl(video.thumbnail.url)
return video
OBJECTS = {ArteVideo: fill_video}
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/arte/browser.py 0000664 0000000 0000000 00000003300 11430720725 0027102 0 ustar 00root root 0000000 0000000 # -*- 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.tools.browser import BaseBrowser
from weboob.tools.browser.decorators import id2url
from .pages import IndexPage, VideoPage
from .video import ArteVideo
__all__ = ['ArteBrowser']
class ArteBrowser(BaseBrowser):
DOMAIN = u'videos.arte.tv'
ENCODING = None
PAGES = {r'http://videos.arte.tv/fr/videos/arte7.*': IndexPage,
r'http://videos.arte.tv/fr/do_search/videos/recherche.*': IndexPage,
r'http://videos.arte.tv/fr/videos/.*\.html': VideoPage
}
@id2url(ArteVideo.id2url)
def get_video(self, url, video=None):
self.location(url)
return self.page.get_video(video)
def home(self):
self.location('http://videos.arte.tv/fr/videos/arte7')
def iter_search_results(self, pattern):
if not pattern:
self.home()
else:
self.location(self.buildurl('/fr/do_search/videos/recherche', q=pattern))
assert self.is_on_page(IndexPage)
return self.page.iter_videos()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/arte/pages.py 0000664 0000000 0000000 00000003751 11430720725 0026530 0 ustar 00root root 0000000 0000000 # -*- 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 re
from weboob.tools.browser import BasePage
from weboob.tools.parsers.lxmlparser import select
from .video import ArteVideo
__all__ = ['IndexPage', 'VideoPage']
class IndexPage(BasePage):
def iter_videos(self):
videos = self.document.getroot().cssselect("div[class=video]")
for div in videos:
title = div.find('h2').find('a').text
m = re.match(r'/fr/videos/(.*)\.html', div.find('h2').find('a').attrib['href'])
_id = ''
if m:
_id = m.group(1)
rating = rating_max = 0
rates = select(div, 'div[class=rateContainer]', 1)
for r in rates.findall('div'):
if 'star-rating-on' in r.attrib['class']:
rating += 1
rating_max += 1
thumb = select(div, 'img[class=thumbnail]', 1)
thumbnail_url = 'http://videos.arte.tv' + thumb.attrib['src']
yield ArteVideo(_id,
title=title,
rating=rating,
rating_max=rating_max,
thumbnail_url=thumbnail_url)
class VideoPage(BasePage):
VIDEO_SIGNATURE_REGEX = re.compile(r'&t=([^ ,&]*)')
def get_video(self, video=None):
return video
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/arte/video.py 0000664 0000000 0000000 00000001641 11430720725 0026533 0 ustar 00root root 0000000 0000000 # -*- 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 weboob.capabilities.video import BaseVideo
__all__ = ['ArteVideo']
class ArteVideo(BaseVideo):
@classmethod
def id2url(cls, _id):
return 'http://videos.arte.tv/fr/videos/%s.html' % _id
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/ 0000775 0000000 0000000 00000000000 11430720725 0024700 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/__init__.py 0000664 0000000 0000000 00000001415 11430720725 0027012 0 ustar 00root root 0000000 0000000 # -*- 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 .browser import AuMBrowser
from .backend import AuMBackend
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/backend.py 0000664 0000000 0000000 00000026600 11430720725 0026645 0 ustar 00root root 0000000 0000000 # -*- 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 datetime import datetime
from dateutil import tz
from logging import warning
from time import sleep
from weboob.capabilities.chat import ICapChat
from weboob.capabilities.messages import ICapMessages, ICapMessagesReply, Message
from weboob.capabilities.dating import ICapDating, StatusField
from weboob.capabilities.contact import ICapContact, Contact, ProfileNode
from weboob.tools.backend import BaseBackend
from weboob.tools.browser import BrowserUnavailable
from .browser import AuMBrowser
from .exceptions import AdopteWait
from .optim.profiles_walker import ProfilesWalker
from .optim.visibility import Visibility
__all__ = ['AuMBackend']
class AuMBackend(BaseBackend, ICapMessages, ICapMessagesReply, ICapDating, ICapChat, ICapContact):
NAME = 'aum'
MAINTAINER = 'Romain Bignon'
EMAIL = 'romain@peerfuse.org'
VERSION = '0.1'
LICENSE = 'GPLv3'
DESCRIPTION = u"“Adopte un mec” french dating website"
CONFIG = {'username': BaseBackend.ConfigField(description='Username on website'),
'password': BaseBackend.ConfigField(description='Password of account', is_masked=True),
'register': BaseBackend.ConfigField(default=False, description='Register as new account?'),
}
STORAGE = {'profiles_walker': {'viewed': []},
'sluts': {},
}
BROWSER = AuMBrowser
def create_default_browser(self):
if self.config['register']:
browser = self.create_browser(self.config['username'])
browser.register(password= self.config['password'],
sex= 0,
birthday_d= 1,
birthday_m= 1,
birthday_y= 1970,
zipcode= 75001,
country= 'fr',
godfather= '')
else:
return self.create_browser(self.config['username'], self.config['password'])
def get_status(self):
with self.browser:
try:
return (
StatusField('myname', 'My name', self.browser.get_my_name()),
StatusField('score', 'Score', self.browser.score()),
StatusField('avcharms', 'Available charms', self.browser.nb_available_charms()),
)
except AdopteWait:
return (StatusField('notice', '', u'
You are currently waiting 1am to be able to connect with this account
', StatusField.FIELD_HTML|StatusField.FIELD_TEXT))
def iter_messages(self, thread=None):
for message in self._iter_messages(thread, False):
yield message
def iter_new_messages(self, thread=None):
for message in self._iter_messages(thread, True):
yield message
def _get_slut(self, id):
if not id in self.storage.get('sluts'):
slut = {'lastmsg': datetime(1970,1,1),
'msgstatus': ''}
else:
slut = self.storage.get('sluts', id)
slut['lastmsg'] = slut['lastmsg'].replace(tzinfo=tz.tzutc())
return slut
def _iter_messages(self, thread, only_new):
with self.browser:
try:
profiles = {}
if thread:
slut = self._get_slut(int(thread))
for mail in self._iter_thread_messages(thread, only_new, slut['lastmsg'], {}):
if slut['lastmsg'] < mail.get_date():
slut['lastmsg'] = mail.get_date()
yield mail
self.storage.set('sluts', int(thread), slut)
self.storage.save()
else:
contacts = self.browser.get_threads_list()
for contact in contacts:
slut = self._get_slut(contact.get_id())
last_msg = slut['lastmsg']
if only_new and contact.get_lastmsg_date() < last_msg and contact.get_status() == slut['msgstatus'] or \
not thread is None and int(thread) != contact.get_id():
continue
for mail in self._iter_thread_messages(contact.get_id(), only_new, last_msg, profiles):
if last_msg < mail.get_date():
last_msg = mail.get_date()
yield mail
slut['lastmsg'] = last_msg
slut['msgstatus'] = contact.get_status()
self.storage.set('sluts', contact.get_id(), slut)
self.storage.save()
# Send mail when someone added me in her basket.
# XXX possibly race condition if a slut adds me in her basket
# between the aum.nbNewBaskets() and aum.getBaskets().
new_baskets = self.browser.nb_new_baskets()
if new_baskets:
ids = self.browser.get_baskets()
while new_baskets > 0 and len(ids) > new_baskets:
new_baskets -= 1
profile = self.browser.get_profile(ids[new_baskets])
yield Message(profile.get_id(), 1,
title='Basket of %s' % profile.get_name(),
sender=profile.get_name(),
content='You are taken in her basket!',
signature=profile.get_profile_text())
except BrowserUnavailable:
pass
def _iter_thread_messages(self, id, only_new, last_msg, profiles):
mails = self.browser.get_thread_mails(id)
for mail in mails:
if only_new and mail.get_date() <= last_msg:
continue
if not mail.profile_link in profiles:
profiles[mail.profile_link] = self.browser.get_profile(mail.profile_link)
mail.signature += u'\n%s' % profiles[mail.profile_link].get_profile_text()
yield mail
def post_reply(self, thread_id, reply_id, title, message):
while 1:
try:
with self.browser:
self.browser.post_mail(thread_id, message)
except AdopteWait:
# If we are on a waiting state, retry every 30 minutes until it is posted.
sleep(60*30)
else:
return
def get_contact(self, contact):
try:
with self.browser:
if isinstance(contact, Contact):
_id = contact.id
elif isinstance(contact, (int,long,str,unicode)):
_id = contact
else:
raise TypeError("The parameter 'contact' isn't a contact nor a int/long/str/unicode: %s" % contact)
profile = self.browser.get_profile(_id)
if profile.is_online():
s = Contact.STATUS_ONLINE
else:
s = Contact.STATUS_OFFLINE
if isinstance(contact, Contact):
contact.id = _id
contact.name = profile.get_name()
contact.status = s
else:
contact = Contact(_id, profile.get_name(), s)
contact.status_msg = u'%s old' % profile.table['details']['old']
contact.summary = profile.description
contact.avatar = None
for photo in profile.photos:
contact.set_photo(photo.split('/')[-1], url=photo, thumbnail_url=photo.replace('image', 'thumb1_'))
contact.profile = []
stats = ProfileNode('stats', 'Stats', [], flags=ProfileNode.HEAD|ProfileNode.SECTION)
for label, value in profile.get_stats().iteritems():
stats.value.append(ProfileNode(label, label.capitalize(), value))
contact.profile.append(stats)
for section, d in profile.get_table().iteritems():
s = ProfileNode(section, section.capitalize(), [], flags=ProfileNode.SECTION)
for key, value in d.iteritems():
s.value.append(ProfileNode(key, key.capitalize(), value))
contact.profile.append(s)
return contact
except BrowserUnavailable:
return None
def init_optimizations(self):
self.OPTIM_PROFILE_WALKER = ProfilesWalker(self.weboob.scheduler, self.storage, self.browser)
self.OPTIM_VISIBILITY = Visibility(self.weboob.scheduler, self.browser)
def iter_contacts(self, status=Contact.STATUS_ALL, ids=None):
with self.browser:
for contact in self.browser.iter_contacts():
s = 0
if contact['cat'] == 1:
s = Contact.STATUS_ONLINE
elif contact['cat'] == 3:
s = Contact.STATUS_OFFLINE
elif contact['cat'] == 2:
s = Contact.STATUS_AWAY
else:
warning('Unknown AuM contact status: %s' % contact['cat'])
if not status & s or ids and contact['id'] in ids:
continue
# TODO age in contact['birthday']
c = Contact(contact['id'], contact['pseudo'], s)
c.status_msg = u'%s old' % contact['birthday']
c.set_photo(contact['cover'].split('/')[-1].replace('thumb0_', 'image'), thumbnail_url=contact['cover'])
yield c
def iter_chat_messages(self, _id=None):
with self.browser:
return self.browser.iter_chat_messages(_id)
def send_chat_message(self, _id, message):
with self.browser:
return self.browser.send_chat_message(_id, message)
#def start_chat_polling(self):
#self._profile_walker = ProfilesWalker(self.weboob.scheduler, self.storage, self.browser)
def fill_contact(self, contact, fields):
if 'profile' in fields:
contact = self.get_contact(contact)
if 'photos' in fields:
for name, photo in contact.photos.iteritems():
with self.browser:
if photo.url:
data = self.browser.openurl(photo.url).read()
contact.set_photo(name, data=data)
if photo.thumbnail_url:
data = self.browser.openurl(photo.thumbnail_url).read()
contact.set_photo(name, thumbnail_data=data)
OBJECTS = {Contact: fill_contact}
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/browser.py 0000664 0000000 0000000 00000030071 11430720725 0026736 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2008-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 datetime
import time
from logging import warning
import random
import simplejson
import urllib
from weboob.tools.browser import BaseBrowser, BrowserUnavailable
from weboob.tools.parsers.html5libparser import Html5libParser
from weboob.backends.aum.exceptions import AdopteWait
from weboob.backends.aum.pages.home import HomePage
from weboob.backends.aum.pages.contact_list import ContactListPage
from weboob.backends.aum.pages.contact_thread import ContactThreadPage
from weboob.backends.aum.pages.baskets import BasketsPage
from weboob.backends.aum.pages.profile import ProfilePage
from weboob.backends.aum.pages.search import SearchPage
from weboob.backends.aum.pages.login import LoginPage, RedirectPage, BanPage, ErrPage, RegisterPage, RegisterWaitPage, RegisterConfirmPage, ShopPage
from weboob.backends.aum.pages.edit import EditPhotoPage, EditPhotoCbPage, EditAnnouncePage, EditDescriptionPage, EditSexPage, EditPersonalityPage
from weboob.backends.aum.pages.wait import WaitPage
from weboob.capabilities.chat import ChatException, ChatMessage
__all__ = ['AuMBrowser']
class AuMBrowser(BaseBrowser):
DOMAIN = 'www.adopteunmec.com'
ENCODING = 'iso-8859-1'
PAGES = {'http://www.adopteunmec.com/': LoginPage,
'http://www.adopteunmec.com/index.html': LoginPage,
'http://www.adopteunmec.com/index.php': LoginPage,
'http://www.adopteunmec.com/loginErr.php.*': ErrPage,
'http://www.adopteunmec.com/bans.php.*': BanPage,
'http://www.adopteunmec.com/redirect.php\?action=login': RedirectPage,
'http://www.adopteunmec.com/wait.php': WaitPage,
'http://www.adopteunmec.com/register2.php': RegisterPage,
'http://www.adopteunmec.com/register3.php.*': RegisterWaitPage,
'http://www.adopteunmec.com/register4.php.*': RegisterConfirmPage,
'http://www.adopteunmec.com/home.php': HomePage,
'http://www.adopteunmec.com/shop2c.php': ShopPage,
'http://www.adopteunmec.com/mails.php': ContactListPage,
'http://www.adopteunmec.com/mails.php\?type=1': BasketsPage,
'http://www.adopteunmec.com/thread.php\?id=([0-9]+)(&see=all)?': ContactThreadPage,
'http://www.adopteunmec.com/edit.php\?type=1': EditPhotoPage,
'http://s\d+.adopteunmec.com/upload\d.php\?.*': EditPhotoCbPage,
'http://www.adopteunmec.com/edit.php\?type=2': EditAnnouncePage,
'http://www.adopteunmec.com/edit.php\?type=3': EditDescriptionPage,
'http://www.adopteunmec.com/edit.php\?type=4': EditSexPage,
'http://www.adopteunmec.com/edit.php\?type=5': EditPersonalityPage,
'http://www.adopteunmec.com/search.php.*': SearchPage,
'http://www.adopteunmec.com/searchRes.php.*': SearchPage,
'http://www.adopteunmec.com/rencontres-femmes/(.*)/([0-9]+)': ProfilePage,
'http://www.adopteunmec.com/catalogue-hommes/(.*)/([0-9]+)': ProfilePage,
'http://www.adopteunmec.com/view2.php': ProfilePage, # my own profile
'http://www.adopteunmec.com/(\w+)': ProfilePage, # a custom profile url
}
def __init__(self, *args, **kwargs):
kwargs['parser'] = Html5libParser(api='dom')
BaseBrowser.__init__(self, *args, **kwargs)
self.my_id = 0
def login(self):
if not self.is_on_page(LoginPage):
self.home()
self.page.login(self.username, self.password)
def is_logged(self):
return not self.is_on_page(LoginPage)
def home(self):
return self.location('http://www.adopteunmec.com/home.php')
def pageaccess(func):
def inner(self, *args, **kwargs):
if self.is_on_page(WaitPage):
if not self.page.check():
raise AdopteWait(u'Could not connect between 6pm and 1am.')
self.home()
if not self.page or self.is_on_page(LoginPage) and self.password:
self.home()
return func(self, *args, **kwargs)
return inner
def register(self, password, sex, birthday_d, birthday_m, birthday_y, zipcode, country, godfather=''):
if not self.is_on_page(RegisterPage):
self.location('http://www.adopteunmec.com/register2.php')
return self.page.register(password, sex, birthday_d, birthday_m, birthday_y, zipcode, country, godfather)
@pageaccess
def add_photo(self, name, f):
if not self.is_on_page(EditPhotoPage):
self.location('/edit.php?type=1')
return self.page.add_photo(name, f)
@pageaccess
def set_nickname(self, nickname):
if not self.is_on_page(EditAnnouncePage):
self.location('/edit.php?type=2')
return self.page.set_nickname(nickname)
@pageaccess
def set_announce(self, title=None, description=None, lookingfor=None):
if not self.is_on_page(EditAnnouncePage):
self.location('/edit.php?type=2')
return self.page.set_announce(title, description, lookingfor)
@pageaccess
def set_description(self, **args):
if not self.is_on_page(EditDescriptionPage):
self.location('/edit.php?type=3')
return self.page.set_description(**args)
@pageaccess
def score(self):
if time.time() - self.last_update > 60:
self.home()
return self.page.score()
@pageaccess
def get_my_name(self):
if time.time() - self.last_update > 60:
self.home()
return self.page.get_my_name()
@pageaccess
def get_my_id(self):
if self.my_id:
return self.my_id
if not self.is_on_page(HomePage):
self.home()
self.my_id = self.page.get_my_id()
return self.my_id
@pageaccess
def nb_new_mails(self):
if time.time() - self.last_update > 60:
self.home()
return self.page.nb_new_mails()
@pageaccess
def nb_new_baskets(self):
if time.time() - self.last_update > 60:
self.home()
return self.page.nb_new_baskets()
@pageaccess
def nb_new_visites(self):
if time.time() - self.last_update > 60:
self.home()
return self.page.nb_new_visites()
@pageaccess
def nb_available_charms(self, reload=False):
if reload or not self.is_on_page(HomePage):
self.home()
return self.page.nb_available_charms()
@pageaccess
def get_baskets(self):
self.location('/mails.php?type=1')
return self.page.get_profiles_ids_list()
@pageaccess
def flush_visits(self):
""" Does nothing, only flush new visits to increase my score """
self.openurl('/mails.php?type=3')
@pageaccess
def get_threads_list(self):
if not self.is_on_page(ContactListPage):
self.location('/mails.php')
return self.page.get_contact_list()
@pageaccess
def get_thread_mails(self, id):
if not self.is_on_page(ContactThreadPage) or self.page.get_id() != int(id):
self.page.open_thread_page(id)
return self.page.get_mails()
@pageaccess
def post_mail(self, id, content):
if not self.is_on_page(ContactThreadPage) or self.page.get_id() != int(id):
self.page.open_thread_page(id)
self.page.post(content)
@pageaccess
def send_charm(self, id):
result = self.openurl('http://www.adopteunmec.com/fajax_addBasket.php?id=%s' % id).read()
warning('Charm: %s' % result)
return result.find('noMoreFlashes') < 0
@pageaccess
def add_basket(self, id):
result = self.openurl('http://www.adopteunmec.com/fajax_addBasket.php?id=%s' % id).read()
warning('Basket: %s' % result)
# TODO check if it works (but it should)
return True
def deblock(self, id):
result = self.openurl('http://www.adopteunmec.com/fajax_postMessage.php?action=deblock&to=%s' % id).read()
warning('Deblock: %s' % result)
return True
@pageaccess
def rate(self, id, what, rating):
print 'rate "%s"' % id, what, rating
result = self.openurl('http://www.adopteunmec.com/fajax_vote.php', 'member=%s&what=%s&rating=%s' % (id, what, rating)).read()
print result
return float(result)
@pageaccess
def search_profiles(self, **kwargs):
self.location('/search.php?display=1')
self.page.search(**kwargs)
return self.page.get_profiles_ids()
@pageaccess
def get_profile(self, link):
if isinstance(link, (str,unicode)) and link.startswith('/'):
link = link[1:]
self.location('/%s' % link)
return self.page
@pageaccess
def get_slut_state(self, id):
result = self.openurl('http://www.adopteunmec.com/%s' % id).read()
if result.find('
en ligne
') >= 0:
r = 'online'
elif result.find('Cet utilisateur a quitt\xe9 le site ') >= 0:
r = 'removed'
elif result.find('ce profil a \xe9t\xe9 bloqu\xe9 par l\'\xe9quipe de mod\xe9ration ') >= 0:
r = 'removed'
elif result.find('
Cette personne vous a bloqu\xe9
') >= 0:
r = 'blocked'
else:
r = 'offline'
print 'getSlutState(%s) = %s' % (id, r)
return r
@pageaccess
def is_slut_online(self, id):
result = self.openurl('http://www.adopteunmec.com/%s' % id).read()
r = result.find('
en ligne
') >= 0
print 'isSlutOnline(%s) = %s' % (id, r)
return r
def _get_chat_infos(self):
try:
json = simplejson.load(self.openurl('http://www.adopteunmec.com/1.1_cht_get.php?anticache=%f' % random.random()))
except simplejson.JSONDecodeError:
raise BrowserUnavailable()
if json['error']:
raise ChatException(u'Error while getting chat infos. json:\n%s' % json)
return json
def iter_contacts(self):
def iter_dedupe(contacts):
yielded_ids = set()
for contact in contacts:
if contact['id'] not in yielded_ids:
yield contact
yielded_ids.add(contact['id'])
json = self._get_chat_infos()
return iter_dedupe(json['contacts'])
def iter_chat_messages(self, _id=None):
json = self._get_chat_infos()
if json['messages'] is not None:
for message in json['messages']:
yield ChatMessage(id_from=message['id_from'], id_to=message['id_to'], message=message['message'], date=message['date'])
def send_chat_message(self, _id, message):
url = 'http://www.adopteunmec.com/1.1_cht_send.php?anticache=%f' % random.random()
data = dict(id=_id, message=message)
headers = {
'Content-type': 'application/x-www-form-urlencoded',
'Accept': 'text/plain',
'Referer': 'http://www.adopteunmec.com/chat.php',
'Origin': 'http://www.adopteunmec.com',
}
request = self.request_class(url, urllib.urlencode(data), headers)
response = self.openurl(request).read()
try:
datetime.datetime.strptime(response, '%Y-%m-%d %H:%M:%S')
return True
except ValueError:
return False
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/captcha.py 0000664 0000000 0000000 00000014536 11430720725 0026666 0 ustar 00root root 0000000 0000000 # -*- 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 hashlib
import sys
import Image
class CaptchaError(Exception): pass
class Tile:
hash = {
'bc8d52d96058478a6def26226145d53b': 'A',
'c62ecdfddb72b2feaed96cd9fe7c2802': 'A',
'8b61cda8a3240d8fa5f2610424271300': 'AD',
'f5dc63d37c7ea3375d86180f0ae62d05': 'AE',
'fd562be230da7f767f4454148632201d': 'AF',
'1860de576d8b0d1d87edc9dcb0b2a64c': 'AG',
'53afa108d36186e6bd23631711ec3d8c': 'AJ',
'6f2f9a1082a9230272c45117320f159d': 'AL',
'e14249a774d24bacc6e2bcadd7f3df65': 'AM',
'389330dbf3d85dea2dc40c6f9cf77d52': 'AN',
'17526a3c2261b55f9cd237c4aa195099': 'AQ',
'7e4820a9cc6c83a9fa60ff73ceb52157': 'AW',
'90690d1209753a2bcfeafa890082a585': 'B',
'2cf22e9ceace03a5f8ed3999e92d877e': 'C',
'a1d0bf1a29600a82a6aa2b8b21651b0f': 'D',
'9bb6909d647a0be3b2e7352d37374228': 'E',
'38120c8346f16cd07a9194283787ee5e': 'F',
'd41ff948fbc50a628c858b8e3e9e931c': 'G',
'4cc9322d3361eb3f9fea7fc83579e40f': 'H',
'837cd0f04e2d47ca6975745bdd0da640': 'I',
'da0204fa51b38414051376cc1c27ba72': 'J',
'199b1a9f9e1df1c2eddadcc4582957d7': 'JW',
'5e8d3d5bd5f683d84b089f2cecc1e196': 'JX',
'bc1fcf3546057d40d2db5454caacb3a5': 'JZ',
'c2f5866ba3bf799ece8b202492d199bf': 'K',
'7abe4091e11921afe6dac16509999010': 'KT',
'281ef08e623184e5621a73b9ccec7c9a': 'KX',
'b28e3fc06411de2ac7f53569bc3b42db': 'L',
'd58a6c26649926f1145fb4b7b42d0554': 'LT',
'4add630c6d124899fef814211975e344': 'M',
'9740cefe1629d6bc149a72d5f2a4586d': 'N',
'396f816f7e78e5c98de6404f8c4bd2ee': 'O',
'31ae7c9536b6c6a96e30a77b70e4b2fd': 'P',
'98ad9b1c32c05e6efc06637a166e4c42': 'PA',
'a05cce33683025fb2c6708ee06f6028e': 'Q',
'2852f51e8939bf9664fe064f7dacf310': 'R',
'3798513fe87e786faa67552a140fd86f': 'S',
'350b13811e34eeb63e3d7fb4b5eade5b': 'T',
'a01b186cbc767e17d948ed04eff114a1': 'U',
'8405f4d80ce80c4e6e9680fcfac4fe40': 'V',
'17ed80e9cb9a585098ae6a55d8d1f5c0': 'W',
'ae54ca77be5561330781a08dfbaff7a7': 'W',
'bbded6a2ba5f521bba276bb843bf4c98': 'WXT',
'ea662dd25fc528b84b832ce71ae3de61': 'WZ',
'4eb23916138e7c01714431dbecfe8b96': 'X',
'c02093d35d852339ff34f2b26873bf5a': 'XW',
'65744e0c6ce0c56d04873dfd732533a7': 'Y',
'315fb7dba7032004bd362cf0bb076733': 'YA',
'ce12a68a4f15657bc5297a6cf698bc0a': 'YAQ',
'275478ea2280351f7433a0606f962175': 'Z',
}
def __init__(self):
self.map = []
def append(self, pxls):
self.map.append(pxls)
def display(self):
print '-' * (len(self.map) * 2 + 2)
for y in xrange(len(self.map[0])):
sys.stdout.write('|')
for x in xrange(len(self.map)):
sys.stdout.write('%s' % ('XX' if self.map[x][y] else ' '))
print '|'
print '-' * (len(self.map) * 2 + 2)
def checksum(self):
s = ''
for pxls in self.map:
for pxl in pxls:
s += '%d' % (1 if pxl else 0)
return hashlib.md5(s).hexdigest()
@property
def letter(self):
try:
return self.hash[self.checksum()]
except KeyError:
raise CaptchaError()
class Captcha:
def __init__(self, f):
self.img = Image.open(f)
self.w, self.h = self.img.size
self.map = self.img.load()
self.tiles = []
tile = None
for x in xrange(self.w):
blank = True
pxls = []
for y in xrange(self.h):
pxls.append(self[x,y])
if self[x,y] != 0:
blank = False
if tile:
if blank:
tile = None
else:
tile.append(pxls)
elif not blank:
tile = Tile()
tile.append(pxls)
self.tiles.append(tile)
def __getitem__(self, (x, y)):
return self.map[x % self.w, y % self.h]
def __iter__(self):
for tile in self.tiles:
yield tile
@property
def text(self):
s = ''
for tile in self.tiles:
s += tile.letter
return s
class Decoder:
def __init__(self):
self.hash = {}
def process(self):
from weboob.backends.aum.browser import AuMBrowser
browser = AuMBrowser('')
browser.openurl('/register2.php')
c = Captcha(browser.openurl('/captcha.php'))
for tile in c:
checksum = tile.checksum()
if checksum in self.hash:
print 'Skipping %s' % self.hash[checksum]
continue
tile.display()
print 'Checksum: %s' % checksum
ntry = 2
while ntry:
sys.stdout.write('Enter the letter: ')
l = sys.stdin.readline().strip()
ntry -= 1
if len(l) != 1:
print 'Error: please enter only one letter'
elif l in self.hash.itervalues():
print 'Warning! This letter has already been catched!'
else:
ntry = 0
self.hash[checksum] = l
def main(self):
try:
while 1:
self.process()
except KeyboardInterrupt:
print ''
print 'hash = {'
l = sorted(self.hash.iteritems(), key=lambda (k,v): (v,k))
for hash, value in l:
print ' \'%s\': %s' % (hash, value)
print '}'
if __name__ == '__main__':
d = Decoder()
d.main()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/exceptions.py 0000664 0000000 0000000 00000001627 11430720725 0027441 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2008-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.tools.browser import BrowserUnavailable
class AdopteWait(BrowserUnavailable):
pass
class AdopteBanned(BrowserUnavailable):
pass
class AdopteCantPostMail(Exception):
pass
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/optim/ 0000775 0000000 0000000 00000000000 11430720725 0026030 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/optim/__init__.py 0000664 0000000 0000000 00000000000 11430720725 0030127 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/optim/profiles_walker.py 0000664 0000000 0000000 00000006365 11430720725 0031604 0 ustar 00root root 0000000 0000000 # -*- 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.
from __future__ import with_statement
from logging import info
from random import randint
from weboob.tools.browser import BrowserUnavailable
from weboob.capabilities.dating import Optimization
__all__ = ['ProfilesWalker']
class ProfilesWalker(Optimization):
def __init__(self, sched, storage, browser):
self.sched = sched
self.storage = storage
self.browser = browser
self.visited_profiles = set(storage.get('profiles_walker', 'viewed'))
info(u'Loaded %d already visited profiles from storage.' % len(self.visited_profiles))
self.profiles_queue = set()
def save(self):
self.storage.set('profiles_walker', 'viewed', list(self.visited_profiles))
self.storage.save()
def start(self):
self.walk_cron = self.sched.repeat(60, self.enqueue_profiles)
self.view_cron = self.sched.schedule(randint(10,40), self.view_profile)
return True
def stop(self):
# TODO
# self.event.cancel(self.event)
# self.event = None
return False
def enqueue_profiles(self):
try:
with self.browser:
profiles_to_visit = self.browser.search_profiles().difference(self.visited_profiles)
info(u'Enqueuing profiles to visit: %s' % profiles_to_visit)
self.profiles_queue.update(profiles_to_visit)
self.save()
except BrowserUnavailable:
return
def view_profile(self):
try:
try:
id = self.profiles_queue.pop()
except KeyError:
return # empty queue
try:
with self.browser:
profile = self.browser.get_profile(id)
info(u'Visited profile %s (%s)' % (profile.get_name(), id))
# Get score from the aum_score module
#d = self.nucentral_core.callService(context.Context.fromComponent(self), 'aum_score', 'score', profile)
# d.addCallback(self.score_cb, profile.getID())
# deferredlist.append(d)
# do not forget that we visited this profile, to avoid re-visiting it.
self.visited_profiles.add(id)
self.save()
except BrowserUnavailable:
# We consider this profil hasn't been [correctly] analysed
self.profiles_queue.add(id)
return
except Exception, e:
print e
finally:
self.sched.schedule(randint(10,40), self.view_profile)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/optim/visibility.py 0000664 0000000 0000000 00000002633 11430720725 0030575 0 ustar 00root root 0000000 0000000 # -*- 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.tools.browser import BrowserUnavailable
from weboob.capabilities.dating import Optimization
from ..browser import AuMBrowser
__all__ = ['Visibility']
class Visibility(Optimization):
def __init__(self, sched, browser):
self.sched = sched
self.browser = browser
def start(self):
self.cron = self.sched.repeat(60*5, self.reconnect)
return True
def stop(self):
# TODO
return False
def reconnect(self):
try:
AuMBrowser(self.browser.username,
self.browser.password,
proxy=self.browser.proxy)
except BrowserUnavailable, e:
print str(e)
pass
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/pages/ 0000775 0000000 0000000 00000000000 11430720725 0025777 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/pages/__init__.py 0000664 0000000 0000000 00000000000 11430720725 0030076 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/pages/base.py 0000664 0000000 0000000 00000007160 11430720725 0027267 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2008-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 re
from logging import error, warning
from weboob.tools.browser import BasePage, BrowserUnavailable
class PageBase(BasePage):
def __init__(self, *args, **kwargs):
BasePage.__init__(self, *args, **kwargs)
# Check the 'oops' error message when adopteunmec guys are gay.
b = self.document.getElementsByTagName('body')[0]
for div in b.getElementsByTagName('div'):
if div.getAttribute('id') == 'oops':
raise BrowserUnavailable()
def open_contact_list_page(self):
self.browser.follow_link(url_regex=r"/mails.php$")
def open_thread_page(self, id, all_messages=False):
if all_messages:
self.browser.location('/thread.php?id=%d&see=all' % int(id))
else:
self.browser.location('/thread.php?id=%d' % int(id))
def score(self):
"""
popularité
7.230 pts
"""
l = self.document.getElementsByTagName('table')
for tag in l:
if tag.getAttribute('width') == '220':
#
child = tag.childNodes[0].childNodes[0].childNodes[3]
return int(child.childNodes[0].childNodes[1].data.replace(' ', '').strip())
error("Error: I can't find the score :(")
return '0'
def __get_indicator(self, elementName):
""" """
l = self.document.getElementsByTagName('span')
for tag in l:
if tag.getAttribute('id') == elementName:
child = tag.childNodes[0]
if not hasattr(child, 'data'):
if child.tagName != u'blink':
warning("Warning: %s counter isn't a blink and hasn't data" % elementName)
child = child.childNodes[0]
if not hasattr(child, 'data'):
break
return int(child.data)
error("Error: I can't find the %s counter :(" % elementName)
return 0
MYNAME_REGEXP = re.compile("Bonjour (.*)")
def get_my_name(self):
""" Bonjour Romain """
tags = self.document.getElementsByTagName('span')
for tag in tags:
if hasattr(tag.firstChild, 'data'):
m = self.MYNAME_REGEXP.match(tag.firstChild.data)
if m:
return m.group(1)
warning('Warning: Unable to fetch name')
return '?'
def nb_new_mails(self):
return self.__get_indicator(u'mailsCounter')
def nb_new_baskets(self):
return self.__get_indicator(u'flashsCounter')
def nb_new_visites(self):
return self.__get_indicator(u'visitesCounter')
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/pages/baskets.py 0000664 0000000 0000000 00000001507 11430720725 0030010 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2008 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.backends.aum.pages.profileslist_base import ProfilesListBase
class BasketsPage(ProfilesListBase):
pass
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/pages/contact_list.py 0000664 0000000 0000000 00000011736 11430720725 0031047 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2008-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 re
from datetime import datetime, timedelta
from dateutil import tz
from logging import warning
from weboob.backends.aum.pages.base import PageBase
class ContactItem:
u"""
Hen
19ans, Montreuil
Comme ça, on est deux.
il y a 1 heure
nouveau
"""
fields = ['thread_link', 'photo', 'useless3', 'name', 'resume', 'status', 'useless', 'remove', 'useless2']
def __init__(self, tr):
self.tr = tr
def __get_element(self, id):
return self.tr.getElementsByTagName('td')[self.fields.index(id)]
def get_name(self):
tag = self.__get_element('name')
node = tag.getElementsByTagName('b')[0].firstChild
if node:
name = node.data
else:
# it is possible if the user has left site and hasn't any nickname
name = ''
return name
def get_status(self):
tag = self.__get_element('status')
return tag.firstChild.data
def is_new(self):
return self.get_status() == u'nouveau'
def is_answered(self):
return self.get_status() == u'répondu'
def get_resume(self):
tag = self.__get_element('resume')
return tag.getElementsByTagName('b')[0].firstChild.data.strip()
LASTMSG_RE = re.compile('il y a (\d+) (\w+)')
def get_lastmsg_date(self):
tag = self.__get_element('resume')
s = tag.childNodes[3].data
m = self.LASTMSG_RE.match(s)
if m:
d = {'secondes': 1,
'seconde': 1,
'minutes': 60,
'minute': 60,
'heures': 3600,
'heure': 3600,
'jours': 24*3600,
'jour': 24*3600,
}
try:
i = int(m.group(1)) * d[m.group(2)]
except KeyError:
warning('Unable to parse lastmsg ("%s" is not a valid unit)' % m.group(2))
return None
else:
return datetime.now(tz=tz.tzutc()) - timedelta(seconds=i)
else:
warning('Unable to parse lastmsg [%s]' % s)
return None
def get_id(self):
tag = self.__get_element('thread_link')
text = tag.getAttribute('onclick')
regexp = re.compile("window.location='/thread.php\?id=(.*)'")
m = regexp.match(text)
if m:
return int(m.group(1))
return 0
class ContactListPage(PageBase):
def on_loaded(self):
self.items = []
tags = self.document.getElementsByTagName('form')[0].childNodes[3].childNodes[1].childNodes
for tag in tags:
if not hasattr(tag, 'tagName') or tag.tagName != u'tr':
continue
if tag.hasAttribute('bgcolor'):
continue
self.items += [ContactItem(tag)]
def get_contact_list(self):
return self.items
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/pages/contact_thread.py 0000664 0000000 0000000 00000034326 11430720725 0031343 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2008-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 re
from datetime import datetime
from dateutil import tz
from logging import error, warning
from mechanize import FormNotFoundError
from weboob.backends.aum.pages.base import PageBase
from weboob.backends.aum.exceptions import AdopteCantPostMail
from weboob.capabilities.messages import Message
class MailParser(Message):
"""
Romain
27 octobre 2008, 01:11:32, nouveau
Moi en g�n�ral j'aime sortir tout simplement dans des bars, pour discuter avec mes amis et/ou coll�gues, et rencontrer des gens. Sinon je fais de la guitare, et je d�veloppe des projets perso.
Message.__init__(self, id, 0, 'Discussion with %s' % name, name)
self.tr = tr.childNodes[0].childNodes[1].childNodes[0].childNodes[0]
tds = self.tr.childNodes
counter = 0
for td in tds:
if not hasattr(td, 'tagName') or td.tagName != u'td':
continue
counter += 1
if counter == 3:
date = u''
for child in td.childNodes[1].childNodes:
if hasattr(child, 'data'):
date += child.data
self.parse_date(date)
content = ''
for c in td.childNodes[3].childNodes:
if hasattr(c, 'data'):
content += ''.join(c.data.split('\n')) # to strip \n
elif hasattr(c, 'tagName'):
if c.tagName == 'br':
content += '\n'
elif c.tagName == 'img' and c.hasAttribute('src'):
m = self.SMILEY_REGEXP.match(c.getAttribute('src'))
if m and self.smileys.has_key(int(m.group(1))):
try:
content += self.smileys[int(m.group(1))]
except KeyError:
error('Mail: unable to find this smiley: %s' % c.getAttribute('src'))
self.content = content
break
self.parse_profile_link()
self.parse_from()
def set_reply_id(self, date):
self.reply_id = date
def parse_date(self, date_str):
# To match regexp, we have to remove any return chars in string
# before the status ('nouveau', 'lu', etc)
date_str = u''.join(date_str.split(u'\n'))
m = self.DATETIME_REGEXP.match(date_str)
if m:
day = int(m.group(1))
month = self.months.index(m.group(2))
year = int(m.group(3))
hour = int(m.group(4))
minute = int(m.group(5))
sec = int(m.group(6))
# build datetime object with local timezone
d = datetime(year, month, day, hour, minute, sec, tzinfo=tz.tzlocal())
# then, convert it to UTC timezone
d = d.astimezone(tz.tzutc())
# and get timestamp
self.date = d
self.id = self.get_date_int()
if m.group(7).find('nouveau') >= 0:
self.is_new = True
else:
error('Error: unable to parse the datetime string "%s"' % date_str)
def parse_profile_link(self):
tables = self.tr.getElementsByTagName('div')
for table in tables:
if table.hasAttribute('class') and table.getAttribute('class') == 'mini' and table.hasAttribute('onclick'):
text = table.getAttribute('onclick')
regexp = re.compile("window.location='(.*)'")
m = regexp.match(text)
if m:
self.profile_link = m.group(1)
self.signature = u'Profile: http://www.adopteunmec.com%s' % self.profile_link
return
warning('Unable to find the profile URL in the message %s@%s' % (self.get_from(), self.get_id()))
def parse_from(self):
tds = self.tr.getElementsByTagName('div')
for td in tds:
if not td.hasAttribute('class') or td.getAttribute('class') != 'mini_pseudo':
continue
if td.childNodes:
self.sender = td.childNodes[0].data
return
warning('Warning: unable to find from in the mail %s' % self.get_id())
class ContactThreadPage(PageBase):
"""
"""
def post(self, content):
if isinstance(content, unicode):
# LOL the data is Windows-1252 encoded, even if the charset label
# of the page is ISO-8859-1.
# That's crappy, but necessary if we want to use special chars.
content = content.encode('Windows-1252', 'replace')
if len(content) < 3:
raise AdopteCantPostMail("Your message is too short (minimum 3 chars)")
try:
self.browser.select_form(name="sendMsg")
self.browser['message'] = content
self.browser.submit() # submit current form
except FormNotFoundError, e:
error = 'Unknown error (%s)' % e
p_list = self.document.getElementsByTagName('p')
for p in p_list:
if p.hasAttribute('align') and p.getAttribute('align') == 'center':
error = p.firstChild.data
break
raise AdopteCantPostMail(error)
"""
"""
id_regexp = re.compile("/thread.php\?id=([0-9]+)")
def on_loaded(self):
self.items = []
a_list = self.document.getElementsByTagName('a')
self.id = 0
for a in a_list:
if a.hasAttribute('href'):
m = self.id_regexp.match(a.getAttribute('href'))
if m:
self.id = int(m.group(1))
break
self.name = ''
big_list = self.document.getElementsByTagName('big')
for big in big_list:
for child in big.childNodes:
if hasattr(child, 'tagName') and child.tagName == u'b':
self.name = child.childNodes[1].data.strip()
break
tables = self.document.getElementsByTagName('table')
table = None
for t in tables:
if t.hasAttribute('style') and t.getAttribute('style') == 'width:700px;background:black':
table = t
break
if not table:
# It is probably the 'sent' page
return
for tag in table.childNodes[1].childNodes[1:]:
if not hasattr(tag, 'tagName') or tag.tagName != u'tr':
continue
if not tag.hasAttribute('valign'):
continue
mail = MailParser(self.id, self.name, tag)
if self.items:
self.items[-1].set_reply_id(mail.get_date_int())
self.items += [mail]
def get_id(self):
return self.id
def get_mails(self):
return self.items
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/pages/edit.py 0000664 0000000 0000000 00000006714 11430720725 0027306 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2008-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.backends.aum.pages.base import PageBase
class EditPhotoPage(PageBase):
def add_photo(self, name, f):
self.browser.select_form(name="form")
self.browser.find_control('uploaded').add_file(f, 'image/jpeg', name)
self.browser.submit()
self.browser.openurl('http://www.adopteunmec.com/home.php')
class EditPhotoCbPage(PageBase):
# Do nothing
pass
class EditAnnouncePage(PageBase):
def set_nickname(self, nickname):
self.browser.select_form(name="form")
self.browser['pseudo'] = nickname
self.browser.submit()
def set_announce(self, **kwargs):
self.browser.select_form(name="form")
self.browser.set_field(kwargs, 'title')
self.browser.set_field(kwargs, 'description', field='about1')
self.browser.set_field(kwargs, 'lookingfor', field='about2')
self.browser.submit()
class EditDescriptionPage(PageBase):
SHAPES = ['--', 'svelte', 'sportive', u'équilibrée', 'pulpeuse', u'généreuse', 'normale']
HAIR_COLORS = ['--', 'blancs', 'gris', 'noirs', 'bruns', 'chatains', 'roux', 'blonds', 'platines', u'colorés']
HAIR_SIZES = ['--', u'rasés', 'courts', 'mi-longs', 'longs']
EYES = ['--', 'noirs', 'marrons', 'noisettes', 'bleus', 'verts', 'gris']
ORIGINS = ['--', u'européennes', 'afro', 'maghrebines', 'asiatiques', u'métisses', 'eurasiennes', 'latines']
STYLES = ['--', 'fashion', 'chic', 'sport', u'décontracté', 'rock', u'bohème', 'masculin', 'dark', 'excentrique', 'electro', 'skate']
FOODS = ['--', 'mange de tout', 'piscovore', u'végétarien', u'végétalien', 'bio']
DRINKS = ['--', 'jamais', 'de temps en temps', 'souvent', 'pilier de bar']
SMOKES = ['--', u'ne tolère pas la fumée', u'tolère la fumée', 'fume de temps en temps', 'fume souvent']
def set_description(self, **kwargs):
self.browser.select_form(name='form')
self.browser.set_field(kwargs, 'height', field='size', is_list=True)
self.browser.set_field(kwargs, 'weight', is_list=True)
self.browser.set_field(kwargs, 'shape', is_list=self.SHAPES)
self.browser.set_field(kwargs, 'hair_color', is_list=self.HAIR_COLORS)
self.browser.set_field(kwargs, 'hair_size', is_list=self.HAIR_SIZES)
self.browser.set_field(kwargs, 'eyes', is_list=self.EYES)
self.browser.set_field(kwargs, 'origins', is_list=self.ORIGINS)
self.browser.set_field(kwargs, 'style', is_list=self.STYLES)
self.browser.set_field(kwargs, 'food', is_list=self.FOODS)
self.browser.set_field(kwargs, 'drink', is_list=self.DRINKS)
self.browser.set_field(kwargs, 'smoke', is_list=self.SMOKES)
self.browser.submit()
class EditSexPage(PageBase):
pass
class EditPersonalityPage(PageBase):
pass
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/pages/home.py 0000664 0000000 0000000 00000003631 11430720725 0027304 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2008-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 re
from weboob.backends.aum.pages.base import PageBase
from logging import error, warning
class HomePage(PageBase):
MYID_REGEXP = re.compile("http://www.adopteunmec.com/\?mid=(\d+)")
def get_my_id(self):
fonts = self.document.getElementsByTagName('font')
for font in fonts:
m = self.MYID_REGEXP.match(font.firstChild.data)
if m:
return m.group(1)
error("Error: Unable to find my ID")
return 0
def nb_available_charms(self):
tables = self.document.getElementsByTagName('table')
for table in tables:
if table.hasAttribute('style') and table.getAttribute('style') == 'background-color:black;background-image:url(http://s.adopteunmec.com/img/barmec.gif);background-repeat:no-repeat':
fonts = table.getElementsByTagName('font')
i = 0
for font in fonts:
if font.hasAttribute('color') and font.getAttribute('color') == '#ff0198':
i += 1
if i == 3:
return int(font.firstChild.data)
warning(u'Could not parse number of charms available')
return 0
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/pages/login.py 0000664 0000000 0000000 00000006515 11430720725 0027470 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2008-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.tools.browser import BrowserIncorrectPassword
from .base import PageBase
from ..captcha import Captcha
class LoginPage(PageBase):
def login(self, login, password):
self.browser.select_form(name="form_login")
self.browser['login'] = login
self.browser['password'] = password
self.browser.submit() # submit current form
class RegisterPage(PageBase):
def register(self, password, sex, birthday_d, birthday_m, birthday_y, zipcode, country):
"""
Form name=register (#1)
## ## __Name__________________ __Type___ __ID________ __Value__________________
1 sent1time hidden (None)
2 sex radio sex-0 [] of ['0', '1']
3 birthday0 select birthday0 ['0'] of ['0', '1', '2', '3', '4', ' ...
4 birthday1 select birthday1 ['0'] of ['0', '1', '2', '3', '4', ' ...
5 birthday2 select birthday2 ['0'] of ['0', '1992', '1991', '1990 ...
6 country select country ['0'] of ['0', 'fr', 'be', 'ch', 'ca']
7 zip text zip
8 email text email
9 pass password pass
10 pass_retype password pass_retype
11 captcha text captcha
12 swear_adult checkbox swear_adult [] of ['on']
13 want_goods checkbox want_goods [] of ['on']
"""
c = Captcha(self.browser.openurl('/captcha.php'))
self.browser.select_form(name='register')
self.browser.set_all_readonly(False)
self.browser['sex'] = str(sex)
self.browser['birthday0'] = [str(birthday_d)]
self.browser['birthday1'] = [str(birthday_m)]
self.browser['birthday2'] = [str(birthday_y)]
self.browser['country'] = [str(country)]
self.browser['zip'] = str(zipcode)
self.browser['email'] = self.browser.username
self.browser['pass'] = password
self.browser['pass_retype'] = password
self.browser['captcha'] = c.text
self.browser['swear_adult'] = ['on']
self.browser['want_goods'] = []
self.browser.submit()
class RegisterWaitPage(PageBase):
pass
class RegisterConfirmPage(PageBase):
pass
class RedirectPage(PageBase):
def on_loaded(self):
for link in self.browser.links():
print link
self.browser.location('/wait.php')
class BanPage(PageBase):
pass
class ShopPage(PageBase):
pass
class ErrPage(PageBase):
def on_loaded(self):
raise BrowserIncorrectPassword('Incorrect login/password')
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/pages/profile.py 0000664 0000000 0000000 00000042433 11430720725 0030017 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2008-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.backends.aum.pages.base import PageBase
from copy import deepcopy
from logging import warning
import re
class FieldBase:
def __init__(self, key):
self.key = key
def put_value(self, d, value):
raise NotImplementedError
class FieldString(FieldBase):
def put_value(self, d, value):
d[self.key] = unicode(value)
class FieldList(FieldBase):
def put_value(self, d, value):
d[self.key] = value.split(', ')
class FieldWideList(FieldBase):
def put_value(self, d, value):
d[self.key] += [value]
class FieldOld(FieldBase):
regexp = re.compile(u'([0-9]+) ans( \(Née le ([0-9]+) ([^ ]+) ([0-9]+)\))?')
month2i = ['', 'janvier', u'février', 'mars', 'avril', 'mai', 'juin', 'juillet', u'août', 'septembre', 'octobre', 'novembre', u'décembre']
def put_value(self, d, value):
m = self.regexp.match(value)
if not m:
return
d[self.key] = int(m.group(1))
if not m.group(2):
return
try:
d['birthday'] = (int(m.group(3)),
self.month2i.index(m.group(4)),
int(m.group(5)))
except ValueError, e:
print str(e)
class FieldLocation(FieldBase):
location = re.compile('(.*) \(([0-9]{5})\), (.*)')
def __init__(self):
FieldBase.__init__(self, '')
def put_value(self, d, value):
# TODO: determine distance, or something like
m = self.location.match(value)
if m:
d['location'] = m.group(1)
d['zipcode'] = int(m.group(2))
d['country'] = m.group(3)
else:
warning('Unable to parse the location "%s"' % value)
d['location'] = unicode(value)
class FieldMeasurements(FieldBase):
height = re.compile('([0-9]{1,3}) cm')
weight = re.compile('([0-9]{1,3}) kg')
# TODO: parse third parameter
def __init__(self):
FieldBase.__init__(self, '')
def put_value(self, d, value):
for s in value.split(', '):
m = self.height.match(s)
if m:
d['height'] = int(m.group(1))
continue
m = self.weight.match(s)
if m:
d['weight'] = int(m.group(1))
continue
if d['height'] and d['weight']:
bmi = (d['weight']/float(pow(d['height']/100.0, 2)))
if bmi < 15.5:
d['fat'] = 'severely underweight'
elif bmi < 18.4:
d['fat'] = 'underweight'
elif bmi < 24.9:
d['fat'] = 'normal'
elif bmi < 30:
d['fat'] = 'overweight'
else:
d['fat'] = 'obese'
d['BMI'] = bmi
class FieldParticularSignes(FieldBase):
def __init__(self): FieldBase.__init__(self, '')
def put_value(self, d, value):
for s in value.split(', '):
if s.find('tatouages') >= 0:
d['tatoos'] = True
elif s.find('piercing') >= 0:
d['piercing'] = True
elif s.find('lunettes') >= 0:
d['glasses'] = True
elif s.find('rousseur') >= 0:
d['freckle'] = True
class ProfilePage(PageBase):
empty_table = {'details': {'old': 0,
'birthday': (0,0,0),
'zipcode': 0,
'location': '',
'country': '',
'eyes': '',
'hairs': [],
'height': 0,
'weight': 0,
'BMI': 0,
'fat': '',
'from': '',
'tatoos': False,
'piercing': False,
'freckle': False,
'glasses': False,
'job': '',
'style': '',
'alimentation': '',
'alcool': '',
'tabac': '',
},
'liking': {'activities': '',
'music': [],
'cinema': [],
'books': [],
'tv': [],
},
'sex': {'underwear': [],
'top': [],
'bottom': [],
'interval': '',
'favorite': [],
'practices': [],
'toys': [],
},
'personality': {'snap': '',
'exciting': '',
'hate': '',
'vices': '',
'assets': '',
'fantasies': '',
'is': [],
},
}
tables = {'tab_0': 'details',
'tab_1': 'liking',
'tab_2': 'sex',
'tab_3': 'personality'
}
fields = {'details': {'Age': FieldOld('old'),
u'Réside à': FieldLocation(),
'Yeux': FieldString('eyes'),
'Cheveux ': FieldList('hairs'),
'Mensurations ': FieldMeasurements(),
'Origines ': FieldString('from'),
'Signes particuliers ': FieldParticularSignes(),
'Style ': FieldString('style'),
'Profession ': FieldString('job'),
'Alimentation': FieldString('alimentation'),
'Alcool': FieldString('alcool'),
'Tabac': FieldString('tabac'),
},
'liking': {'Hobbies ': FieldString('activities'),
'Musique ': FieldWideList('music'),
u'Cinéma': FieldWideList('cinema'),
'Livres ': FieldWideList('books'),
u'Télé': FieldWideList('tv'),
},
'sex': {u'Sous-v\xeatements ': FieldList('underwear'),
'... en haut ': FieldList('top'),
'... en bas ': FieldList('bottom'),
u'Fréquence idéale des rapports sexuels ':
FieldString('interval'),
u'Position favorite ': FieldList('favorite'),
'Pratiques sexuelles ': FieldList('practices'),
u'Accessoires préférés ':FieldList('toys'),
u'Ce qui se cache en dessous ':
FieldList('underwear'),
u"Ce qui m'\xe9moustille ":
FieldList('favorite'),
u"Au lit j'aime ": FieldList('practices'),
u'Mes accessoires ': FieldList('toys'),
},
'personality': {u'Ça la fait craquer ': FieldString('snap'),
u'Ça l\'excite ': FieldString('exciting'),
u'Elle déteste ': FieldString('hate'),
'Ses vices ': FieldString('vices'),
'Ses atouts ': FieldString('assets'),
'Ses fantasmes ': FieldString('fantasies'),
'Elle est ': FieldList('is'),
},
}
ID_REGEXP = re.compile('(charm|addBasket|openAlbum)\(([0-9]+)(,[\s\'\d]+)?\)')
PHOTO_REGEXP = re.compile('http://(s|p)([0-9]+)\.adopteunmec\.com/(.*)')
STATS2ID = {'visites': 'visits',
'charmes': 'charms',
'paniers': 'baskets',
'mails': 'mails',
'POPULARIT': 'score',
}
STATS_VALUE_REGEXP = re.compile('([0-9\s]+).*')
def __repr__(self):
if isinstance(self.name, unicode):
name = self.name.encode('utf-8', 'backslashreplace')
else:
name = self.name
return '' % name
def on_loaded(self):
self.name = u''
self.description = u''
self.table = deepcopy(self.empty_table)
self.id = 0
self.photos = []
self.status = ''
self.stats = {'score': 0,
'visits': 0,
'charms': 0,
'baskets': 0,
'mails': 0,
}
divs = self.document.getElementsByTagName('td')
for div in divs:
if (div.hasAttribute('style') and
div.getAttribute('style') == "color:#ffffff;font-size:32px;font-weight:bold;letter-spacing:-2px" and
hasattr(div.firstChild, 'data')):
self.name = div.firstChild.data
if (div.hasAttribute('style') and
div.getAttribute('style') == "font-size:12px;font-weight:bold" and
hasattr(div.firstChild, 'data')):
self.status = div.firstChild.data
if div.hasAttribute('background'):
m = self.PHOTO_REGEXP.match(div.getAttribute('background'))
if m:
self.photos += [re.sub(u'thumb[0-2]_', u'image', div.getAttribute('background'))]
if div.hasAttribute('width') and str(div.getAttribute('width')) == '226':
trs = div.getElementsByTagName('tr')
for tr in trs:
tds = tr.getElementsByTagName('td')
if len(tds) > 2 and hasattr(tds[2].firstChild, 'data'):
label = tds[0].firstChild.data
value = tds[2].firstChild.data
elif len(tds) == 2:
label = unicode(tds[0].childNodes[1].data)
value = tds[1].childNodes[1].data
else:
continue
m = self.STATS_VALUE_REGEXP.match(value)
if m and self.STATS2ID.has_key(label):
self.stats[self.STATS2ID[label]] = int(m.group(1).replace(' ', ''))
divs = self.document.getElementsByTagName('div')
for div in divs:
if div.hasAttribute('id'):
if div.getAttribute('id') == 'about_div':
self.parse_description(div)
if div.getAttribute('id').startswith('tab_'):
self.parse_table(div)
for tag in ('img', 'td'):
imgs = self.document.getElementsByTagName(tag)
for img in imgs:
if img.hasAttribute('onclick'):
m = self.ID_REGEXP.match(img.getAttribute('onclick'))
if m:
self.id = int(m.group(2))
break
if self.id:
break
def parse_description(self, div):
# look for description
description = ''
for c in div.childNodes:
if hasattr(c, 'data'):
description += ''.join(c.data.split('\n')) # to strip \n
elif hasattr(c, 'tagName') and c.tagName == 'br':
description += '\n'
elif hasattr(c, 'tagName') and c.tagName == 'i':
description += ''.join(c.childNodes[0].data.split('\n'))
self.description = description
def parse_table(self, div):
d = self.table[self.tables[div.getAttribute('id')]]
fields = self.fields[self.tables[div.getAttribute('id')]]
table = div.getElementsByTagName('table')[1]
field1 = None
field2 = None
for tr in table.getElementsByTagName('tr'):
tds = tr.getElementsByTagName('td')
if len(tds) != 2:
continue
label1 = ''
label2 = ''
value1 = ''
value2 = ''
# Check for first column
if len(tds[0].childNodes) > 0:
b = len(tds[0].childNodes) > 2 and tds[0].childNodes[2]
if b and hasattr(b, 'tagName') and b.tagName == 'b':
for child in b.childNodes:
label1 += child.data
else:
for child in tds[0].childNodes:
if child.data != u'\xa0': # strip nbsp
value1 += child.data
value2 = value2.strip()
# Check for second column
if len(tds[1].childNodes) > 0:
b = tds[1].childNodes[0]
if b and hasattr(b, 'tagName') and b.tagName == 'b':
for child in b.firstChild.childNodes:
label2 += child.data
else:
for child in tds[1].childNodes:
if hasattr(child, 'data') and child.data != u'\xa0': # strip nbsp
value2 += child.data
if label1 and value2:
# This is a typically tuple of key/value on the line.
try:
fields[label1].put_value(d, value2)
except KeyError:
warning('Unable to find "%s" (%s)' % (label1, repr(label1)))
elif label1 and label2:
# two titles, so there will have a list of value in
# next lines on each columns
field1 = fields[label1]
field2 = fields[label2]
elif not label1 and not label1:
# two values, so it is a line of values
if field1 and value1:
field1.put_value(d, value1)
if field2 and value2:
field2.put_value(d, value2)
def get_name(self):
return self.name
def get_description(self):
return self.description
def get_table(self):
return self.table
def get_id(self):
return self.id
def get_photos(self):
return self.photos
def get_status(self):
return self.status
def is_online(self):
return self.status.find('en ligne') >= 0
def get_stats(self):
return self.stats
def get_profile_text(self):
body = u'Status: %s' % unicode(self.status)
if self.photos:
body += u'\nPhotos:'
for photo in self.photos:
body += u'\n\t\t%s' % unicode(photo)
body += u'\nStats:'
for label, value in self.get_stats().iteritems():
body += u'\n\t\t%-15s %s' % (label + ':', value)
body += u'\n\nInformations:'
for section, d in self.get_table().iteritems():
body += u'\n\t%s\n' % section
for key, value in d.items():
key = '%s:' % key
if isinstance(value, list):
body += u'\t\t%-15s %s\n' % (key, u', '.join([unicode(s) for s in value]))
elif isinstance(value, float):
body += u'\t\t%-15s %.2f\n' % (key, value)
else:
body += u'\t\t%-15s %s\n' % (key, unicode(value))
body += u'\n\nDescription:\n%s' % unicode(self.get_description())
return body
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/pages/profileslist_base.py 0000664 0000000 0000000 00000003570 11430720725 0032067 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2008-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.backends.aum.pages.base import PageBase
import re
class ProfilesListBase(PageBase):
PROFILE_REGEXP = re.compile(".*window\.location='(.*)'")
PHOTO_REGEXP = re.compile(".*background-image:url\(([A-Za-z0-9_\-:/\.]+)\).*")
WITHOUT_PHOTO = 'http://s.adopteunmec.com/img/thumb0.gif'
SHOW_WITHOUT_PHOTO = True
def on_loaded(self):
self.id_list = []
a_list = self.document.getElementsByTagName('div')
for a in a_list:
if a.hasAttribute('onclick') and a.hasAttribute('class') and a.getAttribute('class') in ('small', 'mini'):
m = self.PROFILE_REGEXP.match(a.getAttribute('onclick'))
if m:
url = m.group(1).split('/')[-1]
m = self.PHOTO_REGEXP.match(a.getElementsByTagName('div')[0].getAttribute('style'))
if url != 'home.php' and not url in self.id_list and \
m and (self.SHOW_WITHOUT_PHOTO or m.group(1) != self.WITHOUT_PHOTO):
self.id_list.append(url)
def get_profiles_ids(self):
return set(self.id_list)
def get_profiles_ids_list(self):
return self.id_list
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/pages/search.py 0000664 0000000 0000000 00000002712 11430720725 0027620 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2008-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.backends.aum.pages.profileslist_base import ProfilesListBase
class SearchPage(ProfilesListBase):
SHOW_WITHOUT_PHOTO = False
def search(self, **kwargs):
self.browser.select_form(name="form")
self.browser.set_all_readonly(False)
self.browser.set_field(kwargs, 'ageMin', is_list=True)
self.browser.set_field(kwargs, 'ageMax', is_list=True)
self.browser.set_field(kwargs, 'country', is_list=True)
self.browser.set_field(kwargs, 'dist', is_list=True)
self.browser.set_field(kwargs, 'nickname', field='pseudo')
self.browser.set_field(kwargs, 'save', value='true')
self.browser['originsV[]'] = ['1'] # excludes niggers (it doesn't work :( )
self.browser.submit()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/aum/pages/wait.py 0000664 0000000 0000000 00000002331 11430720725 0027314 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2008-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.backends.aum.pages.base import PageBase
from weboob.backends.aum.exceptions import AdopteWait
from time import sleep
class WaitPage(PageBase):
def on_loaded(self):
raise AdopteWait()
def check(self):
result = self.browser.openurl('http://www.adopteunmec.com/fajax_checkEnter.php?anticache=0.46168455299380795').read()
return result == 'Ok'
def process_wait(self):
while not self.check(self):
sleep(10)
self.browser.location('/home.php')
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/bnporc/ 0000775 0000000 0000000 00000000000 11430720725 0025401 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/bnporc/__init__.py 0000664 0000000 0000000 00000001360 11430720725 0027512 0 ustar 00root root 0000000 0000000 # -*- 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 .backend import BNPorcBackend
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/bnporc/backend.py 0000664 0000000 0000000 00000003674 11430720725 0027354 0 ustar 00root root 0000000 0000000 # -*- 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.bank import ICapBank, AccountNotFound
from weboob.tools.backend import BaseBackend
from .browser import BNPorc
class BNPorcBackend(BaseBackend, ICapBank):
NAME = 'bnporc'
MAINTAINER = 'Romain Bignon'
EMAIL = 'romain@peerfuse.org'
VERSION = '0.1'
LICENSE = 'GPLv3'
DESCRIPTION = 'BNP Paribas french bank\' website'
CONFIG = {'login': BaseBackend.ConfigField(description='Account ID'),
'password': BaseBackend.ConfigField(description='Password of account', is_masked=True)
}
BROWSER = BNPorc
def create_default_browser(self):
return self.create_browser(self.config['login'], self.config['password'])
def iter_accounts(self):
for account in self.browser.get_accounts_list():
yield account
def get_account(self, _id):
try:
_id = long(_id)
except ValueError:
raise AccountNotFound()
else:
account = self.browser.get_account(_id)
if account:
return account
else:
raise AccountNotFound()
def iter_operations(self, account):
for coming in self.browser.get_coming_operations(account):
yield coming
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/bnporc/browser.py 0000664 0000000 0000000 00000006121 11430720725 0027436 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2009-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.tools.browser import BaseBrowser, BrowserIncorrectPassword
from weboob.backends.bnporc import pages
# Browser
class BNPorc(BaseBrowser):
DOMAIN = 'www.secure.bnpparibas.net'
PROTOCOL = 'https'
ENCODING = None # refer to the HTML encoding
PAGES = {'.*identifiant=DOSSIER_Releves_D_Operation.*': pages.AccountsList,
'.*identifiant=DSP_HISTOCPT.*': pages.AccountHistory,
'.*NS_AVEEC.*': pages.AccountComing,
'.*NS_AVEDP.*': pages.AccountPrelevement,
'.*Action=DSP_VGLOBALE.*': pages.LoginPage,
'.*type=homeconnex.*': pages.LoginPage,
'.*layout=HomeConnexion.*': pages.ConfirmPage,
}
is_logging = False
def home(self):
self.location('https://www.secure.bnpparibas.net/banque/portail/particulier/HomeConnexion?type=homeconnex')
def is_logged(self):
return not self.is_on_page(pages.LoginPage) or self.is_logging
def login(self):
assert isinstance(self.username, (str,unicode))
assert isinstance(self.password, (str,unicode))
assert self.password.isdigit()
if not self.is_on_page(pages.LoginPage):
self.location('https://www.secure.bnpparibas.net/banque/portail/particulier/HomeConnexion?type=homeconnex')
self.is_logging = True
self.page.login(self.username, self.password)
self.location('/NSFR?Action=DSP_VGLOBALE')
if self.is_on_page(pages.LoginPage):
raise BrowserIncorrectPassword()
self.is_logging = False
def get_accounts_list(self):
if not self.is_on_page(pages.AccountsList):
self.location('/NSFR?Action=DSP_VGLOBALE')
return self.page.get_list()
def get_account(self, id):
assert isinstance(id, (int, long))
if not self.is_on_page(pages.AccountsList):
self.location('/NSFR?Action=DSP_VGLOBALE')
l = self.page.get_list()
for a in l:
if a.id == id:
return a
return None
def get_coming_operations(self, account):
if not self.is_on_page(pages.AccountComing) or self.page.account.id != account.id:
self.location('/NS_AVEEC?ch4=%s' % account.link_id)
return self.page.get_operations()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/bnporc/captcha.py 0000664 0000000 0000000 00000006677 11430720725 0027376 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2009-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 hashlib
import sys
import Image
class TileError(Exception):
def __init__(self, msg, tile = None):
Exception.__init__(self, msg)
self.tile = tile
class Captcha:
def __init__(self, file):
self.inim = Image.open(file)
self.nx,self.ny = self.inim.size
self.inmat = self.inim.load()
self.map = {}
self.tiles = [[Tile(x+5*y+1) for y in xrange(5)] for x in xrange(5)]
def __getitem__(self, (x, y)):
return self.inmat[x % self.nx, y % self.ny]
def all_coords(self):
for y in xrange(self.ny):
for x in xrange(self.nx):
yield x, y
def get_codes(self, code):
s = ''
for c in code:
s += '%02d' % self.map[int(c)].id
return s
def build_tiles(self):
y = 5
ty = 0
while y < self.ny:
x = 6
tx = 0
while x < self.nx:
if self[x,y] == 8:
tile = self.tiles[tx][ty]
tile.valid = True
yy = y
while not self[x,yy] in (3,7):
l = []
tile.map.append(l)
xx = x
while not self[xx,yy] in (3,7):
l.append(self[xx,yy])
xx += 1
yy += 1
self.map[tile.get_num()] = tile
x += 26
tx += 1
y += 25
ty += 1
class Tile:
hash = {'b2d25ae11efaaaec6dd6a4c00f0dfc29': 1,
'600873fa288e75ca6cca092ae95bf129': 2,
'da24ac28930feee169adcbc9bad4acaf': 3,
'76294dec2a3c6a7b8d9fcc7a116d1d4f': 4,
'd9531059e3834b6b8a97e29417a47dec': 5,
'8ba0c0cfe5e64d6b4afb1aa6f3612c1a': 6,
'19e0120231e7a9cf4544f96d8c388c8a': 7,
'83d8ad340156cb7f5c1e64454b66c773': 8,
'5ee8648d77eeb3e0979f6e59b2fbe66a': 9,
'3f3fb79bf61ebad096e05287119169df': 0
}
def __init__(self, _id):
self.id = _id
self.valid = False
self.map = []
def __repr__(self):
return "" % (self.id, self.valid)
def checksum(self):
s = ''
for pxls in self.map:
for pxl in pxls:
s += '%02d' % pxl
return hashlib.md5(s).hexdigest()
def get_num(self):
sum = self.checksum()
try:
return self.hash[sum]
except KeyError:
raise TileError('Tile not found', self)
def display(self):
for pxls in self.map:
for pxl in pxls:
sys.stdout.write('%02d' % pxl)
sys.stdout.write('\n')
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/bnporc/pages/ 0000775 0000000 0000000 00000000000 11430720725 0026500 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/bnporc/pages/__init__.py 0000664 0000000 0000000 00000001645 11430720725 0030617 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2009-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 .accounts_list import AccountsList
from .account_coming import AccountComing
from .login import LoginPage, ConfirmPage
class AccountHistory(AccountsList): pass
class AccountPrelevement(AccountsList): pass
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/bnporc/pages/account_coming.py 0000664 0000000 0000000 00000003576 11430720725 0032055 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2009-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.tools.browser import BasePage
from weboob.capabilities.bank import Operation
__all__ = ['AccountComing']
class AccountComing(BasePage):
def on_loaded(self):
self.operations = []
for tr in self.document.getiterator('tr'):
if tr.attrib.get('class', '') == 'hdoc1' or tr.attrib.get('class', '') == 'hdotc1':
tds = tr.findall('td')
if len(tds) != 3:
continue
date = tds[0].getchildren()[0].attrib.get('name', '')
label = u''
label += tds[1].text
for child in tds[1].getchildren():
if child.text: label += child.text
if child.tail: label += child.tail
if tds[1].tail: label += tds[1].tail
label = label.strip()
amount = tds[2].text.replace('.', '').replace(',', '.')
operation = Operation()
operation.date = date
operation.label = label
operation.amount = float(amount)
self.operations.append(operation)
def get_operations(self):
return self.operations
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/bnporc/pages/accounts_list.py 0000664 0000000 0000000 00000004507 11430720725 0031732 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2009-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 re
from weboob.capabilities.bank import Account
from weboob.tools.browser import BasePage
class AccountsList(BasePage):
LINKID_REGEXP = re.compile(".*ch4=(\w+).*")
def on_loaded(self):
pass
def get_list(self):
l = []
for tr in self.document.getiterator('tr'):
if tr.attrib.get('class', '') == 'comptes':
account = Account()
for td in tr.getiterator('td'):
if td.attrib.get('headers', '').startswith('Numero_'):
id = td.text
account.id = ''.join(id.split(' ')).strip()
elif td.attrib.get('headers', '').startswith('Libelle_'):
a = td.findall('a')
label = unicode(a[0].text)
account.label = label
m = self.LINKID_REGEXP.match(a[0].attrib.get('href', ''))
if m:
account.link_id = m.group(1)
elif td.attrib.get('headers', '').startswith('Solde'):
a = td.findall('a')
balance = a[0].text
balance = balance.replace('.','').replace(',','.')
account.balance = float(balance)
elif td.attrib.get('headers', '').startswith('Avenir'):
a = td.findall('a')
coming = a[0].text
coming = coming.replace('.','').replace(',','.')
account.coming = float(coming)
l.append(account)
return l
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/bnporc/pages/login.py 0000664 0000000 0000000 00000003203 11430720725 0030160 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2009-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.tools.mech import ClientForm
import sys
from weboob.tools.browser import BasePage
from weboob.backends.bnporc.captcha import Captcha, TileError
class LoginPage(BasePage):
def on_loaded(self):
pass
def login(self, login, password):
img = Captcha(self.browser.openurl('/NSImgGrille'))
try:
img.build_tiles()
except TileError, err:
print >>sys.stderr, "Error: %s" % err
if err.tile:
err.tile.display()
self.browser.select_form('logincanalnet')
# HACK because of fucking malformed HTML, the field isn't recognized by mechanize.
self.browser.controls.append(ClientForm.TextControl('text', 'ch1', {'value': ''}))
self.browser.set_all_readonly(False)
self.browser['ch1'] = login
self.browser['ch5'] = img.get_codes(password)
self.browser.submit()
class ConfirmPage(BasePage):
pass
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/canaltp/ 0000775 0000000 0000000 00000000000 11430720725 0025540 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/canaltp/__init__.py 0000664 0000000 0000000 00000001361 11430720725 0027652 0 ustar 00root root 0000000 0000000 # -*- 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 .backend import CanalTPBackend
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/canaltp/backend.py 0000664 0000000 0000000 00000003300 11430720725 0027475 0 ustar 00root root 0000000 0000000 # -*- 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.travel import ICapTravel, Station, Departure
from weboob.tools.backend import BaseBackend
from .browser import CanalTP
__all__ = ['CanalTPBackend']
class CanalTPBackend(BaseBackend, ICapTravel):
NAME = 'canaltp'
MAINTAINER = 'Romain Bignon'
EMAIL = 'romain@peerfuse.org'
VERSION = '0.1'
LICENSE = 'GPLv3'
DESCRIPTION = "French trains"
BROWSER = CanalTP
def iter_station_search(self, pattern):
for _id, name in self.browser.iter_station_search(pattern):
yield Station(_id, name)
def iter_station_departures(self, station_id, arrival_id=None):
for i, d in enumerate(self.browser.iter_station_departures(station_id, arrival_id)):
departure = Departure(i, d['type'], d['time'])
departure.departure_station = d['departure']
departure.arrival_station = d['arrival']
departure.late = d['late']
departure.information = d['late_reason']
yield departure
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/canaltp/browser.py 0000664 0000000 0000000 00000005070 11430720725 0027577 0 ustar 00root root 0000000 0000000 # -*- 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 datetime import datetime, date, time
from weboob.tools.browser import BaseBrowser
from weboob.tools.misc import to_unicode
class CanalTP(BaseBrowser):
DOMAIN = 'widget.canaltp.fr'
def __init__(self):
BaseBrowser.__init__(self, '')
def iter_station_search(self, pattern):
url = u'http://widget.canaltp.fr/Prochains_departs_15122009/dev/gare.php?txtrech=%s' % unicode(pattern)
result = self.openurl(url.encode('utf-8')).read()
for station in result.split('&'):
try:
_id, name = station.split('=')
except ValueError:
continue
else:
yield _id, to_unicode(name)
def iter_station_departures(self, station_id, arrival_id=None):
url = u'http://widget.canaltp.fr/Prochains_departs_15122009/dev/index.php?gare=%s' % unicode(station_id)
result = self.openurl(url.encode('utf-8')).read()
result = result
departure = ''
for line in result.split('&'):
key, value = line.split('=', 1)
if key == 'nomgare':
departure = value
elif key.startswith('ligne'):
_type, unknown, _time, arrival, served, late, late_reason = value.split(';', 6)
yield {'type': to_unicode(_type),
'time': datetime.combine(date.today(), time(*[int(x) for x in _time.split(':')])),
'departure': to_unicode(departure),
'arrival': to_unicode(arrival).strip(),
'late': late and time(0, int(late.split()[0])) or time(),
'late_reason': to_unicode(late_reason).replace('\n', '').strip()}
def home(self):
pass
def login(self):
pass
def is_logged(self):
""" Do not need to be logged """
return True
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/cragr/ 0000775 0000000 0000000 00000000000 11430720725 0025214 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/cragr/__init__.py 0000664 0000000 0000000 00000001357 11430720725 0027333 0 ustar 00root root 0000000 0000000 # -*- 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 .backend import CragrBackend
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/cragr/backend.py 0000664 0000000 0000000 00000004050 11430720725 0027154 0 ustar 00root root 0000000 0000000 # -*- 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.bank import ICapBank, AccountNotFound
from weboob.tools.backend import BaseBackend
from .browser import Cragr
class CragrBackend(BaseBackend, ICapBank):
NAME = 'cragr'
MAINTAINER = 'Laurent Bachelier'
EMAIL = 'laurent@bachelier.name'
VERSION = '0.1'
DESCRIPTION = 'Credit Agricole french bank\'s website'
LICENSE = 'GPLv3'
CONFIG = {'login': BaseBackend.ConfigField(description='Account ID'),
'password': BaseBackend.ConfigField(description='Password of account', is_masked=True),
'website': BaseBackend.ConfigField(description='What website to use', default='m.lefil.com'),
}
BROWSER = Cragr
def create_default_browser(self):
return self.create_browser(self.config['website'], self.config['login'], self.config['password'])
def iter_accounts(self):
for account in self.browser.get_accounts_list():
yield account
def get_account(self, _id):
try:
_id = long(_id)
except ValueError:
raise AccountNotFound()
else:
account = self.browser.get_account(_id)
if account:
return account
else:
raise AccountNotFound()
def iter_operations(self, account):
""" Not supported yet """
return iter([])
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/cragr/browser.py 0000664 0000000 0000000 00000005361 11430720725 0027256 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2009-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.tools.browser import BaseBrowser, BrowserIncorrectPassword
from weboob.backends.cragr import pages
# Browser
class Cragr(BaseBrowser):
PROTOCOL = 'https'
ENCODING = 'utf-8'
USER_AGENT = 'Wget/1.11.4'
is_logging = False
def __init__(self, website, *args, **kwargs):
self.DOMAIN = website
self.PAGES = {'https://%s/' % website: pages.LoginPage,
'https://%s/.*\.c.*' % website: pages.AccountsList,
'https://%s/login/process' % website: pages.AccountsList,
}
BaseBrowser.__init__(self, *args, **kwargs)
def viewing_html(self):
"""
As the fucking HTTP server returns a document in unknown mimetype
'application/vnd.wap.xhtml+xml' it is not recognized by mechanize.
So this is a fucking hack.
"""
return True
def home(self):
self.location('https://%s/' % self.DOMAIN)
def is_logged(self):
return self.page and self.page.is_logged() or self.is_logging
def login(self):
assert isinstance(self.username, (str,unicode))
assert isinstance(self.password, (str,unicode))
self.is_logging = True
if not self.is_on_page(pages.LoginPage):
self.home()
self.page.login(self.username, self.password)
self.is_logging = False
if not self.is_logged():
raise BrowserIncorrectPassword()
def get_accounts_list(self):
if not self.is_on_page(pages.AccountsList):
self.home()
return self.page.get_list()
def get_account(self, id):
assert isinstance(id, (int, long))
l = self.get_accounts_list()
for a in l:
if a.id == id:
return a
return None
#def get_coming_operations(self, account):
# if not self.is_on_page(pages.AccountComing) or self.page.account.id != account.id:
# self.location('/NS_AVEEC?ch4=%s' % account.link_id)
# return self.page.get_operations()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/cragr/pages/ 0000775 0000000 0000000 00000000000 11430720725 0026313 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/cragr/pages/__init__.py 0000664 0000000 0000000 00000001422 11430720725 0030423 0 ustar 00root root 0000000 0000000 # -*- 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 .accounts_list import AccountsList
from .login import LoginPage
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/cragr/pages/accounts_list.py 0000664 0000000 0000000 00000003047 11430720725 0031543 0 ustar 00root root 0000000 0000000 # -*- 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.bank import Account
from .base import CragrBasePage
class AccountsList(CragrBasePage):
def on_loaded(self):
pass
def get_list(self):
l = []
for div in self.document.getiterator('div'):
if div.attrib.get('class', '') == 'dv' and div.getchildren()[0].tag == 'br':
account = Account()
account.label = div.find('a').text.strip()
account.id = div.findall('br')[1].tail.strip()
s = div.find('div').find('span').find('b').text
balance = u''
for c in s:
if c.isdigit():
balance += c
if c == ',':
balance += '.'
account.balance = float(balance)
l.append(account)
return l
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/cragr/pages/base.py 0000664 0000000 0000000 00000001625 11430720725 0027603 0 ustar 00root root 0000000 0000000 # -*- 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.tools.browser import BasePage
class CragrBasePage(BasePage):
def is_logged(self):
for form in self.document.getiterator('form'):
return False
return True
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/cragr/pages/login.py 0000664 0000000 0000000 00000003747 11430720725 0030010 0 ustar 00root root 0000000 0000000 # -*- 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.tools.mech import ClientForm
ControlNotFoundError = ClientForm.ControlNotFoundError
from .base import CragrBasePage
__all__ = ['LoginPage']
class LoginPage(CragrBasePage):
def on_loaded(self):
pass
def login(self, login, password):
self.browser.select_form(nr=0)
try:
self.browser['numero'] = login
self.browser['code'] = password
except ControlNotFoundError:
try:
self.browser['userLogin'] = login
self.browser['userPassword'] = password
except ControlNotFoundError:
self.browser.controls.append(ClientForm.TextControl('text', 'numero', {'value': ''}))
self.browser.controls.append(ClientForm.TextControl('text', 'code', {'value': ''}))
self.browser.controls.append(ClientForm.TextControl('text', 'userLogin', {'value': ''}))
self.browser.controls.append(ClientForm.TextControl('text', 'userPassword', {'value': ''}))
self.browser.set_all_readonly(False)
self.browser['numero'] = login
self.browser['code'] = password
self.browser['userLogin'] = login
self.browser['userPassword'] = password
self.browser.submit()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/dlfp/ 0000775 0000000 0000000 00000000000 11430720725 0025043 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/dlfp/__init__.py 0000664 0000000 0000000 00000001410 11430720725 0027150 0 ustar 00root root 0000000 0000000 # -*- 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 .browser import DLFP
from .backend import DLFPBackend
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/dlfp/backend.py 0000664 0000000 0000000 00000011356 11430720725 0027012 0 ustar 00root root 0000000 0000000 # -*- 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 weboob.tools.backend import BaseBackend
from weboob.tools.browser import BrowserUnavailable
from weboob.capabilities.messages import ICapMessages, ICapMessagesReply, Message
from .feeds import ArticlesList
from .browser import DLFP
__all__ = ['DLFPBackend']
class DLFPBackend(BaseBackend, ICapMessages, ICapMessagesReply):
NAME = 'dlfp'
MAINTAINER = 'Romain Bignon'
EMAIL = 'romain@peerfuse.org'
VERSION = '0.1'
LICENSE = 'GPLv3'
DESCRIPTION = "Da Linux French Page"
CONFIG = {'username': BaseBackend.ConfigField(description='Username on website'),
'password': BaseBackend.ConfigField(description='Password of account', is_masked=True),
'get_news': BaseBackend.ConfigField(default=True, description='Get newspapers'),
'get_telegrams': BaseBackend.ConfigField(default=False, description='Get telegrams'),
}
STORAGE = {'seen': {}}
BROWSER = DLFP
def create_default_browser(self):
return self.create_browser(self.config['username'], self.config['password'])
def iter_messages(self, thread=None):
return self._iter_messages(thread, False)
def iter_new_messages(self, thread=None):
return self._iter_messages(thread, True)
def _iter_messages(self, thread, only_new):
if self.config['get_news']:
for message in self._iter_messages_of('newspaper', thread, only_new):
yield message
if self.config['get_telegrams']:
for message in self._iter_messages_of('telegram', thread, only_new):
yield message
def _iter_messages_of(self, what, thread_wanted, only_new):
if not what in self.storage.get('seen', default={}):
self.storage.set('seen', what, {})
seen = {}
for article in ArticlesList(what).iter_articles():
if thread_wanted and thread_wanted != article.id:
continue
if not article.id in self.storage.get('seen', what, default={}):
seen[article.id] = {'comments': []}
new = True
else:
seen[article.id] = self.storage.get('seen', what, article.id, default={})
new = False
try:
with self.browser:
thread = self.browser.get_content(article.id)
except BrowserUnavailable:
continue
if not only_new or new:
yield Message(thread.id,
0,
thread.title,
thread.author,
article.datetime,
content=''.join([thread.body, thread.part2]),
signature='URL: %s' % article.url,
is_html=True,
is_new=new)
for comment in thread.iter_all_comments():
if not comment.id in seen[article.id]['comments']:
seen[article.id]['comments'].append(comment.id)
new = True
else:
new = False
if not only_new or new:
yield Message(thread.id,
comment.id,
comment.title,
comment.author,
comment.date,
comment.reply_id,
comment.body,
'Score: %d' % comment.score,
is_html=True,
is_new=new)
# If there is no articles seen, it's suspicious, probably I can't
# fetch the feed.
if seen:
self.storage.set('seen', what, seen)
self.storage.save()
def post_reply(self, thread_id, reply_id, title, message):
with self.browser:
return self.browser.post_reply(thread_id, reply_id, title, message)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/dlfp/browser.py 0000664 0000000 0000000 00000006100 11430720725 0027075 0 ustar 00root root 0000000 0000000 # -*- 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 urllib
from cStringIO import StringIO
from weboob.tools.browser import BaseBrowser
from weboob.tools.parsers.lxmlparser import LxmlHtmlParser
from .pages.index import IndexPage, LoginPage
from .pages.news import ContentPage
from .tools import id2url, id2threadid, id2contenttype
class Parser(LxmlHtmlParser):
def parse(self, data, encoding=None):
# Want to kill templeet coders
data = StringIO(data.read().replace('<<', '<'))
return LxmlHtmlParser.parse(self, data, encoding)
# Browser
class DLFP(BaseBrowser):
DOMAIN = 'linuxfr.org'
PROTOCOL = 'https'
PAGES = {'https://linuxfr.org/': IndexPage,
'https://linuxfr.org/pub/': IndexPage,
'https://linuxfr.org/my/': IndexPage,
'https://linuxfr.org/login.html': LoginPage,
'https://linuxfr.org/.*/\d+.html': ContentPage
}
def __init__(self, *args, **kwargs):
kwargs['parser'] = Parser()
BaseBrowser.__init__(self, *args, **kwargs)
def home(self):
return self.location('https://linuxfr.org')
def get_content(self, _id):
self.location(id2url(_id))
return self.page.get_article()
def post_reply(self, thread, reply_id, title, message):
content_type = id2contenttype(thread)
thread_id = id2threadid(thread)
reply_id = int(reply_id)
if not content_type or not thread_id:
return False
# Define every data fields
data = {'news_id': thread_id,
'com_parent': reply_id,
'timestamp': '',
'res_type': content_type,
'referer': '%s://%s%s' % (self.PROTOCOL, self.DOMAIN, id2url(thread)),
'subject': unicode(title).encode('utf-8'),
'body': unicode(message).encode('utf-8'),
'format': 3,
'submit': 'Envoyer',
}
url = '%s://%s/submit/comments,%d,%d,%d.html#post' % (self.PROTOCOL, self.DOMAIN, thread_id, reply_id, content_type)
request = self.request_class(url, urllib.urlencode(data), {'Referer': url})
self.openurl(request).read()
# No message to send
return ()
def login(self):
self.location('/login.html', 'login=%s&passwd=%s&isauto=1' % (self.username, self.password))
def is_logged(self):
return (self.page and self.page.is_logged())
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/dlfp/feeds.py 0000664 0000000 0000000 00000003434 11430720725 0026507 0 ustar 00root root 0000000 0000000 # -*- 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 feedparser
from datetime import datetime
from .tools import url2id
class Article:
RSS = None
def __init__(self, _id, url, title, author, datetime):
self.id = _id
self.url = url
self.title = title
self.author = author
self.datetime = datetime
class Newspaper(Article):
RSS = 'https://linuxfr.org/backend/news/rss20.rss'
class Telegram(Article):
RSS = 'https://linuxfr.org/backend/journaux/rss20.rss'
class ArticlesList:
RSS = {'newspaper': Newspaper,
'telegram': Telegram
}
def __init__(self, section=None):
self.section = section
self.articles = []
def iter_articles(self):
for section, klass in self.RSS.iteritems():
if self.section and self.section != section:
continue
url = klass.RSS
feed = feedparser.parse(url)
for item in feed['items']:
article = klass(url2id(item['link']), item['link'], item['title'], item['author'], datetime(*item['date_parsed'][:7]))
yield article
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/dlfp/pages/ 0000775 0000000 0000000 00000000000 11430720725 0026142 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/dlfp/pages/__init__.py 0000664 0000000 0000000 00000000000 11430720725 0030241 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/dlfp/pages/index.py 0000664 0000000 0000000 00000002545 11430720725 0027631 0 ustar 00root root 0000000 0000000 # -*- 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.tools.browser import BrowserIncorrectPassword, BasePage
class DLFPPage(BasePage):
def is_logged(self):
for form in self.document.getiterator('form'):
if form.attrib.get('id', None) == 'formulaire':
return False
return True
class IndexPage(DLFPPage):
pass
class LoginPage(DLFPPage):
def on_loaded(self):
if self.has_error():
raise BrowserIncorrectPassword()
def has_error(self):
for p in self.document.getiterator('p'):
if p.text and p.text.startswith(u'Vous avez rentré un mauvais mot de passe'):
return True
return False
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/dlfp/pages/news.py 0000664 0000000 0000000 00000011075 11430720725 0027474 0 ustar 00root root 0000000 0000000 # -*- 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 datetime import datetime
from logging import warning
from weboob.tools.misc import local2utc
from weboob.backends.dlfp.tools import url2id
from .index import DLFPPage
class Comment(object):
def __init__(self, browser, div, reply_id):
self.browser = browser
self.id = ''
self.reply_id = reply_id
self.title = u''
self.author = u''
self.date = u''
self.body = u''
self.score = 0
self.comments = []
for sub in div.getchildren():
if sub.tag == 'a':
self.id = sub.attrib['name']
elif sub.tag == 'h1':
try:
self.title = sub.find('b').text
except UnicodeError:
warning('Bad encoded title, but DLFP sucks')
elif sub.tag == 'div' and sub.attrib.get('class', '').startswith('comment'):
self.author = sub.find('a').text
self.date = self.parse_date(sub.find('i').tail)
self.score = int(sub.findall('i')[1].find('span').text)
self.body = self.browser.parser.tostring(sub.find('p'))
elif sub.attrib.get('class', '') == 'commentsul':
comment = Comment(self.browser, sub.find('li'), self.id)
self.comments.append(comment)
def parse_date(self, date_s):
return local2utc(datetime.strptime(date_s.strip().encode('utf-8'), u'le %d/%m/%Y \xe0 %H:%M.'.encode('utf-8')))
def iter_all_comments(self):
for comment in self.comments:
yield comment
for c in comment.iter_all_comments():
yield c
def __repr__(self):
return u"" % (self.id, self.author, self.title)
class Article(object):
def __init__(self, browser, _id, tree):
self.browser = browser
self.id = _id
self.title = u''
self.author = u''
self.body = u''
self.part2 = u''
self.date = u''
self.comments = []
for div in tree.findall('div'):
if div.attrib.get('class', '').startswith('titlediv '):
self.author = div.find('a').text
for a in div.find('h1').getiterator('a'):
if a.text: self.title += a.text
if a.tail: self.title += a.tail
self.title = self.title.strip()
subdivs = div.findall('a')
if len(subdivs) > 1:
date_s = unicode(subdivs[1].text)
else:
date_s = unicode(div.find('i').tail)
#print date_s
if div.attrib.get('class', '').startswith('bodydiv '):
self.body = self.browser.parser.tostring(div)
def append_comment(self, comment):
self.comments.append(comment)
def iter_all_comments(self):
for comment in self.comments:
yield comment
for c in comment.iter_all_comments():
yield c
def parse_part2(self, div):
self.part2 = self.browser.parser.tostring(div)
class ContentPage(DLFPPage):
def on_loaded(self):
self.article = None
for div in self.document.find('body').find('div').findall('div'):
self.parse_div(div)
if div.attrib.get('class', '') == 'centraldiv':
for subdiv in div.findall('div'):
self.parse_div(subdiv)
def parse_div(self, div):
if div.attrib.get('class', '') in ('newsdiv', 'centraldiv'):
self.article = Article(self.browser, url2id(self.url), div)
if div.attrib.get('class', '') == 'articlediv':
self.article.parse_part2(div)
if div.attrib.get('class', '') == 'comments':
comment = Comment(self.browser, div, 0)
self.article.append_comment(comment)
def get_article(self):
return self.article
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/dlfp/tools.py 0000664 0000000 0000000 00000003602 11430720725 0026556 0 ustar 00root root 0000000 0000000 # -*- 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 re
ID2URL_NEWSPAPER = re.compile('.*/(\d{4})/(\d{2})/(\d{2})/(\d+)\.html$')
ID2URL_TELEGRAM = re.compile('.*/~([A-Za-z0-9_]+)/(\d+)\.html$')
URL2ID_NEWSPAPER = re.compile('^N(\d{4})(\d{2})(\d{2}).(\d+)$')
URL2ID_TELEGRAM = re.compile('^T([A-Za-z0-9_]+).(\d+)$')
def url2id(url):
m = ID2URL_NEWSPAPER.match(url)
if m:
return 'N%04d%02d%02d.%d' % (int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4)))
m = ID2URL_TELEGRAM.match(url)
if m:
return 'T%s.%d' % (m.group(1), int(m.group(2)))
return None
def id2url(_id):
m = URL2ID_NEWSPAPER.match(_id)
if m:
return '/%04d/%02d/%02d/%d.html' % (int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4)))
m = URL2ID_TELEGRAM.match(_id)
if m:
return '/~%s/%d.html' % (m.group(1), int(m.group(2)))
return None
def id2threadid(_id):
m = URL2ID_NEWSPAPER.match(_id)
if m:
return int(m.group(4))
m = URL2ID_TELEGRAM.match(_id)
if m:
return int(m.group(2))
return None
def id2contenttype(_id):
if not _id:
return None
if _id[0] == 'N':
return 1
if _id[0] == 'T':
return 5
return None
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/fourchan/ 0000775 0000000 0000000 00000000000 11430720725 0025723 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/fourchan/__init__.py 0000664 0000000 0000000 00000000103 11430720725 0030026 0 ustar 00root root 0000000 0000000 from .backend import FourChanBackend
from .browser import FourChan
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/fourchan/backend.py 0000664 0000000 0000000 00000010100 11430720725 0027654 0 ustar 00root root 0000000 0000000 # -*- 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 logging import warning
from weboob.capabilities.messages import ICapMessages, Message
from weboob.tools.backend import BaseBackend
from .browser import FourChan
__all__ = ['FourChanBackend']
class FourChanBackend(BaseBackend, ICapMessages):
NAME = 'fourchan'
MAINTAINER = 'Romain Bignon'
EMAIL = 'romain@peerfuse.org'
VERSION = '0.1'
LICENSE = 'GPLv3'
DESCRIPTION = "4chan website"
CONFIG = {'boards': BaseBackend.ConfigField(description='Boards'),
}
STORAGE = {'boards': {}}
BROWSER = FourChan
def iter_messages(self, thread=None):
return self._iter_messages(thread, False)
def iter_new_messages(self, thread=None):
return self._iter_messages(thread, True)
def _iter_messages(self, thread, only_new):
if thread:
if '.' in thread:
board, thread = thread.split('.', 2)
return self._iter_messages_of(board, thread, only_new)
else:
warning('"%s" is not a valid ID' % thread)
else:
for board in self.config['boards'].split(' '):
return self._iter_messages_of(board, None, only_new)
def _iter_messages_of(self, board, thread_wanted, only_new):
if not board in self.storage.get('boards', default={}):
self.storage.set('boards', board, {})
if thread_wanted:
for message in self._iter_thread_messages(board, thread_wanted, only_new):
yield message
else:
with self.browser:
threads = self.browser.get_threads(board)
for thread in threads:
for message in self._iter_thread_messages(board, thread.id, only_new):
yield message
def _iter_thread_messages(self, board, thread, only_new):
thread = self.browser.get_thread(board, thread)
if thread.id in self.storage.get('boards', board, default={}):
self.storage.set('boards', board, thread.id, [])
new = True
else:
new = False
if not only_new or new:
yield Message('%s.%s' % (board, thread.id),
0,
thread.filename,
thread.author,
thread.datetime,
content=thread.text,
is_html=True,
is_new=new)
for comment in thread.comments:
if not comment.id in self.storage.get('boards', board, thread.id, default=[]):
self.storage.set('boards', board, thread.id, self.storage.get('boards', board, thread.id, default=[]) + [comment.id])
new = True
else:
new = False
if not only_new or new:
yield Message('%s.%s' % (board, thread.id),
comment.id,
thread.filename,
comment.author,
comment.datetime,
0,
comment.text,
is_html=True,
is_new=new)
self.storage.save()
#def post_reply(self, thread_id, reply_id, title, message):
# return self.browser.post_reply(thread_id, reply_id, title, message)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/fourchan/browser.py 0000664 0000000 0000000 00000002522 11430720725 0027761 0 ustar 00root root 0000000 0000000 # -*- 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.tools.browser import BaseBrowser
from .pages.board import BoardPage
class FourChan(BaseBrowser):
DOMAIN = 'boards.4chan.org'
PAGES = {'http://boards.4chan.org/\w+/': BoardPage,
'http://boards.4chan.org/\w+/res/\d+': BoardPage,
}
def is_logged(self):
return True
def get_threads(self, board):
self.location('http://boards.4chan.org/%s/' % board)
return self.page.articles
def get_thread(self, board, id):
self.location('http://boards.4chan.org/%s/res/%d' % (board, id))
assert len(self.page.articles) == 1
return self.page.articles[0]
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/fourchan/pages/ 0000775 0000000 0000000 00000000000 11430720725 0027022 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/fourchan/pages/__init__.py 0000664 0000000 0000000 00000000000 11430720725 0031121 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/fourchan/pages/board.py 0000664 0000000 0000000 00000006021 11430720725 0030462 0 ustar 00root root 0000000 0000000 # -*- 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 re
from logging import warning
from weboob.tools.browser import BasePage
class Message(object):
def __init__(self, browser, board, id, filename=u'', url=u''):
self.id = id
self.browser = browser
self.board = board
self.filename = filename
self.datetime = 0
self.url = url
self.author = u''
self.text = u''
self.comments = []
def add_comment(self, div):
comment = Message(self.browser, self.board, int(div.attrib.get('id', '')))
comment.author = div.cssselect('span.commentpostername')[0].text
comment.text = self.browser.parser.tostring(div.find('blockquote'))
self.comments.append(comment)
def __repr__(self):
return '' % (self.id, self.filename, self.url, len(self.comments))
class BoardPage(BasePage):
URL_REGEXP = re.compile('http://boards.4chan.org/(\w+)/')
def on_loaded(self):
self.articles = []
m = self.URL_REGEXP.match(self.url)
if m:
self.board = m.group(1)
else:
warning('Unable to find board')
self.board = 'unknown'
forms = self.document.getroot().cssselect('form')
form = None
for f in forms:
if f.attrib.get('name', '') == 'delform':
form = f
break
if form is None:
warning('No delform :(')
article = None
for div in form.getchildren():
if div.tag == 'span' and div.attrib.get('class', '') == 'filesize':
url = div.find('a').get('href', '')
filename = 'unknown.jpg'
span = div.find('span')
if span is not None:
filename = span.text
article = Message(self.browser, self.board, 0, filename, url)
self.articles.append(article)
if div.tag == 'input' and div.attrib.get('type', 'checkbox') and div.attrib.get('value', 'delete'):
article.id = int(div.attrib.get('name', '0'))
if div.tag == 'blockquote':
article.text = self.browser.parser.tostring(div)
if div.tag == 'table':
tags = div.cssselect('td.reply')
if tags:
article.add_comment(tags[0])
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/gazelle/ 0000775 0000000 0000000 00000000000 11430720725 0025541 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/gazelle/__init__.py 0000664 0000000 0000000 00000000044 11430720725 0027650 0 ustar 00root root 0000000 0000000 from .backend import GazelleBackend
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/gazelle/backend.py 0000664 0000000 0000000 00000004134 11430720725 0027504 0 ustar 00root root 0000000 0000000 # -*- 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.torrent import ICapTorrent
from weboob.tools.backend import BaseBackend
from .browser import GazelleBrowser
__all__ = ['GazelleBackend']
class GazelleBackend(BaseBackend, ICapTorrent):
NAME = 'gazelle'
MAINTAINER = 'Romain Bignon'
EMAIL = 'romain@peerfuse.org'
VERSION = '0.1'
DESCRIPTION = 'gazelle bittorrent tracker'
LICENSE = 'GPLv3'
CONFIG = {'username': BaseBackend.ConfigField(description='Username on website'),
'password': BaseBackend.ConfigField(description='Password of account', is_masked=True),
'protocol': BaseBackend.ConfigField(description='Protocol to use ("http" or "https")', regexp='^(http|https)$'),
'domain': BaseBackend.ConfigField(description='Domain (example "ssl.what.cd")'),
}
BROWSER = GazelleBrowser
def create_default_browser(self):
return self.create_browser(self.config['protocol'], self.config['domain'],
self.config['username'], self.config['password'])
def get_torrent(self, id):
return self.browser.get_torrent(id)
def get_torrent_file(self, id):
torrent = self.browser.get_torrent(id)
if not torrent:
return None
return self.browser.openurl(torrent.url.encode('utf-8')).read()
def iter_torrents(self, pattern):
return self.browser.iter_torrents(pattern)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/gazelle/browser.py 0000664 0000000 0000000 00000004346 11430720725 0027605 0 ustar 00root root 0000000 0000000 # -*- 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.tools.browser import BaseBrowser
from .pages.index import IndexPage, LoginPage
from .pages.torrents import TorrentsPage
__all__ = ['GazelleBrowser']
class GazelleBrowser(BaseBrowser):
DOMAIN = 'ssl.what.cd'
PROTOCOL = 'https'
PAGES = {'https?://%s/?(index.php)?': IndexPage,
'https?://%s/login.php': LoginPage,
'https?://%s/torrents.php.*': TorrentsPage,
}
def __init__(self, protocol, domain, *args, **kwargs):
self.DOMAIN = domain
self.PROTOCOL = protocol
self.PAGES = {}
for key, value in GazelleBrowser.PAGES.iteritems():
self.PAGES[key % domain] = value
BaseBrowser.__init__(self, *args, **kwargs)
def login(self):
if not self.is_on_page(LoginPage):
self.home()
self.page.login(self.username, self.password)
def is_logged(self):
if not self.page or self.is_on_page(LoginPage):
return False
if self.is_on_page(IndexPage):
return self.page.is_logged()
return True
def home(self):
return self.location('%s://%s/login.php' % (self.PROTOCOL, self.DOMAIN))
def iter_torrents(self, pattern):
self.location(self.buildurl('/torrents.php', searchstr=pattern))
assert self.is_on_page(TorrentsPage)
return self.page.iter_torrents()
def get_torrent(self, id):
self.location('/torrents.php?torrentid=%s' % id)
assert self.is_on_page(TorrentsPage)
return self.page.get_torrent(id)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/gazelle/pages/ 0000775 0000000 0000000 00000000000 11430720725 0026640 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/gazelle/pages/__init__.py 0000664 0000000 0000000 00000000000 11430720725 0030737 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/gazelle/pages/index.py 0000664 0000000 0000000 00000002151 11430720725 0030320 0 ustar 00root root 0000000 0000000 # -*- 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.tools.browser import BasePage
__all__ = ['IndexPage', 'LoginPage']
class IndexPage(BasePage):
def is_logged(self):
return 'id' in self.document.find('body').attrib
class LoginPage(BasePage):
def login(self, login, password):
self.browser.select_form(nr=0)
self.browser['username'] = login
self.browser['password'] = password
self.browser.submit()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/gazelle/pages/torrents.py 0000664 0000000 0000000 00000014046 11430720725 0031077 0 ustar 00root root 0000000 0000000 # -*- 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 re
from logging import warning
from weboob.tools.misc import html2text
from weboob.tools.browser import BasePage
from weboob.capabilities.torrent import Torrent
__all__ = ['TorrentsPage']
class TorrentsPage(BasePage):
TORRENTID_REGEXP = re.compile('torrents\.php\?action=download&id=(\d+)')
def unit(self, n, u):
m = {'KB': 1024,
'MB': 1024*1024,
'GB': 1024*1024*1024,
'TB': 1024*1024*1024*1024,
}
return float(n.replace(',', '')) * m.get(u, 1)
def format_url(self, url):
return '%s://%s/%s' % (self.browser.PROTOCOL,
self.browser.DOMAIN,
url)
def iter_torrents(self):
table = self.document.getroot().cssselect('table.torrent_table')
if not table:
table = self.document.getroot().cssselect('table#browse_torrent_table')
if table:
table = table[0]
current_group = None
for tr in table.findall('tr'):
if tr.attrib.get('class', '') == 'group':
tds = tr.findall('td')
current_group = u''
div = tds[-6]
if div.getchildren()[0].tag == 'div':
div = div.getchildren()[0]
for a in div.findall('a'):
if not a.text:
continue
if current_group:
current_group += ' - '
current_group += a.text
elif tr.attrib.get('class', '').startswith('group_torrent') or \
tr.attrib.get('class', '').startswith('torrent'):
tds = tr.findall('td')
title = current_group
if len(tds) == 7:
# Under a group
i = 0
elif len(tds) in (8,9):
# An alone torrent
i = len(tds) - 7
else:
# Useless title
continue
if title:
title += u' (%s)' % tds[i].find('a').text
else:
title = tds[i].find('a').text
url = tds[i].find('span').find('a').attrib['href']
id = self.TORRENTID_REGEXP.match(url)
if not id:
continue
id = id.group(1)
size = self.unit(*tds[i+3].text.split())
seeders = int(tds[i+5].text)
leechers = int(tds[i+6].text)
torrent = Torrent(id,
title,
url=self.format_url(url),
size=size,
seeders=seeders,
leechers=leechers)
yield torrent
else:
print tr.attrib
def get_torrent(self, id):
table = self.document.getroot().cssselect('div.thin')
if not table:
warning('No div.thin found')
return None
h2 = table[0].find('h2')
title = h2.text or ''
if h2.find('a') != None:
title += h2.find('a').text + h2.find('a').tail
torrent = Torrent(id, title)
table = self.document.getroot().cssselect('table.torrent_table')
if not table:
warning('No table found')
return None
for tr in table[0].findall('tr'):
if tr.attrib.get('class', '').startswith('group_torrent'):
tds = tr.findall('td')
if not len(tds) == 5:
continue
url = tds[0].find('span').find('a').attrib['href']
id = self.TORRENTID_REGEXP.match(url)
if not id:
warning('ID not found')
continue
id = id.group(1)
if id != torrent.id:
continue
torrent.url = self.format_url(url)
torrent.size = self.unit(*tds[1].text.split())
torrent.seeders = int(tds[3].text)
torrent.leechers = int(tds[4].text)
break
if not torrent.url:
warning('Torrent %d not found in list' % torrent.id)
return None
div = self.document.getroot().cssselect('div.main_column')
if not div:
warning('WTF')
return None
for box in div[0].cssselect('div.box'):
title = None
body = None
title_t = box.cssselect('div.head')
if title_t:
title = title_t[0].find('strong').text
body_t = box.cssselect('div.body')
if body_t:
body = html2text(self.browser.parser.tostring(body_t[0]))
if title and body:
torrent.description += '%s\n\n%s\n' % (title, body)
div = self.document.getroot().cssselect('div#files_%s' % torrent.id)
if div:
for tr in div[0].find('table'):
if tr.attrib.get('class', None) != 'colhead_dark':
torrent.files.append(tr.find('td').text)
return torrent
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/geolocip/ 0000775 0000000 0000000 00000000000 11430720725 0025717 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/geolocip/__init__.py 0000664 0000000 0000000 00000000045 11430720725 0030027 0 ustar 00root root 0000000 0000000 from .backend import GeolocIpBackend
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/geolocip/backend.py 0000664 0000000 0000000 00000005202 11430720725 0027657 0 ustar 00root root 0000000 0000000 # -*- 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 weboob.capabilities.geolocip import ICapGeolocIp, IpLocation
from weboob.tools.backend import BaseBackend
from weboob.tools.browser import BaseBrowser
__all__ = ['GeolocIpBackend']
class GeolocIpBackend(BaseBackend, ICapGeolocIp):
NAME = 'geolocip'
MAINTAINER = 'Romain Bignon'
EMAIL = 'romain@peerfuse.org'
VERSION = '0.1'
LICENSE = 'GPLv3'
DESCRIPTION = u"IP Adresses geolocalisation"
CONFIG = {'email': BaseBackend.ConfigField(description='Username on website'),
'password': BaseBackend.ConfigField(description='Password of account', is_masked=True),
}
BROWSER = BaseBrowser
def create_default_browser(self):
return self.create_browser(self.config['email'], self.config['password'])
def get_location(self, ipaddr):
with self.browser:
args = {'email': self.config['email'],
'pass': self.config['password'],
'ip': str(ipaddr)
}
content = self.browser.readurl(self.browser.buildurl('http://www.geolocalise-ip.com/api.php', **args))
tab = {}
for line in content.split('&'):
if not '=' in line:
continue
key, value = line.split('=', 1)
tab[key] = value
if 'erreur' in tab and tab['erreur'][0] == '1':
raise Exception(tab['erreur'][1:].replace('
', '').replace(' ', '\n'))
iploc = IpLocation(ipaddr)
iploc.city = tab['ville'].decode('iso-8859-15')
iploc.region = tab['region']
iploc.zipcode = tab['cp']
iploc.country = tab['pays']
iploc.lt = float(tab['lt'])
iploc.lg = float(tab['lg'])
iploc.host = tab['host']
iploc.tld = tab['tld']
iploc.isp = tab['fai']
return iploc
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/ina/ 0000775 0000000 0000000 00000000000 11430720725 0024665 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/ina/__init__.py 0000664 0000000 0000000 00000000040 11430720725 0026770 0 ustar 00root root 0000000 0000000 from .backend import InaBackend
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/ina/backend.py 0000664 0000000 0000000 00000002602 11430720725 0026626 0 ustar 00root root 0000000 0000000 # -*- 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 logging import debug
from weboob.capabilities.video import ICapVideo
from weboob.tools.backend import BaseBackend
from .browser import InaBrowser
__all__ = ['InaBackend']
class InaBackend(BaseBackend, ICapVideo):
NAME = 'ina'
MAINTAINER = 'Christophe Benz'
EMAIL = 'christophe.benz@gmail.com'
VERSION = '0.1'
DESCRIPTION = 'INA french video archives'
LICENSE = 'GPLv3'
BROWSER = InaBrowser
def get_video(self, _id):
return self.browser.get_video(_id)
def iter_search_results(self, pattern=None, sortby=ICapVideo.SEARCH_RELEVANCE, nsfw=False):
debug(u'backend ina: iter_search_results is not implemented')
return set()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/ina/browser.py 0000664 0000000 0000000 00000002210 11430720725 0026715 0 ustar 00root root 0000000 0000000 # -*- 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 weboob.tools.browser import BaseBrowser
from weboob.tools.browser.decorators import id2url
from .pages.video import VideoPage
from .video import InaVideo
__all__ = ['InaBrowser']
class InaBrowser(BaseBrowser):
DOMAIN = 'ina.fr'
PAGES = {'http://boutique\.ina\.fr/video/.+\.html': VideoPage,
}
@id2url(InaVideo.id2url)
def get_video(self, url):
self.location(url)
return self.page.video
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/ina/pages/ 0000775 0000000 0000000 00000000000 11430720725 0025764 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/ina/pages/__init__.py 0000664 0000000 0000000 00000000000 11430720725 0030063 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/ina/pages/video.py 0000664 0000000 0000000 00000005106 11430720725 0027446 0 ustar 00root root 0000000 0000000 # -*- 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 datetime
from logging import warning
import re
try:
from urlparse import parse_qs
except ImportError:
from cgi import parse_qs
from weboob.tools.browser import BasePage
from ..video import InaVideo
__all__ = ['VideoPage']
class VideoPage(BasePage):
URL_REGEXP = re.compile('http://boutique.ina.fr/video/(.+).html')
def on_loaded(self):
date, duration = self.get_date_and_duration()
self.video = InaVideo(self.get_id(),
title=self.get_title(),
url=self.get_url(),
date=date,
duration=duration,
)
def get_id(self):
m = self.URL_REGEXP.match(self.url)
if m:
return unicode(m.group(1))
warning('Unable to parse ID')
return 0
def get_date_and_duration(self):
duration_regexp = re.compile('(.+) - (.+)min(.+)s')
el = self.document.getroot().cssselect('.bloc-video-edito h3')[0]
if el is not None:
m = duration_regexp.match(el.text.strip())
if m:
day, month, year = [int(s) for s in m.group(1).split('/')]
date = datetime.datetime(year, month, day)
duration = datetime.timedelta(minutes=m.group(3), seconds=m.group(2))
return date, duration
else:
return None
def get_title(self):
el = self.document.getroot().cssselect('.bloc-video-edito h2')[0]
if el is not None:
return unicode(el.text.strip())
else:
return None
def get_url(self):
qs = parse_qs(self.document.getroot().cssselect('param[name="flashvars"]')[0].attrib['value'])
url = 'http://mp4.ina.fr/lecture/lire/id_notice/%s/token_notice/%s' % (qs['id_notice'][0], qs['token_notice'][0])
return url
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/ina/video.py 0000664 0000000 0000000 00000001563 11430720725 0026352 0 ustar 00root root 0000000 0000000 # -*- 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 weboob.capabilities.video import BaseVideo
__all__ = ['InaVideo']
class InaVideo(BaseVideo):
@classmethod
def id2url(cls, _id):
return _id
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/transilien/ 0000775 0000000 0000000 00000000000 11430720725 0026266 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/transilien/__init__.py 0000664 0000000 0000000 00000001404 11430720725 0030376 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2010 Julien Hébert, 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 .backend import TransilienBackend
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/transilien/backend.py 0000664 0000000 0000000 00000003517 11430720725 0030235 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2010 Julien Hébert, 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.travel import ICapTravel, Station, Departure
from weboob.tools.backend import BaseBackend
from .browser import Transilien
from .stations import STATIONS
class TransilienBackend(BaseBackend, ICapTravel):
NAME = 'transilien'
MAINTAINER = u'Julien Hébert'
EMAIL = 'juke@free.fr'
VERSION = '0.1'
LICENSE = 'GPLv3'
DESCRIPTION = "Transports in Paris"
BROWSER = Transilien
def iter_station_search(self, pattern):
pattern = pattern.lower()
for _id, name in STATIONS.iteritems():
if name.lower().find(pattern) >= 0:
yield Station(_id, name)
def iter_station_departures(self, station_id, arrival_id=None):
for i, d in enumerate(self.browser.iter_station_departures(station_id, arrival_id)):
departure = Departure(i, d['type'], d['time'])
departure.departure_station = d['departure']
departure.arrival_station = d['arrival']
departure.late = d['late']
departure.information = d['late_reason']
departure.plateform = d['plateform']
yield departure
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/transilien/browser.py 0000664 0000000 0000000 00000013310 11430720725 0030321 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2010 Julien Hébert, 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 datetime import datetime, date, time
import HTMLParser
from weboob.tools.browser import BaseBrowser
from weboob.tools.misc import to_unicode
from .pages.route import RoutePage
class Route(object):
"une ligne code_mission | time"
def __init__(self, code_mission, time, destination, platform):
self.code_mission = code_mission
self.time = time
self.destination = destination
self.platform = platform
def __repr__(self):
return "" % (self.code_mission,
self.time, self.destination, self.platform)
class Parser(HTMLParser.HTMLParser):
"Parse les tableaux html contenant les horaires"
def __init__(self):
HTMLParser.HTMLParser.__init__(self)
self.__table_horaires3 = False
self.__code_de_mission = False
self.__a_code_de_mission = False
self.__time = False
self.__destination = False
self.__platform = False
self.__liste_train = []
self.__liste_horaire = []
self.__liste_destination = []
self.__liste_platform = []
def parse(self, data, encoding):
self.feed(data.read())
return self
def handle_starttag(self, tag, attrs):
"execute a chaque balise ouvrante"
if (tag == 'table' and (dict(attrs)['class'] == 'horaires3')):
self.__table_horaires3 = True
elif self.__table_horaires3 and tag == 'td':
try:
self.__code_de_mission = (
dict(attrs)['headers'] == 'Code_de_mission')
self.__time = (
dict(attrs)['headers'] == 'Heure_de_passage')
self.__destination = (
dict(attrs)['headers'] == 'Destination')
self.__platform = (
dict(attrs)['headers'] == 'Voie')
except KeyError:
if dict(attrs).has_key('headers'):
raise
else:
pass
else:
self.__a_code_de_mission = (tag == 'a' and self.__code_de_mission)
def handle_data(self, data):
"execute pour chaque contenu de balise"
if self.__a_code_de_mission:
self.__liste_train.append(data.strip())
if self.__time and data.strip() != '*':
self.__liste_horaire.append(data.strip())
if self.__destination:
self.__liste_destination.append(data.strip())
if self.__platform:
self.__liste_platform.append(data.strip())
def handle_endtag(self, tag):
"execute à chaque balise fermante"
self.__a_code_de_mission ^= (self.__a_code_de_mission and tag == 'a')
self.__time ^= (self.__time and tag == 'td')
self.__destination ^= (self.__destination and tag == 'td')
self.__platform ^= (self.__platform and tag == 'td')
@property
def list_route(self):
"getter"
__list_route = []
__curseur_horaire = 0
for __i in self.__liste_train:
__list_route.append(Route(
code_mission=__i,
time=self.__liste_horaire[__curseur_horaire],
destination=self.__liste_destination[__curseur_horaire],
platform=self.__liste_platform[__curseur_horaire]
))
__curseur_horaire += 1
return __list_route
class Transilien(BaseBrowser):
DOMAIN = 'www.transilien.com'
PAGES = {'http://www\.transilien\.com/web/ITProchainsTrainsAvecDest\.do\?.*': RoutePage,
'http://www\.transilien\.com/web/ITProchainsTrains\.do\?.*': RoutePage
}
def __init__(self):
BaseBrowser.__init__(self, '', parser=Parser())
def iter_station_search(self, pattern):
pass
def iter_station_departures(self, station_id, arrival_id=None):
if arrival_id:
self.location('http://www.transilien.com/web/ITProchainsTrainsAvecDest.do?codeTr3aDepart=%s&codeTr3aDest=%s&urlModule=/site/pid/184&gareAcc=true' % (station_id, arrival_id))
else:
self.location('http://www.transilien.com/web/ITProchainsTrains.do?tr3a=%s&urlModule=/site/pid/184' % station_id)
for route in self.page.document.list_route:
_late_reason = None
try :
_time = datetime.combine(date.today(), time(*[int(x) for x in route.time.split(':')]))
except ValueError:
_time = None
_late_reason = route.time
else:
yield {'type': to_unicode(route.code_mission),
'time': _time,
'departure': to_unicode(station_id),
'arrival': to_unicode(route.destination),
'late': time(),
'late_reason': _late_reason,
'plateform': to_unicode(route.platform)}
def home(self):
pass
def login(self):
pass
def is_logged(self):
""" Do not need to be logged """
return True
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/transilien/pages/ 0000775 0000000 0000000 00000000000 11430720725 0027365 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/transilien/pages/__init__.py 0000664 0000000 0000000 00000000000 11430720725 0031464 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/transilien/pages/route.py 0000664 0000000 0000000 00000001513 11430720725 0031075 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2010 Julien Hébert, 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.tools.browser import BasePage
class RoutePage(BasePage):
def on_loaded(self):
return
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/transilien/pages/station.py 0000664 0000000 0000000 00000001316 11430720725 0031421 0 ustar 00root root 0000000 0000000 # -*- 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.
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/transilien/stations.py 0000664 0000000 0000000 00000024461 11430720725 0030513 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2010 Julien Hébert, 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.
STATIONS = {
'BXN': u'BAGNEAUX SUR LOING',
'BJR': u'BOIS LE ROI',
'BOM': u'BOURRON MARLOTTE GREZ',
'BXI': u'BOUSSY ST ANTOINE',
'BNY': u'BRUNOY',
'CES': u'CESSON',
'CLY': u'CHANTILLY GOUVIEUX',
'CLX': u'CHATELET LES HALLES',
'CBV': u'COMBS LA VILLE QUINCY',
'COE': u'CORBEIL ESSONNES',
'CL': u'CREIL',
'DDI': u'DORDIVES',
'EVR': u'EVRY',
'EVC': u'EVRY COURCOURONNES',
'FFY': u'FERRIERES FONTENAY',
'FON': u'FONTAINEBLEAU AVON',
'GDS': u'GARE DU NORD',
'GAJ': u'GARGES SARCELLES',
'GOU': u'GOUSSAINVILLE',
'GBG': u'GRAND BOURG',
'GGG': u'GRIGNY CENTRE',
'JY': u'JUVISY',
'BBN': u'LA BORNE BLANCHE',
'BFX': u'LE BRAS DE FER',
'WEE': u'LE MEE',
'VD': u'LE VERT DE MAISONS',
'LNX': u'LES NOUES',
'LIU': u'LIEUSAINT MOISSY',
'LOV': u'LOUVRES',
'MFA': u'MAISONS ALFORT ALFORTVILLE',
'MEL': u'MELUN',
'MS': u'MONTARGIS',
'MTU': u'MONTEREAU',
'KRW': u'MONTGERON CROSNE',
'MKN': u'MONTIGNY SUR LOING',
'MOR': u'MORET VENEUX LES SABLONS',
'NSP': u'NEMOURS ST PIERRE',
'OBP': u'ORANGIS BOIS DE L EPINE',
'ORY': u'ORRY LA VILLE COYE LA FORET',
'PRF': u'PIERREFITTE STAINS',
'RIS': u'RIS ORANGIS',
'ZTN': u'SAVIGNY LE TEMPLE NANDY',
'SPP': u'SOUPPES CHÂTEAU LANDON',
'SDE': u'ST DENIS',
'SF': u'ST MAMMES',
'SFD': u'STADE DE FRANCE ST DENIS',
'SUR': u'SURVILLIERS FOSSES',
'TMR': u'THOMERY',
'VGS': u'VIGNEUX SUR SEINE',
'VP': u'VILLENEUVE PRAIRIE',
'VSG': u'VILLENEUVE ST GEORGES',
'VTV': u'VILLENEUVE TRIAGE',
'VIB': u'VILLIERS LE BEL GONESSE ARNOUVILLE',
'VWC': u'VIRY CHATILLON',
'YES': u'YERRES',
'ARW': u'ARGENTEUIL',
'AEH': u'AUBERGENVILLE ELISABETHVILLE',
'BEC': u'BECON LES BRUYERES',
'BCO': u'BOIS COLOMBES',
'CWJ': u'CHAVILLE RIVE DROITE',
'CLL': u'CLICHY LEVALLOIS',
'CBK': u'COLOMBES',
'CSH': u'CONFLANS SAINTE HONORINE',
'CPA': u'CORMEILLES EN PARISIS',
'KOU': u'COURBEVOIE',
'EPO': u'EPONE MEZIERES',
'ERA': u'ERAGNY NEUVILLE',
'ERE': u'ERMONT EAUBONNE',
'PSL': u'GARE ST LAZARE',
'HRY': u'HERBLAY',
'HAR': u'HOUILLES CARRIERES SUR SEINE',
'LDU': u'LA DEFENSE GARE SNCF',
'FMY': u'LA FRETTE MONTIGNY',
'LGK': u'LA GARENNE COLOMBES',
'LSD': u'LE STADE',
'VDO': u'LE VAL D OR',
'KVE': u'LES CLAIRIERES DE VERNEUIL',
'LMU': u'LES MUREAUX',
'LWA': u'LES VALLEES',
'MLF': u'MAISONS LAFFITTE',
'MTE': u'MANTES LA JOLIE',
'MTQ': u'MANTES STATION',
'MFL': u'MONTREUIL',
'NUN': u'NANTERRE UNIVERSITE',
'PSY': u'POISSY',
'PTC': u'PONT CARDINET',
'PSE': u'PONTOISE',
'PTX': u'PUTEAUX',
'SNN': u'SANNOIS',
'SVL': u'SARTROUVILLE',
'VDV': u'SEVRES VILLE D AVRAY',
'SCD': u'ST CLOUD',
'XOA': u'ST OUEN L AUMONE QUARTIER DE L EGLIS',
'MVH': u'SURESNES MONT VALERIEN',
'VDA': u'VAL D ARGENTEUIL',
'VET': u'VERNOUILLET VERNEUIL',
'VRD': u'VERSAILLES RIVE DROITE',
'VSW': u'VILLENNES SUR SEINE',
'VFD': u'VIROFLAY RIVE DROITE',
'ABL': u'ABLON',
'ARP': u'ARPAJON',
'ATH': u'ATHIS MONS',
'BFM': u'BIBLIOTHEQUE F. MITTERRAND',
'BIS': u'BIEVRES',
'BVI': u'BOULEVARD VICTOR',
'BY': u'BRETIGNY',
'BIH': u'BREUILLET BRUYERES LE CHATEL',
'BRW': u'BREUILLET VILLAGE',
'CPM': u'CHAMP DE MARS TOUR EIFFEL',
'CHV': u'CHAVILLE VELIZY',
'CAZ': u'CHILLY MAZARIN',
'CLR': u'CHOISY LE ROI',
'D': u'DOURDAN',
'ELY': u'EGLY',
'EYO': u'EPINAY SUR ORGE',
'GBI': u'GRAVIGNY BALIZY',
'IGY': u'IGNY',
'INV': u'INVALIDES',
'ISY': u'ISSY',
'ISP': u'ISSY VAL DE SEINE',
'IV': u'IVRY SUR SEINE',
'JVL': u'JAVEL',
'JAS': u'JOUY EN JOSAS',
'NG': u'LA NORVILLE ST GERMAIN LES ARPAJON',
'LAD': u'LES ARDOINES',
'LJU': u'LONGJUMEAU',
'MPU': u'MASSY PALAISEAU RER C',
'MFY': u'MEUDON VAL FLEURY',
'MDS': u'MUSEE D ORSAY',
'PJ': u'PETIT JOUY LES LOGES',
'PV': u'PETIT VAUX',
'PDM': u'PONT DE L ALMA',
'POA': u'PORCHEFONTAINE',
'SAO': u'SAVIGNY SUR ORGE',
'SXE': u'SERMAISE',
'SCW': u'ST CHERON',
'SHL': u'ST MICHEL NOTRE DAME',
'SHO': u'ST MICHEL SUR ORGE',
'SXG': u'STE GENEVIEVE DES BOIS',
'VBO': u'VAUBOYEN',
'VC': u'VERSAILLES CHANTIERS',
'VRG': u'VERSAILLES R G CHATEAU DE VERSAILLES',
'VRI': u'VILLENEUVE LE ROI',
'VFG': u'VIROFLAY RIVE GAUCHE',
'VY': u'VITRY SUR SEINE',
'BSO': u'BOURAY',
'CHK': u'CHAMARANDE',
'ETP': u'ETAMPES',
'ETY': u'ETRECHY',
'PZB': u'GARE D\'AUSTERLITZ',
'LYO': u'LARDY',
'MSX': u'MAROLLES EN HUREPOIX',
'SME': u'ST MARTIN D ETAMPES',
'CME': u'CHAMPAGNE SUR SEINE',
'CJR': u'CHARTRETTES',
'HER': u'HERICY',
'GPA': u'LA GRANDE PAROISSE',
'LYQ': u'LIVRY SUR SEINE',
'VSS': u'VERNOU SUR SEINE',
'VUN': u'VULAINES SUR SEINE SAMOREAU',
'PLY': u'GARE DE LYON',
'FPO': u'FONTAINE LE PORT',
'CJN': u'CHANGIS ST JEAN',
'CTH': u'CHATEAU THIERRY',
'CSG': u'CHELLES GOURNAY',
'CYZ': u'CHEZY SUR MARNE',
'CO': u'COULOMMIERS',
'EY': u'ESBLY',
'FMP': u'FAREMOUTIERS POMMEUSE',
'GCM': u'GUERARD LA CELLE SUR MORIN',
'LFJ': u'LA FERTE SOUS JOUARRE',
'LGY': u'LAGNY THORIGNY',
'MLB': u'MARLES EN BRIE',
'MEA': u'MEAUX',
'MOF': u'MORTCERF',
'MXK': u'MOUROUX',
'NAU': u'NANTEUIL SAACY',
'NAA': u'NOGENT L ARTAUD CHARLY',
'TOU': u'TOURNAN',
'TLP': u'TRILPORT',
'VAI': u'VAIRES TORCY',
'AEE': u'ASNIERES',
'AUU': u'AUBER',
'BQA': u'BRY SUR MARNE',
'BXG': u'BUSSY ST GEORGES',
'CGP': u'CHARLES DE GAULLE-ETOILE',
'GYN': u'GARE DE LYON',
'GAW': u'LA DEFENSE RER A',
'LQN': u'LOGNES',
'MVC': u'MARNE LA VALLEE CHESSY',
'NAF': u'NANTERRE PREFECTURE SNCF',
'NTN': u'NATION',
'NYP': u'NEUILLY PLAISANCE',
'NSL': u'NOISIEL',
'NYC': u'NOISY CHAMPS',
'NYG': u'NOISY LE GRAND MONT D EST',
'TOC': u'TORCY MARNE LA VALLEE',
'VDE': u'VAL D EUROPE',
'VFR': u'VAL DE FONTENAY RER A',
'VNC': u'VINCENNES',
'CJV': u'CERGY LE HAUT',
'CYP': u'CERGY PREFECTURE',
'CYC': u'CERGY ST CHRISTOPHE',
'CFD': u'CONFLANS FIN D OISE',
'NUE': u'NEUVILLE UNIVERSITE',
'RYR': u'AEROPORT CDG 2 TGV',
'ATW': u'ANTONY',
'ALC': u'AUBERVILLIERS LA COURNEUVE',
'AB': u'AULNAY SOUS BOIS',
'BAM': u'BLANC MESNIL',
'BQQ': u'BOURG LA REINE',
'BVJ': u'BURES SUR YVETTE',
'CUF': u'CITE UNIVERSITAIRE',
'CVW': u'COURCELLE SUR YVETTE',
'DFR': u'DENFERT ROCHEREAU',
'DRN': u'DRANCY',
'FMN': u'FONTAINE MICHALON',
'GIF': u'GIF SUR YVETTE',
'XBY': u'LA CROIX DE BERNY',
'HAQ': u'LA HACQUINIERE',
'LPN': u'LA PLAINE STADE DE FRANCE',
'LBT': u'LE BOURGET',
'GUW': u'LE GUICHET',
'BQC': u'LES BACONNETS',
'LZV': u'LOZERE',
'LXJ': u'LUXEMBOURG',
'MP': u'MASSY PALAISEAU RER B',
'MVP': u'MASSY VERRIERES RER B',
'ORS': u'ORSAY VILLE',
'PAX': u'PALAISEAU',
'PAW': u'PALAISEAU VILLEBON',
'PCX': u'PARC DE SCEAUX',
'PEX': u'PARC DES EXPOSITIONS',
'PWR': u'PORT ROYAL',
'BDE': u'SEVRAN BEAUDOTTES',
'XND': u'ST MICHEL NOTRE DAME',
'SNM': u'ST REMY LES CHEVREUSE',
'VPN': u'VILLEPINTE',
'RSY': u'AEROPORT CDG 1',
'ARK': u'ARCUEIL CACHAN',
'BGK': u'BAGNEUX',
'GTL': u'GENTILLY',
'LJA': u'LAPLACE',
'CVF': u'CHANTELOUP LES VIGNES',
'GGV': u'GARGENVILLE',
'IPO': u'ISSOU PORCHEVILLE',
'JUZ': u'JUZIERS',
'LIM': u'LIMAY',
'MHD': u'MEULAN HARDRICOURT',
'TPA': u'THUN LE PARADIS',
'TSS': u'TRIEL SUR SEINE',
'VXS': u'VAUX SUR SEINE',
'FNR': u'FONTENAY AUX ROSES',
'MY': u'MITRY CLAYE',
'RNS': u'ROBINSON',
'SKX': u'SCEAUX',
'SEV': u'SEVRAN LIVRY',
'VGL': u'VERT GALANT',
'VII': u'VILLEPARISIS MITRY LE NEUF',
'BOF': u'BOUFFEMONT MOISSELLES',
'DEU': u'DEUIL MONTMAGNY',
'DMO': u'DOMONT',
'ECZ': u'ECOUEN EZANVILLE',
'EPV': u'EPINAY VILLETANEUSE',
'PNB': u'GARE DU NORD',
'GRL': u'GROSLAY',
'LUZ': u'LUZARCHES',
'MSO': u'MONTSOULT MAFFLIERS',
'SLL': u'SARCELLES ST BRICE',
'SWY': u'SEUGY',
'VMS': u'VIARMES',
'VW': u'VILLAINES',
'CH': u'CHARTRES',
'CVI': u'CHAVILLE RIVE GAUCHE',
'CMA': u'CLAMART',
'CGW': u'COIGNIERES',
'DX': u'DREUX',
'EPN': u'EPERNON',
'FAF': u'FONTENAY LE FLEURY',
'GAQ': u'GARANCIERES LA QUEUE',
'GZA': u'GAZERAN',
'HOA': u'HOUDAN',
'JOY': u'JOUY',
'VYL': u'LA VERRIERE',
'LPE': u'LE PERRAY',
'LSI': u'LES ESSARTS LE ROI',
'MTN': u'MAINTENON',
'MBR': u'MARCHEZAIS BROUE',
'MDN': u'MEUDON',
'MLM': u'MONTFORT L AMAURY MERE',
'OGB': u'ORGERUS BEHOUST',
'PMP': u'PARIS MONTPARNASSE',
'PG': u'PLAISIR GRIGNON',
'PIE': u'PLAISIR LES CLAYES',
'RBT': u'RAMBOUILLET',
'SVR': u'SEVRES RIVE GAUCHE',
'SCR': u'ST CYR',
'SPI': u'ST PIAT',
'SQY': u'ST QUENTIN EN YVELINES',
'TAE': u'TACOIGNIERES RICHEBOURG',
'TVO': u'TRAPPES',
'VMK': u'VANVES MALAKOFF',
'VEP': u'VILLEPREUX LES CLAYES',
'VEH': u'VILLIERS NEAUPHLE PONTCHARTRAIN',
'CEG': u'CHAMP DE COURSES D ENGHIEN',
'CPO': u'CHAMPAGNE SUR OISE',
'EN': u'ENGHIEN LES BAINS',
'ERT': u'ERMONT EAUBONNE',
'ERM': u'ERMONT HALTE',
'FPN': u'FREPILLON',
'GNX': u'GROS NOYER ST PRIX',
'IAP': u'L ISLE ADAM PARMAIN',
'LBJ': u'LA BARRE ORMESSON',
'MLV': u'MERIEL',
'MWO': u'MERY SUR OISE',
'PEB': u'PERSAN BEAUMONT',
'SLF': u'ST LEU LA FORET',
'TVY': u'TAVERNY',
'VMD': u'VALMONDOIS',
'VCX': u'VAUCELLES',
'MJM': u'MAREIL SUR MAULDRE',
'MAE': u'MAULE',
'NZL': u'NEZEL AULNAY',
}
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/yahoo/ 0000775 0000000 0000000 00000000000 11430720725 0025235 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/yahoo/__init__.py 0000664 0000000 0000000 00000001357 11430720725 0027354 0 ustar 00root root 0000000 0000000 # -*- 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 .backend import YahooBackend
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/yahoo/backend.py 0000664 0000000 0000000 00000004463 11430720725 0027205 0 ustar 00root root 0000000 0000000 # -*- 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 xml.dom import minidom
# TODO store datetime objects instead of strings
# from datetime import datetime
from weboob.capabilities.weather import ICapWeather, CityNotFound, Current, Forecast
from weboob.tools.backend import BaseBackend
__all__ = ['YahooBackend']
class YahooBackend(BaseBackend, ICapWeather):
NAME = 'yahoo'
MAINTAINER = 'Romain Bignon'
EMAIL = 'romain@peerfuse.org'
VERSION = '0.1'
DESCRIPTION = 'Yahoo'
LICENSE = 'GPLv3'
WEATHER_URL = 'http://weather.yahooapis.com/forecastrss?w=%s&u=%s'
def iter_city_search(self, pattern):
raise NotImplementedError()
def _get_weather_dom(self, city_id):
handler = urllib2.urlopen(self.WEATHER_URL % (city_id, 'c'))
dom = minidom.parse(handler)
handler.close()
if not dom.getElementsByTagName('yweather:condition'):
raise CityNotFound()
return dom
def get_current(self, city_id):
dom = self._get_dom(city_id)
current = dom.getElementsByTagName('yweather:condition')[0]
return Current(current.getAttribute('date'), int(current.getAttribute('temp')), current.getAttribute('text'), 'C')
def iter_forecast(self, city_id):
dom = self._get_dom(city_id)
for forecast in dom.getElementsByTagName('yweather:forecast'):
yield Forecast(forecast.getAttribute('date'),
int(forecast.getAttribute('low')),
int(forecast.getAttribute('high')),
forecast.getAttribute('text'),
'C',
)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youjizz/ 0000775 0000000 0000000 00000000000 11430720725 0025641 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youjizz/__init__.py 0000664 0000000 0000000 00000001362 11430720725 0027754 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2010 Roger Philibert
#
# 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 .backend import YoujizzBackend
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youjizz/backend.py 0000664 0000000 0000000 00000004011 11430720725 0027576 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2010 Roger Philibert
#
# 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 weboob.capabilities.video import ICapVideo
from weboob.tools.backend import BaseBackend
from .browser import YoujizzBrowser
from .video import YoujizzVideo
__all__ = ['YoujizzBackend']
class YoujizzBackend(BaseBackend, ICapVideo):
NAME = 'youjizz'
MAINTAINER = 'Roger Philibert'
EMAIL = 'roger.philibert@gmail.com'
VERSION = '0.1'
DESCRIPTION = 'Youjizz videos website'
LICENSE = 'GPLv3'
BROWSER = YoujizzBrowser
def get_video(self, _id):
with self.browser:
video = self.browser.get_video(_id)
return video
def iter_search_results(self, pattern=None, sortby=ICapVideo.SEARCH_RELEVANCE, nsfw=False):
if not nsfw:
return set()
with self.browser:
return self.browser.iter_search_results(pattern)
def fill_video(self, video, fields):
if fields != ['thumbnail']:
# if we don't want only the thumbnail, we probably want also every fields
with self.browser:
video = self.browser.get_video(YoujizzVideo.id2url(video.id), video)
if 'thumbnail' in fields:
with self.browser:
video.thumbnail.data = self.browser.readurl(video.thumbnail.url)
return video
OBJECTS = {YoujizzVideo: fill_video}
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youjizz/browser.py 0000664 0000000 0000000 00000003330 11430720725 0027675 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2010 Roger Philibert
#
# 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 urllib
from weboob.tools.browser import BaseBrowser
from weboob.tools.browser.decorators import check_domain, id2url
from .pages.index import IndexPage
from .pages.video import VideoPage
from .video import YoujizzVideo
__all__ = ['YoujizzBrowser']
class YoujizzBrowser(BaseBrowser):
DOMAIN = 'youjizz.com'
ENCODING = None
PAGES = {r'http://.*youjizz\.com/?': IndexPage,
r'http://.*youjizz\.com/index.php': IndexPage,
r'http://.*youjizz\.com/search/(?P.+)\.html': IndexPage,
r'http://.*youjizz\.com/videos/(?P.+)\.html': VideoPage,
}
@id2url(YoujizzVideo.id2url)
def get_video(self, url, video=None):
self.location(url)
return self.page.get_video(video)
def iter_search_results(self, pattern):
if not pattern:
self.home()
else:
self.location('/search/%s-1.html' % (urllib.quote_plus(pattern)))
assert self.is_on_page(IndexPage)
return self.page.iter_videos()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youjizz/pages/ 0000775 0000000 0000000 00000000000 11430720725 0026740 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youjizz/pages/__init__.py 0000664 0000000 0000000 00000000000 11430720725 0031037 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youjizz/pages/index.py 0000664 0000000 0000000 00000004000 11430720725 0030413 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2010 Roger Philibert
#
# 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 datetime
import re
from weboob.tools.browser import BasePage
from weboob.tools.parsers.lxmlparser import select, SelectElementException
from ..video import YoujizzVideo
__all__ = ['IndexPage']
class IndexPage(BasePage):
def iter_videos(self):
span_list = select(self.document.getroot(), 'span#miniatura')
for span in span_list:
a = select(span, 'a', 1)
url = a.attrib['href']
_id = re.sub(r'/videos/(.+)\.html', r'\1', url)
thumbnail_url = span.find('.//img').attrib['src']
title_el = select(span, 'span#title1', 1)
title = title_el.text.strip()
time_span = select(span, 'span.thumbtime span', 1)
time_txt = time_span.text.strip()
if time_txt == 'N/A':
minutes, seconds = 0, 0
elif ':' in time_txt:
minutes, seconds = (int(v) for v in time_txt.split(':'))
else:
raise SelectElementException('Unable to parse the video duration: %s' % time_txt)
yield YoujizzVideo(_id,
title=title,
duration=datetime.timedelta(minutes=minutes, seconds=seconds),
thumbnail_url=thumbnail_url,
)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youjizz/pages/video.py 0000664 0000000 0000000 00000004243 11430720725 0030423 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2010 Roger Philibert
#
# 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 datetime
import lxml.html
import re
from weboob.tools.browser import BasePage
from weboob.tools.misc import to_unicode
from weboob.tools.parsers.lxmlparser import select, SelectElementException
from ..video import YoujizzVideo
__all__ = ['VideoPage']
class VideoPage(BasePage):
def get_video(self, video=None):
_id = to_unicode(self.group_dict['id'])
if video is None:
video = YoujizzVideo(_id)
title_el = select(self.document.getroot(), 'title', 1)
video.title = to_unicode(title_el.text.strip())
# youjizz HTML is crap, we must parse it with regexps
data = lxml.html.tostring(self.document.getroot())
m = re.search(r'.*?Runtime.*? (.+?)', data)
try:
if m:
minutes, seconds = (int(v) for v in to_unicode(m.group(1).strip()).split(':'))
video.duration = datetime.timedelta(minutes=minutes, seconds=seconds)
else:
raise Exception()
except Exception:
raise SelectElementException('Could not retrieve video duration')
video_file_urls = re.findall(r'"(http://media[^ ,]+\.flv)"', data)
if len(video_file_urls) == 0:
raise SelectElementException('Video URL not found')
elif len(video_file_urls) > 1:
raise SelectElementException('Many video file URL found')
else:
video.url = video_file_urls[0]
return video
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youjizz/video.py 0000664 0000000 0000000 00000002032 11430720725 0027316 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2010 Roger Philibert
#
# 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 BaseVideo
__all__ = ['YoujizzVideo']
class YoujizzVideo(BaseVideo):
def __init__(self, *args, **kwargs):
BaseVideo.__init__(self, *args, **kwargs)
self.nsfw = True
@classmethod
def id2url(cls, _id):
return 'http://www.youjizz.com/videos/%s.html' % _id
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youporn/ 0000775 0000000 0000000 00000000000 11430720725 0025631 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youporn/__init__.py 0000664 0000000 0000000 00000000044 11430720725 0027740 0 ustar 00root root 0000000 0000000 from .backend import YoupornBackend
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youporn/backend.py 0000664 0000000 0000000 00000004064 11430720725 0027576 0 ustar 00root root 0000000 0000000 # -*- 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 weboob.capabilities.video import ICapVideo
from weboob.tools.backend import BaseBackend
from .browser import YoupornBrowser
from .video import YoupornVideo
__all__ = ['YoupornBackend']
class YoupornBackend(BaseBackend, ICapVideo):
NAME = 'youporn'
MAINTAINER = 'Romain Bignon'
EMAIL = 'romain@peerfuse.org'
VERSION = '0.1'
DESCRIPTION = 'Youporn videos website'
LICENSE = 'GPLv3'
BROWSER = YoupornBrowser
def get_video(self, _id):
with self.browser:
return self.browser.get_video(_id)
SORTBY = ['relevance', 'rating', 'views', 'time']
def iter_search_results(self, pattern=None, sortby=ICapVideo.SEARCH_RELEVANCE, nsfw=False):
if not nsfw:
return set()
with self.browser:
return self.browser.iter_search_results(pattern, self.SORTBY[sortby])
def fill_video(self, video, fields):
if fields != ['thumbnail']:
# if we don't want only the thumbnail, we probably want also every fields
with self.browser:
video = self.browser.get_video(YoupornVideo.id2url(video.id), video)
if 'thumbnail' in fields:
with self.browser:
video.thumbnail.data = self.browser.readurl(video.thumbnail.url)
return video
OBJECTS = {YoupornVideo: fill_video}
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youporn/browser.py 0000664 0000000 0000000 00000003304 11430720725 0027666 0 ustar 00root root 0000000 0000000 # -*- 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.tools.browser import BaseBrowser
from weboob.tools.browser.decorators import id2url
from .pages.index import IndexPage
from .pages.video import VideoPage
from .video import YoupornVideo
__all__ = ['YoupornBrowser']
class YoupornBrowser(BaseBrowser):
DOMAIN = 'youporn.com'
ENCODING = None
PAGES = {r'http://[w\.]*youporn\.com/?': IndexPage,
r'http://[w\.]*youporn\.com/search.*': IndexPage,
r'http://[w\.]*youporn\.com/watch/(?P.+)': VideoPage,
r'http://[w\.]*youporngay\.com:80/watch/(?P.+)': VideoPage,
}
@id2url(YoupornVideo.id2url)
def get_video(self, url, video=None):
self.location(url)
return self.page.get_video(video)
def iter_search_results(self, pattern, sortby):
if not pattern:
self.home()
else:
self.location(self.buildurl('/search/%s' % sortby, query=pattern))
assert self.is_on_page(IndexPage)
return self.page.iter_videos()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youporn/pages/ 0000775 0000000 0000000 00000000000 11430720725 0026730 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youporn/pages/__init__.py 0000664 0000000 0000000 00000000000 11430720725 0031027 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youporn/pages/base.py 0000664 0000000 0000000 00000002244 11430720725 0030216 0 ustar 00root root 0000000 0000000 # -*- 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.tools.mech import ClientForm
ControlNotFoundError = ClientForm.ControlNotFoundError
from mechanize import FormNotFoundError
from weboob.tools.browser import BasePage
__all__ = ['PornPage']
class PornPage(BasePage):
def on_loaded(self):
try:
self.browser.select_form(nr=0)
self.browser.submit(name='user_choice')
return False
except (ControlNotFoundError, FormNotFoundError):
return True
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youporn/pages/index.py 0000664 0000000 0000000 00000004714 11430720725 0030417 0 ustar 00root root 0000000 0000000 # -*- 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 datetime
from .base import PornPage
from ..video import YoupornVideo
__all__ = ['IndexPage']
class IndexPage(PornPage):
def iter_videos(self):
uls = self.document.getroot().cssselect("ul[class=clearfix]")
if not uls:
return
for ul in uls:
for li in ul.findall('li'):
a = li.find('a')
if a is None or a.find('img') is None:
continue
thumbnail_url = a.find('img').attrib['src']
h1 = li.find('h1')
a = h1.find('a')
if a is None:
continue
url = a.attrib['href']
_id = url[len('/watch/'):]
_id = _id[:_id.find('/')]
title = a.text.strip()
minutes = seconds = 0
div = li.cssselect('div[class=duration_views]')
if div:
h2 = div[0].find('h2')
minutes = int(h2.text.strip())
seconds = int(h2.find('span').tail.strip())
rating = 0
rating_max = 0
div = li.cssselect('div[class=rating]')
if div:
p = div[0].find('p')
rating = float(p.text.strip())
rating_max = float(p.find('span').text.strip()[2:])
yield YoupornVideo(int(_id),
title=title,
rating=rating,
rating_max=rating_max,
duration=datetime.timedelta(minutes=minutes, seconds=seconds),
thumbnail_url=thumbnail_url,
)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youporn/pages/video.py 0000664 0000000 0000000 00000006323 11430720725 0030414 0 ustar 00root root 0000000 0000000 # -*- 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 re
import datetime
from weboob.tools.parsers.lxmlparser import select
from .base import PornPage
from ..video import YoupornVideo
class VideoPage(PornPage):
def get_video(self, video=None):
if not PornPage.on_loaded(self):
return
if video is None:
video = YoupornVideo(self.group_dict['id'])
video.title = self.get_title()
video.url = self.get_url()
self.set_details(video)
return video
def get_url(self):
download_div = select(self.document.getroot(), '#download', 1)
a = select(download_div, 'a', 1)
return a.attrib['href']
def get_title(self):
element = select(self.document.getroot(), '#videoArea h1', 1)
return unicode(element.getchildren()[0].tail).strip()
DATE_REGEXP = re.compile("\w+ (\w+) (\d+) (\d+):(\d+):(\d+) (\d+)")
MONTH2I = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
def set_details(self, v):
details_div = select(self.document.getroot(), '#details', 1)
for li in details_div.getiterator('li'):
span = li.find('span')
name = span.text.strip()
value = span.tail.strip()
if name == 'Duration:':
seconds = minutes = 0
for word in value.split():
if word.endswith('min'):
minutes = int(word[:word.find('min')])
elif word.endswith('sec'):
seconds = int(word[:word.find('sec')])
v.duration = datetime.timedelta(minutes=minutes, seconds=seconds)
elif name == 'Submitted:':
author = li.find('i')
if author is None:
author = li.find('a')
if author is None:
v.author = value
else:
v.author = author.text
elif name == 'Rating:':
r = value.split()
v.rating = float(r[0])
v.rating_max = float(r[2])
elif name == 'Date:':
m = self.DATE_REGEXP.match(value)
if m:
month = self.MONTH2I.index(m.group(1))
day = int(m.group(2))
hour = int(m.group(3))
minute = int(m.group(4))
second = int(m.group(5))
year = int(m.group(6))
v.date = datetime.datetime(year, month, day, hour, minute, second)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youporn/video.py 0000664 0000000 0000000 00000002135 11430720725 0027312 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2010 Roger Philibert
#
# 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 BaseVideo
__all__ = ['YoupornVideo']
class YoupornVideo(BaseVideo):
def __init__(self, *args, **kwargs):
BaseVideo.__init__(self, *args, **kwargs)
self.nsfw = True
@classmethod
def id2url(cls, _id):
if _id.isdigit():
return 'http://www.youporn.com/watch/%d' % int(_id)
else:
return None
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youtube/ 0000775 0000000 0000000 00000000000 11430720725 0025612 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youtube/__init__.py 0000664 0000000 0000000 00000001362 11430720725 0027725 0 ustar 00root root 0000000 0000000 # -*- 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 .backend import YoutubeBackend
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youtube/backend.py 0000664 0000000 0000000 00000006026 11430720725 0027557 0 ustar 00root root 0000000 0000000 # -*- 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 __future__ import with_statement
import datetime
from weboob.capabilities.video import ICapVideo
from weboob.tools.backend import BaseBackend
from .browser import YoutubeBrowser
from .video import YoutubeVideo
__all__ = ['YoutubeBackend']
class YoutubeBackend(BaseBackend, ICapVideo):
NAME = 'youtube'
MAINTAINER = 'Christophe Benz'
EMAIL = 'christophe.benz@gmail.com'
VERSION = '0.1'
DESCRIPTION = 'Youtube videos website'
LICENSE = 'GPLv3'
BROWSER = YoutubeBrowser
def get_video(self, _id):
with self.browser:
return self.browser.get_video(_id)
def iter_search_results(self, pattern=None, sortby=ICapVideo.SEARCH_RELEVANCE, nsfw=False):
import gdata.youtube.service
yt_service = gdata.youtube.service.YouTubeService()
query = gdata.youtube.service.YouTubeVideoQuery()
query.orderby = ('relevance', 'rating', 'viewCount', 'published')[sortby]
query.racy = 'include' if nsfw else 'exclude'
if pattern:
query.categories.extend('/%s' % search_term.lower().encode('utf-8') for search_term in pattern.split())
feed = yt_service.YouTubeQuery(query)
for entry in feed.entry:
if entry.media.name:
author = entry.media.name.text.decode('utf-8').strip()
else:
author = None
video = YoutubeVideo(entry.id.text.split('/')[-1].decode('utf-8'),
title=entry.media.title.text.decode('utf-8').strip(),
author=author,
duration=datetime.timedelta(seconds=int(entry.media.duration.seconds.decode('utf-8').strip())),
thumbnail_url=entry.media.thumbnail[0].url.decode('utf-8').strip(),
)
yield video
def fill_video(self, video, fields):
if fields != ['thumbnail']:
# if we don't want only the thumbnail, we probably want also every fields
with self.browser:
video = self.browser.get_video(YoutubeVideo.id2url(video.id), video)
if 'thumbnail' in fields:
with self.browser:
video.thumbnail.data = self.browser.readurl(video.thumbnail.url)
return video
OBJECTS = {YoutubeVideo: fill_video}
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youtube/browser.py 0000664 0000000 0000000 00000002616 11430720725 0027654 0 ustar 00root root 0000000 0000000 # -*- 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.tools.browser import BaseBrowser
from weboob.tools.browser.decorators import id2url
from .pages import ForbiddenVideoPage, VerifyAgePage, VideoPage
from .video import YoutubeVideo
__all__ = ['YoutubeBrowser']
class YoutubeBrowser(BaseBrowser):
DOMAIN = u'youtube.com'
ENCODING = None
PAGES = {r'.*youtube\.com/watch\?v=(?P.+)': VideoPage,
r'.*youtube\.com/index\?ytsession=.+': ForbiddenVideoPage,
r'.*youtube\.com/verify_age\?next_url=(?P.+)': VerifyAgePage,
}
@id2url(YoutubeVideo.id2url)
def get_video(self, url, video=None):
self.location(url)
return self.page.get_video(video)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youtube/pages.py 0000664 0000000 0000000 00000004512 11430720725 0027265 0 ustar 00root root 0000000 0000000 # -*- 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.
import re
from weboob.tools.browser import BasePage
from weboob.tools.parsers.lxmlparser import select
from .video import YoutubeVideo
__all__ = ['ForbiddenVideo', 'ForbiddenVideoPage', 'VerifyAgePage', 'VideoPage']
class ForbiddenVideo(Exception):
pass
class ForbiddenVideoPage(BasePage):
def get_video(self, video=None):
element = select(self.document.getroot(), '.yt-alert-content', 1)
raise ForbiddenVideo(element.text.strip())
class VerifyAgePage(BasePage):
def get_video(self, video=None):
raise ForbiddenVideo('verify age not implemented')
class VideoPage(BasePage):
VIDEO_SIGNATURE_REGEX = re.compile(r'&t=([^ ,&]*)')
def get_video(self, video=None):
if video is None:
video = YoutubeVideo(self.group_dict['id'])
video.title = self.get_title()
video.url = self.get_url(video.id)
video.author = self.get_author()
return video
def get_author(self):
element = select(self.document.getroot(), 'a.watch-description-username strong', 1)
return element.text.strip()
def get_title(self):
element = select(self.document.getroot(), 'meta[name=title]', 1)
return unicode(element.attrib['content']).strip()
def get_url(self, _id):
video_signature = None
for data in self.document.getiterator('script'):
if not data.text:
continue
for m in re.finditer(self.VIDEO_SIGNATURE_REGEX, data.text):
video_signature = m.group(1)
return u'http://www.youtube.com/get_video?video_id=%s&t=%s&fmt=18' % (_id, video_signature)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/backends/youtube/video.py 0000664 0000000 0000000 00000001641 11430720725 0027274 0 ustar 00root root 0000000 0000000 # -*- 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 weboob.capabilities.video import BaseVideo
__all__ = ['YoutubeVideo']
class YoutubeVideo(BaseVideo):
@classmethod
def id2url(cls, _id):
return 'http://www.youtube.com/watch?v=%s' % _id
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/capabilities/ 0000775 0000000 0000000 00000000000 11430720725 0024775 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/capabilities/__init__.py 0000664 0000000 0000000 00000000070 11430720725 0027103 0 ustar 00root root 0000000 0000000 __import__('pkg_resources').declare_namespace(__name__)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/capabilities/bank.py 0000664 0000000 0000000 00000004377 11430720725 0026275 0 ustar 00root root 0000000 0000000 # -*- 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 sys
if sys.version_info[:2] <= (2, 5):
from weboob.tools.property import property
from .cap import ICap
__all__ = ['Account', 'AccountNotFound', 'ICapBank', 'Operation']
class AccountNotFound(Exception):
pass
class Account(object):
def __init__(self):
self.id = 0
self.label = ''
self._balance = 0.0
self._coming = 0.0
self.link_id = ''
@property
def balance(self):
return self._balance
@balance.setter
def balance(self, value):
self._balance = float(value)
@property
def coming(self):
return self._coming
@coming.setter
def coming(self, value):
self._coming = float(value)
def __repr__(self):
return u"" % (self.id, self.label)
class Operation(object):
def __init__(self):
self.date = None
self._label = u''
self._amount = 0.0
def __repr__(self):
return "" % (self.date, self.label, self.amount)
@property
def label(self):
return self._label
@label.setter
def label(self, value):
self._label = str(value)
@property
def amount(self):
return self._amount
@amount.setter
def amount(self, value):
self._amount = float(value)
class ICapBank(ICap):
def iter_accounts(self):
raise NotImplementedError()
def get_account(self, _id):
raise NotImplementedError()
def iter_operations(self, account):
raise NotImplementedError()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/capabilities/cap.py 0000664 0000000 0000000 00000001352 11430720725 0026113 0 ustar 00root root 0000000 0000000 # -*- 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.
class ICap(object):
pass
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/capabilities/chat.py 0000664 0000000 0000000 00000002403 11430720725 0026265 0 ustar 00root root 0000000 0000000 # -*- 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 datetime
from .cap import ICap
__all__ = ['ChatException', 'ICapChat']
class ChatException(Exception):
pass
class ChatMessage(object):
def __init__(self, id_from, id_to, message, date=None):
self.id_from = id_from
self.id_to = id_to
self.message = message
self.date = datetime.datetime.utcnow() if date is None else date
class ICapChat(ICap):
def iter_chat_messages(self, _id=None):
raise NotImplementedError()
def send_chat_message(self, _id, message):
raise NotImplementedError()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/capabilities/contact.py 0000664 0000000 0000000 00000006520 11430720725 0027005 0 ustar 00root root 0000000 0000000 # -*- 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 .cap import ICap
from weboob.tools.ordereddict import OrderedDict
__all__ = ['ICapContact', 'Contact']
class ProfileNode(object):
HEAD = 0x01
SECTION = 0x02
def __init__(self, name, label, value, sufix=None, flags=0):
self.name = name
self.label = label
self.value = value
self.sufix = sufix
self.flags = flags
class ContactPhoto(object):
def __init__(self, name):
self.name = name
self.url = u''
self.data = ''
self.thumbnail_url = u''
self.thumbnail_data = u''
def __iscomplete__(self):
return (self.data and (not self.thumbnail_url or self.thumbnail_data))
def __str__(self):
return self.url
def __repr__(self):
return u'' % (self.name, len(self.data), len(self.thumbnail_data))
class Contact(object):
STATUS_ONLINE = 0x001
STATUS_AWAY = 0x002
STATUS_OFFLINE = 0x004
STATUS_ALL = 0xfff
def __init__(self, id, name, status):
self.id = id
self.name = name
self.status = status
self.status_msg = u''
self.summary = u''
self.avatar = None
self.photos = OrderedDict()
self.profile = None
def set_photo(self, name, **kwargs):
if not name in self.photos:
self.photos[name] = ContactPhoto(name)
photo = self.photos[name]
for key, value in kwargs.iteritems():
setattr(photo, key, value)
def iter_fields(self):
return {'id': self.id,
'name': self.name,
'status': self.status,
'status_msg': self.status_msg,
'summary': self.summary,
'avatar': self.avatar,
'photos': self.photos,
'profile': self.profile,
}.iteritems()
class ICapContact(ICap):
def iter_contacts(self, status=Contact.STATUS_ALL, ids=None):
"""
Iter contacts
@param status get only contacts with the specified status
@param ids if set, get the specified contacts
@return iterator over the contacts found
"""
raise NotImplementedError()
def get_contact(self, id):
"""
Get a contact from his id.
The default implementation only calls iter_contacts()
with the proper values, but it might be overloaded
by backends.
@param id the ID requested
@return the Contact object, or None if not found
"""
l = self.iter_contacts(ids=[id])
try:
return l[0]
except IndexError:
return None
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/capabilities/dating.py 0000664 0000000 0000000 00000004133 11430720725 0026616 0 ustar 00root root 0000000 0000000 # -*- 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 .cap import ICap
__all__ = ['ICapDating']
class OptimizationNotFound(Exception):
pass
class Optimization(object):
def start(self):
raise NotImplementedError()
def stop(self):
raise NotImplementedError()
class StatusField(object):
FIELD_TEXT = 0x001 # the value is a long text
FIELD_HTML = 0x002 # the value is HTML formated
def __init__(self, key, label, value, flags=0):
self.key = key
self.label = label
self.value = value
self.flags = flags
class ICapDating(ICap):
def get_status(self):
"""
Get a list of fields
"""
raise NotImplementedError()
OPTIM_PROFILE_WALKER = None
OPTIM_VISIBILITY = None
OPTIM_PRIORITY_CONNECTION = None
def init_optimizations(self):
raise NotImplementedError()
def _get_optim(self, optim):
optim = optim.upper()
if not hasattr(self, 'OPTIM_%s' % optim):
raise OptimizationNotFound()
return getattr(self, 'OPTIM_%s' % optim)
def start_optimization(self, optim):
optim = self._get_optim(optim)
if not optim:
return False
return optim.start()
def stop_optimization(self, optim):
optim = self._get_optim(optim)
if not optim:
return False
return optim.stop()
def list_optimizations(self):
pass
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/capabilities/geolocip.py 0000664 0000000 0000000 00000002254 11430720725 0027153 0 ustar 00root root 0000000 0000000 # -*- 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 .cap import ICap
__all__ = ('IpLocation', 'ICapGeolocIp')
class IpLocation(object):
def __init__(self, ipaddr):
self.ipaddr = ipaddr
self.city = None
self.region = None
self.zipcode = None
self.country = None
self.lt = None
self.lg = None
self.host = None
self.tld = None
self.isp = None
class ICapGeolocIp(ICap):
def get_location(self, ipaddr):
raise NotImplementedError()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/capabilities/messages.py 0000664 0000000 0000000 00000006366 11430720725 0027171 0 ustar 00root root 0000000 0000000 # -*- 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 datetime
import time
from .cap import ICap
__all__ = ['ICapMessages', 'ICapMessagesReply', 'Message']
class Message(object):
def __init__(self, thread_id, _id, title, sender, date=None, reply_id=u'',
content=u'', signature=u'', is_html=False, is_new=False):
self.thread_id = unicode(thread_id)
self.id = unicode(_id)
self.reply_id = unicode(reply_id)
self.title = unicode(title)
self.sender = unicode(sender)
self.signature = unicode(signature)
self.content = content
if date is None:
date = datetime.datetime.utcnow()
self.date = date
self.is_new = is_new
self.is_html = is_html
def get_date_int(self):
return int(time.strftime('%Y%m%d%H%M%S', self.get_date().timetuple()))
def get_full_id(self):
return '%s.%s' % (self.thread_id, self.id)
def get_full_reply_id(self):
return '%s.%s' % (self.thread_id, self.reply_id)
def get_id(self):
return self.id
def get_thread_id(self):
return self.thread_id
def get_reply_id(self):
return self.reply_id
def get_title(self):
return self.title
def get_date(self):
return self.date
def get_from(self):
return self.sender
def get_content(self):
return self.content
def get_signature(self):
return self.signature
def __eq__(self, msg):
return self.id == msg.id and self.thread_id == msg.thread_id
def __repr__(self):
result = '' % (
self.thread_id, self.id, self.title, self.date, self.sender)
return result.encode('utf-8')
class ICapMessages(ICap):
def iter_new_messages(self, thread=None):
"""
Iterates on new messages from last time this function has been called.
@param thread thread name (optional)
@return [list] Message objects
"""
raise NotImplementedError()
def iter_messages(self, thread=None):
"""
Iterates on every messages
@param thread thread name (optional)
@return [list] Message objects
"""
raise NotImplementedError()
class ICapMessagesReply(ICap):
def post_reply(self, thread_id, reply_id, title, message):
"""
Post a reply.
@param thread_id ID of thread
@param reply_id message's id to reply
@param title title of message
@param message message to send
"""
raise NotImplementedError()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/capabilities/torrent.py 0000664 0000000 0000000 00000002602 11430720725 0027044 0 ustar 00root root 0000000 0000000 # -*- 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 .cap import ICap
__all__ = ['ICapTorrent', 'Torrent']
class Torrent(object):
def __init__(self, id, name, date=None, size=0.0, url=u'', seeders=0, leechers=0, files=[], description=u''):
self.id = id
self.name = name
self.date = date
self.size = size
self.url = url
self.seeders = seeders
self.leechers = leechers
self.files = files
self.description = description
class ICapTorrent(ICap):
def iter_torrents(self, pattern):
raise NotImplementedError()
def get_torrent(self, _id):
raise NotImplementedError()
def get_torrent_file(self, _id):
raise NotImplementedError()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/capabilities/travel.py 0000664 0000000 0000000 00000004143 11430720725 0026646 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon, Julien Hebert
#
# 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 datetime import time
from .cap import ICap
__all__ = ['Departure', 'ICapTravel', 'Station']
class ICapTravel(ICap):
def iter_station_search(self, pattern):
"""
Iterates on search results of stations.
@param pattern [str] the search pattern
@return [iter] the of Station objects
"""
raise NotImplementedError()
def iter_station_departures(self, station_id, arrival_id):
"""
Iterate on departures.
@param station_id [id] the station id
@param arrival_id [id] optionnal arrival station id
@return [iter] result of Departure objects
"""
raise NotImplementedError()
class Station(object):
def __init__(self, _id, name):
self.id = _id
self.name = name
def __repr__(self):
return "" % (self.id, self.name)
class Departure(object):
def __init__(self, _id, _type, _time):
self.id = _id
self.type = _type
self.time = _time
self.departure_station = u''
self.arrival_station = u''
self.late = time()
self.information = u''
self.plateform = u''
def __repr__(self):
return "" % (
self.id, self.type, self.time.strftime('%H:%M'), self.departure_station, self.arrival_station)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/capabilities/video.py 0000664 0000000 0000000 00000005360 11430720725 0026461 0 ustar 00root root 0000000 0000000 # -*- 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.
from .cap import ICap
__all__ = ['BaseVideo', 'ICapVideo']
class VideoThumbnail(object):
def __init__(self, url):
self.url = url.replace(' ', '%20')
self.data = None
def __str__(self):
return self.url
def __repr__(self):
return '' % self.url
def __iscomplete__(self):
return self.data
class BaseVideo(object):
def __init__(self, _id, title=None, url=None, author=None, duration=None, date=None,
rating=0.0, rating_max=0.0, thumbnail=None, thumbnail_url=None, nsfw=False):
self.id = unicode(_id)
self.title = title
self.url = url
self.author = author
self.duration = duration
self.date = date
self.rating = float(rating)
self.rating_max = float(rating_max)
self.thumbnail = thumbnail
if thumbnail_url and not self.thumbnail:
self.thumbnail = VideoThumbnail(thumbnail_url)
self.nsfw = nsfw
@classmethod
def id2url(cls, _id):
"""Overloaded in child classes provided by backends."""
raise NotImplementedError()
@property
def page_url(self):
return self.id2url(self.id)
class ICapVideo(ICap):
def iter_page_urls(self, mozaic_url):
raise NotImplementedError()
(SEARCH_RELEVANCE,
SEARCH_RATING,
SEARCH_VIEWS,
SEARCH_DATE) = range(4)
def iter_search_results(self, pattern=None, sortby=SEARCH_RELEVANCE, nsfw=False):
"""
Iter results of a search on a pattern. Note that if pattern is None,
it get the latest videos.
@param pattern [str] pattern to search on
@param sortby [enum] sort by...
@param nsfw [bool] include non-suitable for work videos if True
"""
raise NotImplementedError()
def get_video(self, _id):
"""
Get a Video from an ID.
@param _id the video id. It can be a numeric ID, or a page url, or so.
@return a Video object
"""
raise NotImplementedError()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/capabilities/weather.py 0000664 0000000 0000000 00000003100 11430720725 0027000 0 ustar 00root root 0000000 0000000 # -*- 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 .cap import ICap
__all__ = ['City', 'CityNotFound', 'Current', 'Forecast', 'ICapWeather']
class Forecast(object):
def __init__(self, date, low, high, text, unit):
self.date = date
self.low = low
self.high = high
self.text = text
self.unit = unit
class Current(object):
def __init__(self, date, temp, text, unit):
self.date = date
self.temp = temp
self.text = text
self.unit = unit
class City(object):
def __init__(self, city_id, name):
self.city_id = city_id
self.name = name
class CityNotFound(Exception):
pass
class ICapWeather(ICap):
def iter_city_search(self, pattern):
raise NotImplementedError()
def get_current(self, city_id):
raise NotImplementedError()
def iter_forecast(self, city_id):
raise NotImplementedError()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/core/ 0000775 0000000 0000000 00000000000 11430720725 0023274 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/core/__init__.py 0000664 0000000 0000000 00000001355 11430720725 0025411 0 ustar 00root root 0000000 0000000 # -*- 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 .bcall import CallErrors
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/core/backends.py 0000664 0000000 0000000 00000017026 11430720725 0025426 0 ustar 00root root 0000000 0000000 # -*- 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 ConfigParser import SafeConfigParser
import logging
from logging import debug, error, exception, warning
import os
import re
import stat
from weboob.capabilities.cap import ICap
from weboob.tools.backend import BaseBackend
__all__ = ['Backend', 'BackendsConfig', 'BackendsLoader']
class Backend(object):
def __init__(self, package):
self.package = package
self.klass = None
for attrname in dir(self.package):
attr = getattr(self.package, attrname)
if isinstance(attr, type) and issubclass(attr, BaseBackend) and attr != BaseBackend:
self.klass = attr
if not self.klass:
raise ImportError('%s is not a backend (no BaseBackend class found)' % package)
@property
def name(self):
return self.klass.NAME
@property
def maintainer(self):
return '%s <%s>' % (self.klass.MAINTAINER, self.klass.EMAIL)
@property
def version(self):
return self.klass.VERSION
@property
def description(self):
return self.klass.DESCRIPTION
@property
def license(self):
return self.klass.LICENSE
@property
def config(self):
return self.klass.CONFIG
@property
def icon_path(self):
if self.klass.ICON is None:
try:
import xdg.IconTheme
except ImportError:
pass
else:
self.klass.ICON = xdg.IconTheme.getIconPath(self.klass.NAME)
return self.klass.ICON
def iter_caps(self):
for cap in self.klass.__bases__:
if issubclass(cap, ICap) and cap != ICap:
yield cap
def has_caps(self, *caps):
for c in caps:
if (isinstance(c, (unicode,str)) and c in [cap.__name__ for cap in self.iter_caps()]) or \
(type(c) == type and issubclass(self.klass, c)):
return True
return False
def create_instance(self, weboob, instance_name, config, storage):
backend_instance = self.klass(weboob, instance_name, config, storage)
debug(u'Created backend instance "%s" for backend "%s"' % (instance_name, self.name))
return backend_instance
class BackendsConfig(object):
class WrongPermissions(Exception):
pass
def __init__(self, confpath):
self.confpath = confpath
try:
mode = os.stat(confpath).st_mode
except OSError:
os.mknod(confpath, 0600)
else:
if mode & stat.S_IRGRP or mode & stat.S_IROTH:
raise self.WrongPermissions(
u'Weboob will not start until config file %s is readable by group or other users.' % confpath)
def iter_backends(self):
config = SafeConfigParser()
config.read(self.confpath)
for instance_name in config.sections():
params = dict(config.items(instance_name, raw=True))
try:
backend_name = params.pop('_backend')
except KeyError:
try:
backend_name = params.pop('_type')
logging.warning(u'Please replace _type with _backend in your config file "%s", for backend "%s"' % (
self.confpath, backend_name))
except KeyError:
warning('Missing field "_backend" for configured backend "%s"', instance_name)
continue
yield instance_name, backend_name, params
def add_backend(self, instance_name, backend_name, params, edit=False):
if not instance_name:
raise ValueError(u'Please give a name to the configured backend.')
config = SafeConfigParser()
config.read(self.confpath)
if not edit:
config.add_section(instance_name)
config.set(instance_name, '_backend', backend_name)
for key, value in params.iteritems():
config.set(instance_name, key, value)
with open(self.confpath, 'wb') as f:
config.write(f)
def edit_backend(self, instance_name, backend_name, params):
return self.add_backend(instance_name, backend_name, params, True)
def get_backend(self, instance_name):
config = SafeConfigParser()
config.read(self.confpath)
if not config.has_section(instance_name):
raise KeyError(u'Configured backend "%s" not found' % instance_name)
items = dict(config.items(instance_name, raw=True))
try:
backend_name = items.pop('_backend')
except KeyError:
try:
backend_name = items.pop('_type')
logging.warning(u'Please replace _type with _backend in your config file "%s"' % self.confpath)
except KeyError:
warning('Missing field "_backend" for configured backend "%s"', instance_name)
raise KeyError(u'Configured backend "%s" not found' % instance_name)
return backend_name, items
def remove_backend(self, instance_name):
config = SafeConfigParser()
config.read(self.confpath)
config.remove_section(instance_name)
with open(self.confpath, 'w') as f:
config.write(f)
class BackendsLoader(object):
def __init__(self):
self.loaded = {}
def get_or_load_backend(self, backend_name):
if backend_name not in self.loaded:
self.load_backend(backend_name)
if backend_name in self.loaded:
return self.loaded[backend_name]
else:
return None
def iter_existing_backend_names(self):
try:
import weboob.backends
except ImportError:
return
for path in weboob.backends.__path__:
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:
yield m.group(1)
def load_all(self):
for existing_backend_name in self.iter_existing_backend_names():
self.load_backend(existing_backend_name)
def load_backend(self, backend_name):
try:
package_name = 'weboob.backends.%s' % backend_name
backend = Backend(__import__(package_name, fromlist=[str(package_name)]))
except ImportError, e:
msg = u'Unable to load backend "%s": %s' % (backend_name, e)
if logging.root.level == logging.DEBUG:
exception(msg)
return
else:
error(msg)
return
if backend.name in self.loaded:
debug('Backend "%s" is already loaded from %s' % (backend_name, backend.package.__path__[0]))
return
self.loaded[backend.name] = backend
debug('Loaded backend "%s" from %s' % (backend_name, backend.package.__path__[0]))
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/core/bcall.py 0000664 0000000 0000000 00000015247 11430720725 0024734 0 ustar 00root root 0000000 0000000 # -*- 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.
from __future__ import with_statement
from copy import copy
import logging
from logging import debug
from threading import Thread, Event, RLock, Timer
from weboob.tools.misc import get_backtrace
__all__ = ['BackendsCall', 'CallErrors']
class CallErrors(Exception):
def __init__(self, errors):
Exception.__init__(self, u'These errors have been raised in backend threads '\
'(use --debug option to print backtraces):\n%s' % (
u'\n'.join((u' * %s: %s%s' % (backend, repr(error), backtrace.decode('utf-8') + '\n'
if logging.root.level == logging.DEBUG else ''))
for backend, error, backtrace in errors)))
self.errors = copy(errors)
def __iter__(self):
return self.errors.__iter__()
class BackendsCall(object):
def __init__(self, backends, function, *args, **kwargs):
"""
@param backends list of backends to call
@param function backends' method name, or callable object
@param args, kwargs arguments given to called functions
"""
# Store if a backend is finished
self.backends = {}
for backend in backends:
self.backends[backend.name] = False
# Global mutex on object
self.mutex = RLock()
# Event set when every backends have give their data
self.finish_event = Event()
# Event set when there are new responses
self.response_event = Event()
# Waiting responses
self.responses = []
# Errors
self.errors = []
# Threads
self.threads = []
# Create jobs for each backend
with self.mutex:
for backend in backends:
debug('Creating a new thread for %s' % backend)
self.threads.append(Timer(0, self._caller, (backend, function, args, kwargs)).start())
if not backends:
self.finish_event.set()
def _store_error(self, backend, error):
with self.mutex:
backtrace = get_backtrace(error)
self.errors.append((backend, error, backtrace))
def _store_result(self, backend, result):
with self.mutex:
self.responses.append((backend, result))
self.response_event.set()
def _caller(self, backend, function, args, kwargs):
debug('%s: Thread created successfully' % backend)
with backend:
try:
# Call method on backend
try:
debug('%s: Calling function %s' % (backend, function))
if callable(function):
result = function(backend, *args, **kwargs)
else:
result = getattr(backend, function)(*args, **kwargs)
except Exception, error:
self._store_error(backend, error)
else:
debug('%s: Called function %s returned: %r' % (backend, function, result))
if hasattr(result, '__iter__') and not isinstance(result, (str,unicode)):
# Loop on iterator
try:
for subresult in result:
# Lock mutex only in loop in case the iterator is slow
# (for example if backend do some parsing operations)
self._store_result(backend, subresult)
except Exception, error:
self._store_error(backend, error)
else:
self._store_result(backend, result)
finally:
with self.mutex:
# This backend is now finished
self.backends[backend.name] = True
for finished in self.backends.itervalues():
if not finished:
return
self.finish_event.set()
self.response_event.set()
def _callback_thread_run(self, callback, errback):
responses = []
while not self.finish_event.isSet() or self.response_event.isSet():
self.response_event.wait()
with self.mutex:
responses = self.responses
self.responses = []
# Reset event
self.response_event.clear()
# Consume responses
while responses:
callback(*responses.pop(0))
if errback:
with self.mutex:
while self.errors:
errback(*self.errors.pop(0))
callback(None, None)
def callback_thread(self, callback, errback=None):
"""
Call this method to create a thread which will callback a
specified function everytimes a new result comes.
When the process is over, the function will be called with
both arguments set to None.
The functions prototypes:
def callback(backend, result)
def errback(backend, error)
"""
thread = Thread(target=self._callback_thread_run, args=(callback, errback))
thread.start()
return thread
def wait(self):
self.finish_event.wait()
with self.mutex:
if self.errors:
raise CallErrors(self.errors)
def __iter__(self):
# Don't know how to factorize with _callback_thread_run
responses = []
while not self.finish_event.isSet() or self.response_event.isSet():
self.response_event.wait()
with self.mutex:
responses = self.responses
self.responses = []
# Reset event
self.response_event.clear()
# Consume responses
while responses:
yield responses.pop(0)
# Raise errors
with self.mutex:
if self.errors:
raise CallErrors(self.errors)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/core/ouiboube.py 0000664 0000000 0000000 00000015333 11430720725 0025464 0 ustar 00root root 0000000 0000000 # -*- 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 logging import warning
import os
from weboob.core.bcall import BackendsCall
from weboob.core.backends import BackendsConfig, BackendsLoader
from weboob.core.scheduler import Scheduler
from weboob.tools.backend import BaseBackend
__all__ = ['Weboob']
class Weboob(object):
WORKDIR = os.path.join(os.path.expanduser('~'), '.weboob')
BACKENDS_FILENAME = 'backends'
def __init__(self, workdir=WORKDIR, backends_filename=None, scheduler=None, storage=None):
self.workdir = workdir
self.backend_instances = {}
# Scheduler
if scheduler is None:
scheduler = Scheduler()
self.scheduler = scheduler
# Create WORKDIR
if not os.path.exists(self.workdir):
os.mkdir(self.workdir, 0700)
elif not os.path.isdir(self.workdir):
warning(u'"%s" is not a directory' % self.workdir)
# Backends loader
self.backends_loader = BackendsLoader()
# Backend instances config
if not backends_filename:
backends_filename = os.path.join(self.workdir, self.BACKENDS_FILENAME)
elif not backends_filename.startswith('/'):
backends_filename = os.path.join(self.workdir, backends_filename)
self.backends_config = BackendsConfig(backends_filename)
# Storage
self.storage = storage
def __deinit__(self):
self.deinit()
def deinit(self):
self.unload_backends()
def load_backends(self, caps=None, names=None, storage=None):
loaded = {}
if storage is None:
storage = self.storage
self.backends_loader.load_all()
for backend_name, backend in self.backends_loader.loaded.iteritems():
if caps is not None and not backend.has_caps(caps) or \
names is not None and backend_name not in names:
continue
backend_instance = backend.create_instance(self, backend_name, {}, storage)
self.backend_instances[backend_name] = loaded[backend_name] = backend_instance
return loaded
def load_configured_backends(self, caps=None, names=None, storage=None):
loaded = {}
if storage is None:
storage = self.storage
for instance_name, backend_name, params in self.backends_config.iter_backends():
if '_enabled' in params and not params['_enabled']:
continue
backend = self.backends_loader.get_or_load_backend(backend_name)
if backend is None:
warning(u'Backend "%s" is referenced in ~/.weboob/backends '
'configuration file, but was not found. '
'Hint: is it installed?' % backend_name)
continue
if caps is not None and not backend.has_caps(caps) or \
names is not None and instance_name not in names:
continue
backend_instance = backend.create_instance(self, instance_name, params, storage)
self.backend_instances[instance_name] = loaded[instance_name] = backend_instance
return loaded
def unload_backends(self, names=None):
if isinstance(names, (str,unicode)):
names = [names]
elif names is None:
names = self.backend_instances.keys()
for name in names:
backend = self.backend_instances.pop(name)
with backend:
backend.deinit()
def iter_backends(self, caps=None):
"""
Iter on each backends.
Note: each backend is locked when it is returned.
@param caps Optional list of capabilities to select backends
@return iterator on selected backends.
"""
for name, backend in sorted(self.backend_instances.iteritems()):
if caps is None or backend.has_caps(caps):
with backend:
yield backend
def do(self, function, *args, **kwargs):
"""
Do calls on loaded backends with specified arguments, in separated
threads.
This function has two modes:
- If 'function' is a string, it calls the method with this name on
each backends with the specified arguments;
- If 'function' is a callable, it calls it in a separated thread with
the locked backend instance at first arguments, and *args and
**kwargs.
@param function backend's method name, or callable object
@param backends list of backends to iterate on
@param caps iterate on backends with this caps
@return an iterator of results
"""
backends = self.backend_instances.values()
_backends = kwargs.pop('backends', None)
if _backends:
if isinstance(_backends, BaseBackend):
backends = [_backends]
elif isinstance(_backends, (str,unicode)) and _backends:
backends = [self.backend_instances[_backends]]
elif isinstance(_backends, (list,tuple)):
backends = []
for backend in _backends:
if isinstance(backend, (str,unicode)):
try:
backends.append(self.backend_instances[backend])
except ValueError:
pass
else:
backends.append(backend)
else:
warning(u'The "backends" value isn\'t supported: %r' % _backends)
if 'caps' in kwargs:
caps = kwargs.pop('caps')
backends = [backend for backend in backends if backend.has_caps(caps)]
return BackendsCall(backends, function, *args, **kwargs)
def schedule(self, interval, function, *args):
return self.scheduler.schedule(interval, function, *args)
def repeat(self, interval, function, *args):
return self.scheduler.repeat(interval, function, *args)
def want_stop(self):
return self.scheduler.want_stop()
def loop(self):
return self.scheduler.run()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/core/scheduler.py 0000664 0000000 0000000 00000005371 11430720725 0025632 0 ustar 00root root 0000000 0000000 # -*- 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.
from __future__ import with_statement
import logging
from threading import Timer, Event, RLock
__all__ = ['Scheduler']
class IScheduler(object):
def schedule(self, interval, function, *args):
raise NotImplementedError()
def repeat(self, interval, function, *args):
raise NotImplementedError()
def run(self):
raise NotImplementedError()
def want_stop(self):
raise NotImplementedError()
class Scheduler(IScheduler):
def __init__(self):
self.mutex = RLock()
self.stop_event = Event()
self.count = 0
self.queue = {}
def schedule(self, interval, function, *args):
if self.stop_event.isSet():
return
with self.mutex:
self.count += 1
logging.debug('function "%s" will be called in %s seconds' % (function.__name__, interval))
timer = Timer(interval, self._callback, (self.count, function, args))
self.queue[self.count] = timer
timer.start()
return self.count
def _callback(self, count, function, args):
with self.mutex:
self.queue.pop(count)
return function(*args)
def repeat(self, interval, function, *args):
return self._repeat(True, interval, function, *args)
def _repeat(self, first, interval, function, *args):
return self.schedule(0 if first else interval, self._repeated_cb, interval, function, args)
def _wait_to_stop(self):
self.want_stop()
with self.mutex:
for e in self.queue.itervalues():
e.cancel()
e.join()
def run(self):
try:
while 1:
self.stop_event.wait(0.1)
except KeyboardInterrupt:
self._wait_to_stop()
raise
else:
self._wait_to_stop()
return True
def want_stop(self):
self.stop_event.set()
def _repeated_cb(self, interval, function, args):
function(*args)
self._repeat(False, interval, function, *args)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/ 0000775 0000000 0000000 00000000000 11430720725 0023504 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/__init__.py 0000664 0000000 0000000 00000000070 11430720725 0025612 0 ustar 00root root 0000000 0000000 __import__('pkg_resources').declare_namespace(__name__)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/ 0000775 0000000 0000000 00000000000 11430720725 0026007 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/__init__.py 0000664 0000000 0000000 00000000070 11430720725 0030115 0 ustar 00root root 0000000 0000000 __import__('pkg_resources').declare_namespace(__name__)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/base.py 0000664 0000000 0000000 00000022747 11430720725 0027307 0 ustar 00root root 0000000 0000000 # -*- 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 sys, os
import logging
import optparse
from optparse import OptionGroup, OptionParser
from weboob.core.ouiboube import Weboob
from weboob.tools.config.iconfig import ConfigError
from weboob.tools.backend import ObjectNotSupported
from weboob.tools.misc import iter_fields
__all__ = ['BackendNotFound', 'BaseApplication', 'ConfigError']
class BackendNotFound(Exception):
pass
class ApplicationStorage(object):
def __init__(self, name, storage):
self.name = name
self.storage = storage
def set(self, *args):
if self.storage:
return self.storage.set('applications', self.name, *args)
def get(self, *args, **kwargs):
if self.storage:
return self.storage.get('applications', self.name, *args, **kwargs)
else:
return kwargs.get('default', None)
def load(self, default):
if self.storage:
return self.storage.load('applications', self.name, default)
def save(self):
if self.storage:
return self.storage.save('applications', self.name)
class BaseApplication(object):
"""
Base application.
This class can be herited to have some common code within weboob
applications.
"""
# ------ Class attributes --------------------------------------
# Application name
APPNAME = ''
# Configuration and work directory (default: ~/.weboob/)
CONFDIR = os.path.join(os.path.expanduser('~'), '.weboob')
# Default configuration dict (can only contain key/values)
CONFIG = {}
# Default storage tree
STORAGE = {}
# Synopsis
SYNOPSIS = 'Usage: %prog [options (-h for help)] ...'
# Version
VERSION = None
# Copyright
COPYRIGHT = None
# ------ Abstract methods --------------------------------------
def create_weboob(self):
return Weboob()
def _get_completions(self):
"""
Overload this method in subclasses if you want to enrich shell completion.
@return a set object
"""
return set()
def _handle_app_options(self):
"""
Overload this method in subclasses if you want to handle options defined in subclass constructor.
"""
pass
# ------ BaseApplication methods -------------------------------
def __init__(self, option_parser=None):
self.weboob = self.create_weboob()
self.config = None
if option_parser is None:
self._parser = OptionParser(self.SYNOPSIS, version=self._get_optparse_version())
else:
self._parser = option_parser
self._parser.add_option('-b', '--backends', help='what backend(s) to enable (comma separated)')
logging_options = OptionGroup(self._parser, 'Logging Options')
logging_options.add_option('-d', '--debug', action='store_true', help='display debug messages')
logging_options.add_option('-q', '--quiet', action='store_true', help='display only error messages')
logging_options.add_option('-v', '--verbose', action='store_true', help='display info messages')
self._parser.add_option_group(logging_options)
self._parser.add_option('--shell-completion', action='store_true', help=optparse.SUPPRESS_HELP)
def deinit(self):
self.weboob.deinit()
def create_storage(self, path=None, klass=None, localonly=False):
"""
Create a storage object.
@param path [str] an optional specific path.
@param klass [IStorage] what klass to instance.
@param localonly [bool] if True, do not set it on the Weboob object.
@return a IStorage object
"""
if klass is None:
from weboob.tools.storage import StandardStorage
klass = StandardStorage
if path is None:
path = os.path.join(self.CONFDIR, self.APPNAME + '.storage')
elif not path.startswith('/'):
path = os.path.join(self.CONFDIR, path)
storage = klass(path)
self.storage = ApplicationStorage(self.APPNAME, storage)
self.storage.load(self.STORAGE)
if not localonly:
self.weboob.storage = storage
return storage
def load_config(self, path=None, klass=None):
"""
Load a configuration file and get his object.
@param path [str] an optional specific path.
@param klass [IConfig] what klass to instance.
@return a IConfig object
"""
if klass is None:
from weboob.tools.config.iniconfig import INIConfig
klass = INIConfig
if path is None:
path = os.path.join(self.CONFDIR, self.APPNAME)
elif not path.startswith('/'):
path = os.path.join(self.CONFDIR, path)
self.config = klass(path)
self.config.load(self.CONFIG)
def main(self, argv):
""" Main function """
raise NotImplementedError()
def load_backends(self, caps=None, names=None, *args, **kwargs):
if names is None:
names = self.requested_backends
loaded = self.weboob.load_backends(caps, names, *args, **kwargs)
if not loaded:
logging.warning(u'No backend loaded')
return loaded
def load_configured_backends(self, caps=None, names=None, *args, **kwargs):
if names is None:
names = self.requested_backends
loaded = self.weboob.load_configured_backends(caps, names, *args, **kwargs)
if not loaded:
logging.warning(u'No configured backend loaded')
return loaded
def _get_optparse_version(self):
version = None
if self.VERSION:
if self.COPYRIGHT:
version = '%s v%s (%s)' % (self.APPNAME, self.VERSION, self.COPYRIGHT)
else:
version = '%s v%s' % (self.APPNAME, self.VERSION)
return version
def _complete_obj(self, backend, obj, fields):
if fields:
if '*' in fields:
fields = [k for k, v in iter_fields(obj)]
try:
backend.fillobj(obj, fields)
except ObjectNotSupported, e:
logging.warning(u'Could not retrieve required fields (%s): %s' % (','.join(fields), e))
return obj
def _complete_iter(self, backend, count, fields, res):
for i, sub in enumerate(res):
if count and i == count:
break
sub = self._complete_obj(backend, sub, fields)
yield sub
def complete(self, backend, count, selected_fields, function, *args, **kwargs):
res = getattr(backend, function)(*args, **kwargs)
if self.selected_fields:
fields = self.selected_fields
else:
fields = None
if hasattr(res, '__iter__'):
return self._complete_iter(backend, count, fields, res)
else:
return self._complete_obj(backend, fields, res)
@classmethod
def run(klass, args=None):
"""
This static method can be called to run the application.
It creates the application object, handle options, setup logging, run
the main() method, and catch common exceptions.
You can't do anything after this call, as it *always* finish with
a call to sys.exit().
For example:
>>> from weboob.application.myapplication import MyApplication
>>> MyApplication.run()
"""
if args is None:
args = [(sys.stdin.encoding and arg.decode(sys.stdin.encoding) or arg) for arg in sys.argv]
app = klass()
app.options, args = app._parser.parse_args(args)
if app.options.shell_completion:
items = set()
for option in app._parser.option_list:
if not option.help is optparse.SUPPRESS_HELP:
items.update(str(option).split('/'))
items.update(app._get_completions())
print ' '.join(items)
sys.exit(0)
if app.options.debug:
level=logging.DEBUG
elif app.options.verbose:
level = logging.INFO
elif app.options.quiet:
level = logging.ERROR
else:
level = logging.WARNING
log_format = '%(asctime)s:%(levelname)s:%(pathname)s:%(lineno)d:%(funcName)s %(message)s'
logging.basicConfig(stream=sys.stdout, level=level, format=log_format)
app.requested_backends = app.options.backends.split(',') if app.options.backends else None
app._handle_app_options()
try:
try:
sys.exit(app.main(args))
except KeyboardInterrupt:
print 'Program killed by SIGINT'
sys.exit(0) # XXX is it really the right exit code? -romain
except ConfigError, e:
print 'Configuration error: %s' % e
sys.exit(1)
finally:
app.deinit()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/console.py 0000664 0000000 0000000 00000026262 11430720725 0030033 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2010 Romain Bignon, Julien Hébert, 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
from functools import partial
import getpass
from inspect import getargspec
import logging
from optparse import OptionGroup, OptionParser
import re
import subprocess
import sys
from weboob.core import CallErrors
from weboob.core.backends import BackendsConfig
from .base import BackendNotFound, BaseApplication
from .formatters.load import formatters, load_formatter
from .formatters.iformatter import FieldNotFound
from .results import ResultsCondition, ResultsConditionException
__all__ = ['ConsoleApplication']
class ConsoleApplication(BaseApplication):
"""
Base application class for CLI applications.
"""
SYNOPSIS = 'Usage: %prog [options (-h for help)] command [parameters...]'
def __init__(self):
option_parser = OptionParser(self.SYNOPSIS, version=self._get_optparse_version())
app_options = OptionGroup(option_parser, '%s Options' % self.APPNAME.capitalize())
self.add_application_options(app_options)
option_parser.add_option_group(app_options)
try:
BaseApplication.__init__(self, option_parser=option_parser)
except BackendsConfig.WrongPermissions, e:
logging.error(u'Error: %s' % e)
sys.exit(1)
self._parser.format_description = lambda x: self._parser.description
if self._parser.description is None:
self._parser.description = ''
self._parser.description += 'Available commands:\n'
for name, arguments, doc_string in self._commands:
command = '%s %s' % (name, arguments)
self._parser.description += ' %-30s %s\n' % (command, doc_string)
results_options = OptionGroup(self._parser, 'Results Options')
results_options.add_option('-c', '--condition', help='filter result items to display given a boolean condition')
results_options.add_option('-n', '--count', type='int', help='get a maximum number of results (all backends merged)')
results_options.add_option('-s', '--select', help='select result item keys to display (comma separated)')
self._parser.add_option_group(results_options)
formatting_options = OptionGroup(self._parser, 'Formatting Options')
formatting_options.add_option('-f', '--formatter', choices=formatters, help='select output formatter (%s)' % u','.join(formatters))
formatting_options.add_option('--no-header', dest='no_header', action='store_true', help='do not display header')
formatting_options.add_option('--no-keys', dest='no_keys', action='store_true', help='do not display item keys')
self._parser.add_option_group(formatting_options)
def add_application_options(self, group):
# XXX why is it in ConsoleApplication and not BaseApplication? -romain
pass
def _handle_app_options(self):
if self.options.formatter:
formatter_name = self.options.formatter
else:
formatter_name = 'multiline'
self.formatter = load_formatter(formatter_name)
if self.options.no_header:
self.formatter.display_header = False
if self.options.no_keys:
self.formatter.display_keys = False
if self.options.select:
self.selected_fields = self.options.select.split(',')
else:
self.selected_fields = None
if self.options.condition:
self.condition = ResultsCondition(self.options.condition)
else:
self.condition = None
def _get_completions(self):
return set(name for name, arguments, doc_string in self._commands)
def ask(self, question, default=None, masked=False, regexp=None):
"""
Ask a question to user.
@param question text displayed (str)
@param default optional default value (str)
@param masked if True, do not show typed text (bool)
@param regexp text must match this regexp (str)
@return entered text by user (str)
"""
if default is not None:
question = u'%s [%s]' % (question, default)
hidden_msg = u'(input chars are hidden) ' if masked else ''
question = u'%s%s: ' % (hidden_msg, question)
correct = False
while not correct:
line = getpass.getpass(question) if masked else raw_input(question)
if not line and default is not None:
line = default
correct = not regexp or re.match(regexp, unicode(line))
return line
def process_command(self, command=None, *args):
if command is None:
self._parser.print_help()
return 0
def f(x):
return x.startswith('command_' + command)
matching_commands = filter(f, dir(self))
if len(matching_commands) == 0:
sys.stderr.write("No such command: %s.\n" % command)
return 1
if len(matching_commands) != 1:
sys.stderr.write("Ambiguious command %s: %s.\n" % (command, ', '.join(
[s.replace('command_', '', 1) for s in matching_commands])))
return 1
func = getattr(self, matching_commands[0])
_args, varargs, varkw, defaults = getargspec(func)
nb_max_args = nb_min_args = len(_args) - 1
if defaults:
nb_min_args -= len(defaults)
if len(args) > nb_max_args and not varargs:
sys.stderr.write("Command '%s' takes at most %d arguments.\n" % (command, nb_max_args))
return 1
if len(args) < nb_min_args:
if varargs or defaults:
sys.stderr.write("Command '%s' takes at least %d arguments.\n" % (command, nb_min_args))
else:
sys.stderr.write("Command '%s' takes %d arguments.\n" % (command, nb_min_args))
return 1
try:
command_result = func(*args)
except CallErrors, errors:
logging.error(errors)
return 1
self.formatter.flush()
# Process result if value is returned by command
if isinstance(command_result, str):
print command_result
elif isinstance(command_result, unicode):
print command_result.encode('utf-8')
elif isinstance(command_result, int):
return command_result
elif command_result is None:
return 0
else:
try:
print unicode(command_result).encode('utf-8')
except ValueError:
raise Exception(u'Command result type not expected: %s' % type(command_result))
return 0
_commands = []
def register_command(f, doc_string, register_to=_commands):
def get_arguments(func, skip=0):
"""
Get arguments of a function as a string.
skip is the number of skipped arguments.
"""
skip += 1
args, varargs, varkw, defaults = getargspec(func)
cut = len(args)
if defaults:
cut -= len(defaults)
args = ["<%s>" % a for a in args[skip:cut]] + \
["[%s]" % a for a in args[cut:]]
if varargs:
args.append("[%s..]" % varargs)
if varkw:
raise TypeError('Command %s requests illegal keyword args (**%s)' % varkw)
return " ".join(args)
command_name = f.func_name.replace('command_', '')
register_to.append((command_name, get_arguments(f), doc_string))
return f
def command(doc_string, f=register_command):
return partial(f, doc_string=doc_string)
def set_default_formatter(self, name):
if not self.options.formatter:
try:
self.formatter = load_formatter(name)
except ImportError:
default_name = 'multiline'
logging.error('Could not load default formatter "%s" for this command. Falling back to "%s".' % (
name, default_name))
self.formatter = load_formatter(default_name)
def set_formatter_header(self, string):
self.formatter.set_header(string)
def format(self, result, backend_name=None):
try:
self.formatter.format(obj=result, backend_name=backend_name,
selected_fields=self.selected_fields, condition=self.condition)
except FieldNotFound, e:
logging.error(e)
except ResultsConditionException, e:
logging.error(e)
# XXX why do not use staticmethod as a decorator? -romain
register_command = staticmethod(register_command)
command = staticmethod(command)
def load_configured_backends(self, caps=None, names=None, *args, **kwargs):
loaded_backends = BaseApplication.load_configured_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.applications.weboobcfg import WeboobCfg
weboobcfg = WeboobCfg()
weboobcfg.options, args = weboobcfg._parser.parse_args([])
weboobcfg._handle_app_options()
if caps is not None:
if not isinstance(caps, (list, tuple, set)):
caps = (caps,)
caps = [(cap if isinstance(cap, (str,unicode)) else cap.__name__) for cap in caps]
weboobcfg.command_backends(*caps)
logging.error(u'You can configure backends using the "weboob-config add" command:\nweboob-config add [options..]')
with open('/dev/null', 'w') as devnull:
process = subprocess.Popen(['which', 'weboob-config-qt'], stdout=devnull)
return_code = process.wait()
if return_code == 0:
logging.error(u'You can configure backends using the "weboob-config-qt" GUI, too.')
sys.exit(0)
def parse_id(self, _id):
try:
_id, backend_name = _id.rsplit('@', 1)
except ValueError:
backend_name = None
return _id, backend_name
@classmethod
def run(klass, args=None):
try:
super(ConsoleApplication, klass).run(args)
except BackendNotFound, e:
logging.error(e)
def do(self, function, *args, **kwargs):
"""
Call Weboob.do(), after having filled the yielded object, if selected fields are given by user.
"""
return self.weboob.do(self.complete, self.options.count, self.selected_fields, function, *args, **kwargs)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/formatters/ 0000775 0000000 0000000 00000000000 11430720725 0030175 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/formatters/__init__.py0000664 0000000 0000000 00000000070 11430720725 0032303 0 ustar 00root root 0000000 0000000 __import__('pkg_resources').declare_namespace(__name__)
iformatter.py 0000664 0000000 0000000 00000010525 11430720725 0032647 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/formatters # -*- 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 weboob.tools.misc import iter_fields
from weboob.tools.ordereddict import OrderedDict
__all__ = ['FieldNotFound', 'IFormatter']
class FieldNotFound(Exception):
def __init__(self, field):
Exception.__init__(self, u'Field not found: "%s"' % field)
class IFormatter(object):
def __init__(self, display_keys=True, display_header=True, return_only=False):
self.display_keys = display_keys
self.display_header = display_header
self.return_only = return_only
def after_format(self, formatted):
raise NotImplementedError()
def build_id(self, v, backend_name):
return u'%s@%s' % (unicode(v), backend_name)
def flush(self):
raise NotImplementedError()
def format(self, obj, backend_name=None, selected_fields=None, condition=None):
"""
Format an object to be human-readable.
An object has fields which can be selected, and the objects
can be filtered using a condition (like SELECT and WHERE in SQL).
If the object provides an iter_fields() method, the formatter will
call it. It can be used to specify the fields order.
@param obj [object] object to format
@param backend_name [str] name of backend, used to create object ID
@param selected_fields [tuple] fields to display. If None, all fields are selected
@param condition [Condition] condition to objects to display
@return a string of the formatted object
"""
if isinstance(obj, dict):
item = obj
else:
item = self.to_dict(obj, backend_name, condition, selected_fields)
if item is None:
return None
formatted = self.format_dict(item=item)
if formatted:
self.after_format(formatted)
return formatted
def format_dict(self, item):
"""
Format an dict to be human-readable. The dict is already simplified if user provides selected fields.
Called by format().
This method has to be overridden in child classes.
@param item [dict] item to format
@return a string of the formatted dict
"""
raise NotImplementedError()
def set_header(self, string):
raise NotImplementedError()
def to_dict(self, obj, backend_name=None, condition=None, selected_fields=None):
def iter_select_and_decorate(d):
if hasattr(obj, '__id__'):
id_attr = getattr(obj, '__id__')
if not isinstance(id_attr, (set, list, tuple)):
id_attr = (id_attr,)
id_fields = id_attr
else:
id_fields = ('id',)
if selected_fields is None or '*' in selected_fields:
for k, v in d:
if k in id_fields and backend_name is not None:
v = self.build_id(v, backend_name)
yield k, v
else:
d = dict(d)
for selected_field in selected_fields:
v = d[selected_field]
if selected_field in id_fields and backend_name is not None:
v = self.build_id(v, backend_name)
try:
yield selected_field, v
except KeyError:
raise FieldNotFound(selected_field)
fields_iterator = obj.iter_fields() if hasattr(obj, 'iter_fields') else iter_fields(obj)
d = dict(fields_iterator)
if condition is not None and not condition.is_valid(d):
return None
return OrderedDict([(k, v) for k, v in iter_select_and_decorate(d.iteritems())])
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/formatters/load.py 0000664 0000000 0000000 00000002770 11430720725 0031474 0 ustar 00root root 0000000 0000000 # -*- 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.
__all__ = ['load_formatter']
formatters = ('htmltable', 'multiline', 'simple', 'table', 'webkit')
def load_formatter(name):
if name not in formatters:
raise Exception(u'Formatter "%s" not found' % name)
if name in ('htmltable', 'table'):
from .table import TableFormatter
if name == 'htmltable':
return TableFormatter(result_funcname='get_html_string')
elif name == 'table':
return TableFormatter()
elif name == 'simple':
from .simple import SimpleFormatter
return SimpleFormatter()
elif name == 'multiline':
from .multiline import MultilineFormatter
return MultilineFormatter()
elif name == 'webkit':
from .webkit import WebkitGtkFormatter
return WebkitGtkFormatter()
multiline.py 0000664 0000000 0000000 00000002747 11430720725 0032504 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/formatters # -*- 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 .iformatter import IFormatter
__all__ = ['MultilineFormatter']
class MultilineFormatter(IFormatter):
def __init__(self, key_value_separator=u': ', after_item=u'\n'):
IFormatter.__init__(self)
self.key_value_separator = key_value_separator
self.after_item = after_item
def after_format(self, formatted):
print formatted.encode('utf-8')
def flush(self):
pass
def format_dict(self, item):
result = u'\n'.join(u'%s%s' % ((u'%s%s' % (k, self.key_value_separator) if self.display_keys else ''), v) for k, v in item.iteritems())
if len(item) > 1:
result += self.after_item
return result
def set_header(self, string):
if self.display_header:
print string.encode('utf-8')
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/formatters/simple.py 0000664 0000000 0000000 00000002646 11430720725 0032050 0 ustar 00root root 0000000 0000000 # -*- 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 .iformatter import IFormatter
__all__ = ['SimpleFormatter']
class SimpleFormatter(IFormatter):
def __init__(self, field_separator=u'\t', key_value_separator=u'='):
IFormatter.__init__(self)
self.field_separator = field_separator
self.key_value_separator = key_value_separator
def after_format(self, formatted):
print formatted.encode('utf-8')
def flush(self):
pass
def format_dict(self, item):
return self.field_separator.join(u'%s%s' % ((u'%s%s' % (k, self.key_value_separator) if self.display_keys else ''), v) for k, v in item.iteritems())
def set_header(self, string):
if self.display_header:
print string.encode('utf-8')
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/formatters/table.py 0000664 0000000 0000000 00000004270 11430720725 0031641 0 ustar 00root root 0000000 0000000 # -*- 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 prettytable import PrettyTable
from .iformatter import IFormatter
__all__ = ['TableFormatter']
class TableFormatter(IFormatter):
column_headers = None
queue = []
header = None
def __init__(self, display_keys=True, return_only=False, result_funcname='get_string'):
IFormatter.__init__(self, display_keys=display_keys, return_only=return_only)
self.result_funcname = result_funcname
def after_format(self, formatted):
if self.column_headers is None:
self.column_headers = formatted.keys()
self.queue.append(formatted.values())
def flush(self):
if self.column_headers is None:
return None
s = ''
if self.display_header and self.header:
if self.result_funcname == 'get_string':
s += self.header
elif self.result_funcname == 'get_html_string':
s+= '
%s
' % self.header
s += "\n"
table = PrettyTable(list(self.column_headers))
for column_header in self.column_headers:
table.set_field_align(column_header, 'l')
for line in self.queue:
table.add_row(line)
s += getattr(table, self.result_funcname)()
if self.return_only:
return s
else:
print s.encode('utf-8')
def format_dict(self, item):
# format is done in self.flush() by prettytable
return item
def set_header(self, string):
self.header = string
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/formatters/webkit/ 0000775 0000000 0000000 00000000000 11430720725 0031462 5 ustar 00root root 0000000 0000000 __init__.py 0000664 0000000 0000000 00000001371 11430720725 0033516 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/formatters/webkit # -*- 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 .webkitgtk import WebkitGtkFormatter
webkitgtk.py 0000664 0000000 0000000 00000005257 11430720725 0033761 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/formatters/webkit # -*- 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
import gtk
import webkit
from weboob.tools.application.javascript import get_javascript
from ..table import TableFormatter
__all__ = ['WebkitGtkFormatter']
class WebBrowser(gtk.Window):
def __init__(self):
gtk.Window.__init__(self)
self.connect('destroy', gtk.main_quit)
self.set_default_size(800, 600)
self.web_view = webkit.WebView()
sw = gtk.ScrolledWindow()
sw.add(self.web_view)
self.add(sw)
self.show_all()
class WebkitGtkFormatter(TableFormatter):
def __init__(self):
TableFormatter.__init__(self, return_only=True, result_funcname='get_html_string')
def flush(self):
table_string = TableFormatter.flush(self)
js_filepaths = []
js_filepaths.append(get_javascript('jquery'))
js_filepaths.append(get_javascript('tablesorter'))
scripts = ['' % js_filepath for js_filepath in js_filepaths]
html_string_params = dict(table=table_string)
if scripts:
html_string_params['scripts'] = ''.join(scripts)
html_string = """
%(scripts)s
%(table)s
""" % html_string_params
web_browser = WebBrowser()
web_browser.web_view.load_html_string(html_string, 'file://%s' % os.path.abspath(os.getcwd()))
gtk.main()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/javascript.py 0000664 0000000 0000000 00000003421 11430720725 0030527 0 ustar 00root root 0000000 0000000 # -*- 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
__all__ = ['get_javascript']
def get_javascript(name, load_order=('local', 'web'), minified=True):
if name == 'jquery':
for src in load_order:
if src == 'local':
# try Debian paths
if minified:
filepath = '/usr/share/javascript/jquery/jquery.min.js'
else:
filepath = '/usr/share/javascript/jquery/jquery.js'
if os.path.exists(filepath):
return filepath
elif src == 'web':
# return Google-hosted URLs
if minified:
return 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js'
else:
return 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js'
elif name == 'tablesorter':
if 'web' in load_order:
if minified:
return 'http://tablesorter.com/jquery.tablesorter.min.js'
else:
return 'http://tablesorter.com/jquery.tablesorter.js'
return None
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/prompt.py 0000664 0000000 0000000 00000004402 11430720725 0027702 0 ustar 00root root 0000000 0000000 # -*- 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 sys
from weboob.core.ouiboube import Weboob
from weboob.core.scheduler import Scheduler
from .console import ConsoleApplication
__all__ = ['PromptApplication']
class PromptScheduler(Scheduler):
def __init__(self, prompt_cb, read_cb):
Scheduler.__init__(self)
self.read_cb = read_cb
self.prompt_cb = prompt_cb
def run(self):
try:
while not self.stop_event.isSet():
self.prompt_cb()
line = sys.stdin.readline()
if not line:
self.want_stop()
sys.stdout.write('\n')
else:
self.read_cb(line.strip())
except KeyboardInterrupt:
self._wait_to_stop()
sys.stdout.write('\n')
else:
self._wait_to_stop()
return True
class PromptApplication(ConsoleApplication):
SYNOPSIS = 'Usage: %prog [options (-h for help)]'
def create_weboob(self):
return Weboob(scheduler=PromptScheduler(self.prompt, self.read_cb))
@ConsoleApplication.command("Display this notice")
def command_help(self):
print 'Available commands:'
for name, arguments, doc_string in self._commands:
command = '%s %s' % (name, arguments)
print ' %-30s %s' % (command, doc_string)
def prompt(self):
sys.stdout.write('> ')
sys.stdout.flush()
def loop(self):
self.weboob.loop()
def read_cb(self, line):
line = line.split()
if line:
self.process_command(*line)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/qt/ 0000775 0000000 0000000 00000000000 11430720725 0026433 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/qt/Makefile 0000664 0000000 0000000 00000000264 11430720725 0030075 0 ustar 00root root 0000000 0000000 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)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/qt/__init__.py 0000664 0000000 0000000 00000000143 11430720725 0030542 0 ustar 00root root 0000000 0000000 from .qt import QtApplication, QtMainWindow, QtDo, HTMLDelegate
from .backendcfg import BackendCfg
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/qt/backendcfg.py 0000664 0000000 0000000 00000024623 11430720725 0031063 0 ustar 00root root 0000000 0000000 # -*- 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 QDialog, QTreeWidgetItem, QLabel, QLineEdit, QCheckBox, \
QMessageBox, QPixmap, QImage, QIcon, QHeaderView, \
QListWidgetItem, QTextDocument
from PyQt4.QtCore import SIGNAL, Qt, QVariant, QUrl
import re
from logging import warning
from weboob.tools.application.qt.backendcfg_ui import Ui_BackendCfg
class BackendCfg(QDialog):
def __init__(self, weboob, caps=None, parent=None):
QDialog.__init__(self, parent)
self.ui = Ui_BackendCfg()
self.ui.setupUi(self)
self.to_unload = set()
self.to_load = set()
self.weboob = weboob
self.caps = caps
self.config_widgets = {}
self.weboob.backends_loader.load_all()
self.ui.configuredBackendsList.header().setResizeMode(QHeaderView.ResizeToContents)
self.ui.configFrame.hide()
for name, backend in self.weboob.backends_loader.loaded.iteritems():
if not self.caps or backend.has_caps(*self.caps):
item = QListWidgetItem(name.capitalize())
if backend.icon_path:
img = QImage(backend.icon_path)
item.setIcon(QIcon(QPixmap.fromImage(img)))
self.ui.backendsList.addItem(item)
self.loadConfiguredBackendsList()
self.connect(self.ui.configuredBackendsList, SIGNAL('itemClicked(QTreeWidgetItem *, int)'), self.configuredBackendClicked)
self.connect(self.ui.backendsList, SIGNAL('itemSelectionChanged()'), self.backendSelectionChanged)
self.connect(self.ui.proxyBox, SIGNAL('toggled(bool)'), self.proxyEditEnabled)
self.connect(self.ui.addButton, SIGNAL('clicked()'), self.addEvent)
self.connect(self.ui.removeButton, SIGNAL('clicked()'), self.removeEvent)
self.connect(self.ui.configButtonBox, SIGNAL('accepted()'), self.acceptBackend)
self.connect(self.ui.configButtonBox, SIGNAL('rejected()'), self.rejectBackend)
def loadConfiguredBackendsList(self):
self.ui.configuredBackendsList.clear()
for instance_name, name, params in self.weboob.backends_config.iter_backends():
if name not in self.weboob.backends_loader.loaded:
continue
backend = self.weboob.backends_loader.loaded[name]
if self.caps and not backend.has_caps(*self.caps):
continue
item = QTreeWidgetItem(None, [instance_name, name])
if backend.icon_path:
img = QImage(backend.icon_path)
item.setIcon(0, QIcon(QPixmap.fromImage(img)))
self.ui.configuredBackendsList.addTopLevelItem(item)
def configuredBackendClicked(self, item, col):
bname = unicode(item.text(0))
self.editBackend(bname)
def addEvent(self):
self.editBackend()
def removeEvent(self):
item = self.ui.configuredBackendsList.currentItem()
if not item:
return
bname = unicode(item.text(0))
reply = QMessageBox.question(self, self.tr('Remove a backend'),
unicode(self.tr("Are you sure you want to remove the backend '%s'?")) % bname,
QMessageBox.Yes|QMessageBox.No)
if reply != QMessageBox.Yes:
return
self.weboob.backends_config.remove_backend(bname)
self.to_unload.add(bname)
self.ui.configFrame.hide()
self.loadConfiguredBackendsList()
def editBackend(self, bname=None):
self.ui.configFrame.show()
if bname is not None:
mname, params = self.weboob.backends_config.get_backend(bname)
items = self.ui.backendsList.findItems(mname, Qt.MatchFixedString)
if not items:
print 'Backend not found'
else:
self.ui.backendsList.setCurrentItem(items[0])
self.ui.backendsList.setEnabled(False)
self.ui.nameEdit.setText(bname)
self.ui.nameEdit.setEnabled(False)
if '_proxy' in params:
self.ui.proxyBox.setChecked(True)
self.ui.proxyEdit.setText(params.pop('_proxy'))
else:
self.ui.proxyBox.setChecked(False)
self.ui.proxyEdit.clear()
for key, value in params.iteritems():
l, widget = self.config_widgets[key]
if isinstance(widget, QLineEdit):
widget.setText(unicode(value))
elif isinstance(widget, QCheckBox):
widget.setChecked(value.lower() in ('1', 'true', 'yes', 'on'))
else:
warning('Unknown type field "%s": %s', key, widget)
else:
self.ui.nameEdit.clear()
self.ui.nameEdit.setEnabled(True)
self.ui.proxyBox.setChecked(False)
self.ui.proxyEdit.clear()
self.ui.backendsList.setEnabled(True)
self.ui.backendsList.setCurrentRow(-1)
def acceptBackend(self):
bname = unicode(self.ui.nameEdit.text())
selection = self.ui.backendsList.selectedItems()
if not selection:
QMessageBox.critical(self, self.tr('Unable to add a configured backend'),
self.tr('Please select a backend'))
return
backend = self.weboob.backends_loader.loaded[unicode(selection[0].text()).lower()]
params = {}
missing = []
if not bname:
missing.append(self.tr('Name'))
if self.ui.proxyBox.isChecked():
params['_proxy'] = unicode(self.ui.proxyEdit.text())
if not params['_proxy']:
missing.append(self.tr('Proxy'))
for key, field in backend.config.iteritems():
label, value = self.config_widgets[key]
if isinstance(value, QLineEdit):
params[key] = unicode(value.text())
elif isinstance(value, QCheckBox):
params[key] = '1' if value.isChecked() else '0'
else:
warning('Unknown type field "%s": %s', key, value)
if not params[key]:
params[key] = field.default
if not params[key]:
missing.append(field.description)
elif field.regexp and not re.match(field.regexp, params[key]):
QMessageBox.critical(self,
self.tr('Invalid value'),
unicode(self.tr('Invalid value for field "%s":\n\n%s')) % (field.description, params[key]))
return
if missing:
QMessageBox.critical(self,
self.tr('Missing fields'),
unicode(self.tr('Please set a value in this fields:\n%s')) % ('\n'.join(['- %s' % s for s in missing])))
return
self.weboob.backends_config.add_backend(bname, backend.name, params, edit=not self.ui.nameEdit.isEnabled())
self.to_load.add(bname)
self.ui.configFrame.hide()
self.loadConfiguredBackendsList()
def rejectBackend(self):
self.ui.configFrame.hide()
def backendSelectionChanged(self):
for key, (label, value) in self.config_widgets.iteritems():
label.hide()
value.hide()
self.ui.configLayout.removeWidget(label)
self.ui.configLayout.removeWidget(value)
self.config_widgets.clear()
self.ui.backendInfo.clear()
selection = self.ui.backendsList.selectedItems()
if not selection:
return
backend = self.weboob.backends_loader.loaded[unicode(selection[0].text()).lower()]
if backend.icon_path:
img = QImage(backend.icon_path)
self.ui.backendInfo.document().addResource(QTextDocument.ImageResource, QUrl('mydata://logo.png'), QVariant(img))
self.ui.backendInfo.setText(unicode(self.tr(
'
%s Backend %s
'
'Version: %s '
'Maintainer: %s '
'License: %s '
'Description: %s '
'Capabilities: %s '))
% ('' if backend.icon_path else '',
backend.name.capitalize(),
backend.version,
backend.maintainer.replace('&', '&').replace('<', '<').replace('>', '>'),
backend.license,
backend.description,
', '.join([cap.__name__ for cap in backend.iter_caps()])))
for key, field in backend.config.iteritems():
label = QLabel(u'%s:' % field.description)
if isinstance(field.default, bool):
value = QCheckBox()
if field.default:
value.setChecked(True)
else:
value = QLineEdit()
if field.default is not None:
value.setText(unicode(field.default))
if field.is_masked:
value.setEchoMode(value.Password)
self.ui.configLayout.addRow(label, value)
self.config_widgets[key] = (label, value)
def proxyEditEnabled(self, state):
self.ui.proxyEdit.setEnabled(state)
def run(self):
self.exec_()
ret = (len(self.to_load) > 0 or len(self.to_unload) > 0)
self.weboob.unload_backends(self.to_unload)
self.weboob.load_configured_backends(names=self.to_load)
return ret
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/qt/backendcfg.ui 0000664 0000000 0000000 00000017335 11430720725 0031052 0 ustar 00root root 0000000 0000000
BackendCfg00645652Backends configurationQt::VerticalQAbstractItemView::NoEditTriggers4848falsefalsefalsetruetruefalsefalsefalsetruefalsetrueNameBackendQFrame::StyledPanelQFrame::RaisedAddRemoveQt::Vertical2040QFrame::StyledPanelQFrame::RaisedAvailable backends0032321true10QFrame::NoFrameQFrame::Plain00trueName:Proxy:falseQt::HorizontalQDialogButtonBox::Cancel|QDialogButtonBox::OkQDialogButtonBox::ClosebuttonBoxclicked(QAbstractButton*)BackendCfgaccept()312591312306
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/qt/qt.py 0000664 0000000 0000000 00000012446 11430720725 0027440 0 ustar 00root root 0000000 0000000 # -*- 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 sys
from PyQt4.QtCore import QTimer, SIGNAL, QObject, QString, QSize
from PyQt4.QtGui import QMainWindow, QApplication, QStyledItemDelegate, \
QStyleOptionViewItemV4, QTextDocument, QStyle, \
QAbstractTextDocumentLayout, QPalette
from weboob.core.ouiboube import Weboob
from weboob.core.scheduler import IScheduler
from ..base import BaseApplication
__all__ = ['QtApplication', 'QtMainWindow', 'QtDo', 'HTMLDelegate']
class QtScheduler(IScheduler):
def __init__(self, app):
self.app = app
self.count = 0
self.timers = {}
def schedule(self, interval, function, *args):
timer = QTimer()
timer.setInterval(interval * 1000)
timer.setSingleShot(True)
count = self.count
self.count += 1
timer.start()
self.app.connect(timer, SIGNAL("timeout()"), lambda: self.timeout(count, None, function, *args))
self.timers[count] = timer
def repeat(self, interval, function, *args):
timer = QTimer()
timer.setSingleShot(False)
count = self.count
self.count += 1
timer.start(0)
self.app.connect(timer, SIGNAL("timeout()"), lambda: self.timeout(count, interval, function, *args))
self.timers[count] = timer
def timeout(self, _id, interval, function, *args):
function(*args)
if interval is None:
self.timers.pop(_id)
else:
self.timers[_id].setInterval(interval * 1000)
def want_stop(self):
self.app.quit()
def run(self):
self.app.exec_()
class QtApplication(QApplication, BaseApplication):
def __init__(self):
QApplication.__init__(self, sys.argv)
self.setApplicationName(self.APPNAME)
BaseApplication.__init__(self)
def create_weboob(self):
return Weboob(scheduler=QtScheduler(self))
class QtMainWindow(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
class QtDo(QObject):
def __init__(self, weboob, cb, eb=None):
QObject.__init__(self)
if not eb:
eb = self.default_eb
self.weboob = weboob
self.process = None
self.cb = cb
self.eb = eb
self.connect(self, SIGNAL('cb'), self.local_cb)
self.connect(self, SIGNAL('eb'), self.local_eb)
def do(self, *args, **kwargs):
self.process = self.weboob.do(*args, **kwargs)
self.process.callback_thread(self.thread_cb, self.thread_eb)
def default_eb(self, backend, error, backtrace):
# TODO display a messagebox
print error
print backtrace
def local_cb(self, backend, data):
self.cb(backend, data)
if not backend:
self.disconnect(self, SIGNAL('cb'), self.local_cb)
self.disconnect(self, SIGNAL('eb'), self.local_eb)
def local_eb(self, backend, error, backtrace):
self.eb(backend, error, backtrace)
self.disconnect(self, SIGNAL('cb'), self.local_cb)
self.disconnect(self, SIGNAL('eb'), self.local_eb)
def thread_cb(self, backend, data):
self.emit(SIGNAL('cb'), backend, data)
def thread_eb(self, backend, error, backtrace):
self.emit(SIGNAL('eb'), backend, error, backtrace)
class HTMLDelegate(QStyledItemDelegate):
def paint(self, painter, option, index):
optionV4 = QStyleOptionViewItemV4(option)
self.initStyleOption(optionV4, index)
style = optionV4.widget.style() if optionV4.widget else QApplication.style()
doc = QTextDocument()
doc.setHtml(optionV4.text)
# painting item without text
optionV4.text = QString()
style.drawControl(QStyle.CE_ItemViewItem, optionV4, painter)
ctx = QAbstractTextDocumentLayout.PaintContext()
# Hilight text if item is selected
if optionV4.state & QStyle.State_Selected:
ctx.palette.setColor(QPalette.Text, optionV4.palette.color(QPalette.Active, QPalette.HighlightedText))
textRect = style.subElementRect(QStyle.SE_ItemViewItemText, optionV4)
painter.save()
painter.translate(textRect.topLeft())
painter.setClipRect(textRect.translated(-textRect.topLeft()))
doc.documentLayout().draw(painter, ctx)
painter.restore()
def sizeHint(self, option, index):
optionV4 = QStyleOptionViewItemV4(option)
self.initStyleOption(optionV4, index)
doc = QTextDocument()
doc.setHtml(optionV4.text)
doc.setTextWidth(optionV4.rect.width())
return QSize(doc.idealWidth(), max(doc.size().height(), optionV4.decorationSize.height()))
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/application/results.py 0000664 0000000 0000000 00000007114 11430720725 0030065 0 ustar 00root root 0000000 0000000 # -*- 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.
__all__ = ['Results', 'ResultsCondition', 'ResultsConditionException']
class Results(object):
def __init__(self, name=u'', header=None):
self.name = name
self._groups = []
self._items = []
self.header = header
def add_item(self, item):
self._items.append(item)
def add_items(self, items):
self._items.extend(items)
def iter_items(self):
return iter(self._items)
def deep_iter_items(self):
for i in self._items:
yield i
for g in self._groups:
for i in g.iter_items():
yield i
def add_group(self, group):
self._groups.append(group)
def get_group(self, name):
l = [group for group in self._groups if group.name == name]
if l:
return l[0]
else:
return None
def get_or_create_group(self, name, group_class=None):
if group_class is None:
group_class = Results
group = self.get_group(name)
if group:
return group
else:
new_group = group_class(name)
self.add_group(new_group)
return new_group
def iter_groups(self):
return iter(self._groups)
class ResultsCondition(object):
def __init__(self, condition_str):
condition_str = condition_str.replace('OR', 'or') \
.replace('AND', 'and') \
.replace('NOT', 'not')
or_list = []
try:
for _or in condition_str.split('or'):
and_dict = {}
for _and in _or.split('and'):
if '!=' in _and:
k, v = _and.split('!=')
k += '!'
elif '=' in _and:
k, v = _and.split('=')
else:
raise ResultsConditionException(u'Could not find = or != operator in sub-expression "%s"' % _and)
and_dict[k] = v
or_list.append(and_dict)
except:
raise ResultsConditionException(u'Could not parse condition expression "%s"' % condition_str)
self.condition = or_list
def is_valid(self, d):
for _or in self.condition:
for k, v in _or.iteritems():
if k.endswith('!'):
k = k[:-1]
different = True
else:
different = False
if k in d:
if different:
if d[k] == v:
return False
else:
if d[k] != v:
return False
else:
raise ResultsConditionException(u'Field "%s" is not valid.' % k)
return True
class ResultsConditionException(Exception):
pass
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/backend.py 0000664 0000000 0000000 00000016164 11430720725 0025455 0 ustar 00root root 0000000 0000000 # -*- 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 re
import os
from threading import RLock
from logging import debug
from weboob.capabilities.cap import ICap
__all__ = ['BaseBackend', 'ObjectNotSupported']
class ObjectNotSupported(Exception): pass
class BackendStorage(object):
def __init__(self, name, storage):
self.name = name
self.storage = storage
def set(self, *args):
if self.storage:
return self.storage.set('backends', self.name, *args)
def get(self, *args, **kwargs):
if self.storage:
return self.storage.get('backends', self.name, *args, **kwargs)
else:
return kwargs.get('default', None)
def load(self, default):
if self.storage:
return self.storage.load('backends', self.name, default)
def save(self):
if self.storage:
return self.storage.save('backends', self.name)
class BaseBackend(object):
# Backend name.
NAME = None
# Name of the maintainer of this backend.
MAINTAINER = ''
# Email address of the maintainer.
EMAIL = ''
# Version of backend (for information only).
VERSION = ''
# Description
DESCRIPTION = ''
# License of this backend.
LICENSE = ''
# Icon file path
ICON = None
# Configuration required for this backend.
# Values must be ConfigField objects.
CONFIG = {}
# Storage
STORAGE = {}
# Browser class
BROWSER = None
# Test class
TEST = None
# Supported objects to fill
# The key is the class and the value the method to call to fill
# Method prototype: method(object, fields)
# When the method is called, fields are only the one which are
# NOT yet filled.
OBJECTS = {}
class ConfigField(object):
def __init__(self, default=None, is_masked=False, regexp=None, description=None):
self.default = default
self.is_masked = is_masked
self.regexp = regexp
self.description = description
class ConfigError(Exception): pass
def __enter__(self):
self.lock.acquire()
def __exit__(self, t, v, tb):
self.lock.release()
def __repr__(self):
return u"" % self.name
def __init__(self, weboob, name, config, storage):
self.weboob = weboob
self.name = name
self.lock = RLock()
# Private fields (which start with '_')
self._private_config = dict([(key, value) for key, value in config.iteritems() if key.startswith('_')])
# Configuration of backend
self.config = {}
for name, field in self.CONFIG.iteritems():
value = config.get(name, field.default)
if value is None:
raise BaseBackend.ConfigError('Missing parameter "%s" (%s)' % (name, field.description))
if field.regexp and not re.match(field.regexp, str(value)):
raise BaseBackend.ConfigError('Value of "%s" does not match regexp "%s"' % (name, field.regexp))
if not field.default is None:
if isinstance(field.default, bool) and not isinstance(value, bool):
value = value.lower() in ('1', 'true', 'on', 'yes')
elif isinstance(field.default, int) and not isinstance(value, int):
value = int(value)
elif isinstance(field.default, float) and not isinstance(value, float):
value = float(value)
self.config[name] = value
self.storage = BackendStorage(self.name, storage)
self.storage.load(self.STORAGE)
def deinit(self):
"""
This abstract method is called when the backend is unloaded.
"""
pass
@property
def browser(self):
"""
Attribute 'browser'. The browser is created at the first call
of this attribute, to avoid useless pages access.
Note that the 'create_default_browser' method is called to create it.
"""
if not hasattr(self, '_browser'):
self._browser = self.create_default_browser()
return self._browser
def create_default_browser(self):
"""
Method to overload to build the default browser in
attribute 'browser'.
"""
return self.create_browser()
def create_browser(self, *args, **kwargs):
"""
Build a browser from the BROWSER class attribute and the
given arguments.
"""
if not self.BROWSER:
return None
if '_proxy' in self._private_config:
kwargs['proxy'] = self._private_config['_proxy']
elif 'HTTP_PROXY' in os.environ:
kwargs['proxy'] = os.environ['HTTP_PROXY']
return self.BROWSER(*args, **kwargs)
def iter_caps(self):
for cap in self.__class__.__bases__:
if issubclass(cap, ICap) and cap != ICap:
yield cap
def has_caps(self, *caps):
for c in caps:
if (isinstance(c, (unicode,str)) and c in [cap.__name__ for cap in self.iter_caps()]) or \
isinstance(self, c):
return True
return False
def get_test(self):
if not self.TEST:
return None
return self.TEST(self)
def fillobj(self, obj, fields):
missing_fields = []
for field in fields:
if not hasattr(obj, field):
continue
value = getattr(obj, field)
missing = False
if isinstance(value, dict):
for v in value.itervalues():
if hasattr(v, '__iscomplete__') and not v.__iscomplete__():
missing = True
break
elif isinstance(value, (list,tuple)):
for v in value:
if hasattr(v, '__iscomplete__') and not v.__iscomplete__():
missing = True
break
elif not value or hasattr(value, '__iscomplete__') and not value.__iscomplete__():
missing = True
if missing:
missing_fields.append(field)
for key, value in self.OBJECTS.iteritems():
if isinstance(obj, key):
debug('Complete %r with fields: %s' % (obj, missing_fields))
return value(self, obj, missing_fields) or obj
raise ObjectNotSupported('The object of type %s is not supported by the backend %s' % (type(obj), self))
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/browser/ 0000775 0000000 0000000 00000000000 11430720725 0025167 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/browser/__init__.py 0000664 0000000 0000000 00000001372 11430720725 0027303 0 ustar 00root root 0000000 0000000 # -*- 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 weboob.tools.browser.browser import *
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/browser/browser.py 0000664 0000000 0000000 00000032135 11430720725 0027230 0 ustar 00root root 0000000 0000000 # -*- 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 mechanize
import urllib
import urllib2
from weboob.tools.mech import ClientForm
ControlNotFoundError = ClientForm.ControlNotFoundError
import re
import time
from logging import warning, debug
from copy import copy
from threading import RLock
from weboob.tools.parsers import get_parser
from weboob.tools.decorators import retry
# Try to load cookies
try:
from .firefox_cookies import FirefoxCookieJar
except ImportError, e:
warning("Unable to store cookies: %s" % e)
HAVE_COOKIES = False
else:
HAVE_COOKIES = True
__all__ = ['BrowserIncorrectPassword', 'BrowserBanned', 'BrowserUnavailable', 'BrowserRetry',
'BrowserHTTPError', 'BasePage', 'BaseBrowser']
# Exceptions
class BrowserIncorrectPassword(Exception):
pass
class BrowserBanned(BrowserIncorrectPassword):
pass
class BrowserUnavailable(Exception):
pass
class BrowserHTTPError(BrowserUnavailable):
pass
class BrowserRetry(Exception):
pass
class NoHistory(object):
"""
We don't want to fill memory with history
"""
def __init__(self):
pass
def add(self, request, response):
pass
def back(self, n, _response):
pass
def clear(self):
pass
def close(self):
pass
class BasePage(object):
"""
Base page
"""
def __init__(self, browser, document, url='', groups=None, group_dict=None):
self.browser = browser
self.document = document
self.url = url
self.groups = groups
self.group_dict = group_dict
def on_loaded(self):
"""
Called when the page is loaded.
"""
pass
class BaseBrowser(mechanize.Browser):
"""
Base browser class to navigate on a website.
"""
# ------ Class attributes --------------------------------------
DOMAIN = None
PROTOCOL = 'http'
ENCODING = 'utf-8'
PAGES = {}
USER_AGENTS = {
'desktop_firefox': 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.4) Gecko/2008111318 Ubuntu/8.10 (intrepid) Firefox/3.0.3',
'android': 'Mozilla/5.0 (Linux; U; Android 2.1; en-us; Nexus One Build/ERD62) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17',
}
USER_AGENT = USER_AGENTS['desktop_firefox']
# ------ Abstract methods --------------------------------------
def home(self):
"""
Go to the home page.
"""
if self.DOMAIN is not None:
self.location('%s://%s' % (self.PROTOCOL, self.DOMAIN))
def login(self):
"""
Login to the website.
This function is called when is_logged() returns False and the password
attribute is not None.
"""
raise NotImplementedError()
def is_logged(self):
"""
Return True if we are logged on website. When Browser tries to access
to a page, if this method returns False, it calls login().
It is never called if the password attribute is None.
"""
raise NotImplementedError()
# ------ Browser methods ---------------------------------------
# I'm not a robot, so disable the check of permissions in robots.txt.
default_features = copy(mechanize.Browser.default_features)
default_features.remove('_robots')
def __init__(self, username=None, password=None, firefox_cookies=None,
parser=None, history=NoHistory(), proxy=None):
"""
Constructor of Browser.
@param username [str] username on website.
@param password [str] password on website. If it is None, Browser will
not try to login.
@param filefox_cookies [str] Path to cookies' sqlite file.
@param parser [IParser] parser to use on HTML files.
@param hisory [object] History manager. Default value is an object
which does not keep history.
"""
mechanize.Browser.__init__(self, history=history)
self.addheaders = [
['User-agent', self.USER_AGENT]
]
# Use a proxy
self.proxy = proxy
if proxy:
proto = 'http'
if proxy.find('://') >= 0:
proto, domain = proxy.split('://', 1)
else:
domain = proxy
self.set_proxies({proto: domain})
# Share cookies with firefox
if firefox_cookies and HAVE_COOKIES:
self._cookie = FirefoxCookieJar(self.DOMAIN, firefox_cookies)
self._cookie.load()
self.set_cookiejar(self._cookie)
else:
self._cookie = None
if parser is None:
parser = get_parser()()
elif isinstance(parser, (tuple,list)):
parser = get_parser(parser)()
self.parser = parser
self.page = None
self.last_update = 0.0
self.username = username
self.password = password
self.lock = RLock()
if self.password:
try:
self.home()
# Do not abort the build of browser when the website is down.
except BrowserUnavailable:
pass
def __enter__(self):
self.lock.acquire()
def __exit__(self, t, v, tb):
self.lock.release()
def pageaccess(func):
def inner(self, *args, **kwargs):
if not self.page or self.password and not self.page.is_logged():
self.home()
return func(self, *args, **kwargs)
return inner
@pageaccess
def keepalive(self):
self.home()
def check_location(func):
def inner(self, *args, **kwargs):
if args and isinstance(args[0], (str,unicode)) and args[0][0] == '/' and \
(not self.request or self.request.host != self.DOMAIN):
args = ('%s://%s%s' % (self.PROTOCOL, self.DOMAIN, args[0]),) + args[1:]
return func(self, *args, **kwargs)
return inner
@check_location
@retry(BrowserHTTPError, tries=3)
def openurl(self, *args, **kwargs):
"""
Open an URL but do not create a Page object.
"""
if_fail = kwargs.pop('if_fail', 'raise')
debug('Opening URL "%s", %s' % (args, kwargs))
try:
return mechanize.Browser.open_novisit(self, *args, **kwargs)
except (mechanize.response_seek_wrapper, urllib2.HTTPError, urllib2.URLError), e:
if if_fail == 'raise':
raise BrowserHTTPError('%s (url="%s")' % (e, args and args[0] or 'None'))
else:
return None
except (mechanize.BrowserStateError, BrowserRetry):
self.home()
return mechanize.Browser.open(self, *args, **kwargs)
def readurl(self, url, if_fail=None):
"""
Download URL data specifying what to do on failure (nothing by default).
"""
result = self.openurl(url, if_fail=if_fail)
if result:
return result.read()
else:
return None
def submit(self, *args, **kwargs):
"""
Submit the selected form.
"""
try:
self._change_location(mechanize.Browser.submit(self, *args, **kwargs))
except (mechanize.response_seek_wrapper, urllib2.HTTPError, urllib2.URLError), e:
self.page = None
raise BrowserHTTPError(e)
except (mechanize.BrowserStateError, BrowserRetry), e:
self.home()
raise BrowserUnavailable(e)
def is_on_page(self, pageCls):
return isinstance(self.page, pageCls)
def follow_link(self, *args, **kwargs):
try:
self._change_location(mechanize.Browser.follow_link(self, *args, **kwargs))
except (mechanize.response_seek_wrapper, urllib2.HTTPError, urllib2.URLError), e:
self.page = None
raise BrowserHTTPError('%s (url="%s")' % (e, args and args[0] or 'None'))
except (mechanize.BrowserStateError, BrowserRetry), e:
self.home()
raise BrowserUnavailable(e)
@check_location
@retry(BrowserHTTPError, tries=3)
def location(self, *args, **kwargs):
"""
Change location of browser on an URL.
When the page is loaded, it looks up PAGES to find a regexp which
matches, and create the object. Then, the 'on_loaded' method of
this object is called.
If a password is set, and is_logged() returns False, it tries to login
with login() and reload the page.
"""
keep_args = copy(args)
keep_kwargs = kwargs.copy()
try:
self._change_location(mechanize.Browser.open(self, *args, **kwargs))
except BrowserRetry:
if not self.page or not args or self.page.url != args[0]:
self.location(keep_args, keep_kwargs)
except (mechanize.response_seek_wrapper, urllib2.HTTPError, urllib2.URLError), e:
self.page = None
raise BrowserHTTPError('%s (url="%s")' % (e, args and args[0] or 'None'))
except mechanize.BrowserStateError:
self.home()
self.location(*keep_args, **keep_kwargs)
def _change_location(self, result):
"""
This function is called when we have moved to a page, to load a Page
object.
"""
# Find page from url
pageCls = None
page_groups = None
page_group_dict = None
for key, value in self.PAGES.items():
regexp = re.compile('^%s$' % key)
m = regexp.match(result.geturl())
if m:
pageCls = value
page_groups = m.groups()
page_group_dict = m.groupdict()
break
# Not found
if not pageCls:
self.page = None
#data = result.read()
#if isinstance(data, unicode):
# data = data.encode('utf-8')
#print data
warning('Oh my fucking god, there isn\'t any page corresponding to URL %s' % result.geturl())
return
debug('[user_id=%s] Went on %s' % (self.username, result.geturl()))
self.last_update = time.time()
document = self.parser.parse(result, self.ENCODING)
self.page = pageCls(self, document, result.geturl(), groups=page_groups, group_dict=page_group_dict)
self.page.on_loaded()
if self.password is not None and not self.is_logged():
debug('!! Relogin !!')
self.login()
return
if self._cookie:
self._cookie.save()
def buildurl(self, base, *args, **kwargs):
"""
Build an URL and escape arguments.
You can give a serie of tuples in *args (and the order is keept), or
a dict in **kwargs (but the order is lost).
Example:
>>> buildurl('/blah.php', ('a', '&'), ('c', '=')
'/blah.php?a=%26&b=%3D'
>>> buildurl('/blah.php', a='&', 'c'='=')
'/blah.php?b=%3D&a=%26'
"""
if not args:
args = kwargs
if not args:
return base
else:
return '%s?%s' % (base, urllib.urlencode(args))
def str(self, s):
if isinstance(s, unicode):
s = s.encode('iso-8859-15', 'replace')
return s
def set_field(self, args, label, field=None, value=None, is_list=False):
"""
Set a value to a form field.
@param args [dict] arguments where to look for value.
@param label [str] label in args.
@param field [str] field name. If None, use label instead.
@param value [str] value to give on field.
@param is_list [bool] the field is a list.
"""
try:
if not field:
field = label
if args.get(label, None) is not None:
if not value:
if is_list:
if isinstance(is_list, (list, tuple)):
try:
value = [self.str(is_list.index(args[label]))]
except ValueError, e:
if args[label]:
print '[%s] %s: %s' % (label, args[label], e)
return
else:
value = [self.str(args[label])]
else:
value = self.str(args[label])
self[field] = value
except ControlNotFoundError:
return
def fillobj(self, obj, fields):
raise NotImplementedError()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/browser/decorators.py 0000664 0000000 0000000 00000003040 11430720725 0027703 0 ustar 00root root 0000000 0000000 # -*- 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.
__all__ = ['check_domain', 'id2url']
def check_domain(domain):
def wrapper(func):
def inner(self, *args, **kwargs):
if self.DOMAIN not in args[0]:
return None
return func(self, *args, **kwargs)
return inner
return wrapper
def id2url(id2url):
def wrapper(func):
def inner(self, *args, **kwargs):
arg = unicode(args[0])
if arg.startswith('http://'):
if self.DOMAIN in arg:
url = arg
else:
return None
else:
url = id2url(arg)
if url is None:
return None
new_args = [url]
new_args.extend(args[1:])
return func(self, *new_args, **kwargs)
return inner
return wrapper
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/browser/firefox_cookies.py 0000664 0000000 0000000 00000007230 11430720725 0030721 0 ustar 00root root 0000000 0000000 # -*- 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.
try:
import sqlite3 as sqlite
except ImportError, e:
from pysqlite2 import dbapi2 as sqlite
from mechanize import CookieJar, Cookie
__all__ = ['FirefoxCookieJar']
class FirefoxCookieJar(CookieJar):
def __init__(self, domain, sqlite_file=None, policy=None):
CookieJar.__init__(self, policy)
self.domain = domain
self.sqlite_file = sqlite_file
def __connect(self):
try:
db = sqlite.connect(database=self.sqlite_file, timeout=10.0)
except sqlite.OperationalError, err:
print 'Unable to open %s database: %s' % (self.sqlite_file, err)
return None
return db
def load(self):
db = self.__connect()
if not db: return
cookies = db.execute("""SELECT host, path, name, value, expiry, lastAccessed, isSecure
FROM moz_cookies
WHERE host LIKE '%%%s%%'""" % self.domain)
for entry in cookies:
domain = entry[0]
initial_dot = domain.startswith(".")
domain_specified = initial_dot
path = entry[1]
name = entry[2]
value = entry[3]
expires = entry[4]
secure = entry[6]
discard = False
c = Cookie(0, name, value,
None, False,
domain, domain_specified, initial_dot,
path, False,
secure,
expires,
discard,
None,
None,
{})
#if not ignore_discard and c.discard:
# continue
#if not ignore_expires and c.is_expired(now):
# continue
self.set_cookie(c)
def save(self):
db = self.__connect()
if not db: return
db.execute("DELETE FROM moz_cookies WHERE host LIKE '%%%s%%'" % self.domain)
for cookie in self:
if cookie.secure: secure = 1
else: secure = 0
if cookie.expires is not None:
expires = cookie.expires
else:
expires = 0
if cookie.value is None:
# cookies.txt regards 'Set-Cookie: foo' as a cookie
# with no name, whereas cookielib regards it as a
# cookie with no value.
name = ""
value = cookie.name
else:
name = cookie.name
value = cookie.value
# XXX ugly hack to keep this cookie
if name == 'PHPSESSID':
expires = 1854242393
db.execute("""INSERT INTO moz_cookies (host, path, name, value, expiry, isSecure)
VALUES (?, ?, ?, ?, ?, ?)""",
(cookie.domain, cookie.path, name, value, int(expires), int(secure)))
db.commit()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/config/ 0000775 0000000 0000000 00000000000 11430720725 0024751 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/config/__init__.py 0000664 0000000 0000000 00000000000 11430720725 0027050 0 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/config/iconfig.py 0000664 0000000 0000000 00000002012 11430720725 0026734 0 ustar 00root root 0000000 0000000 # -*- 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.
class ConfigError(Exception):
pass
class IConfig:
def load(self, default={}):
raise NotImplementedError()
def save(self):
raise NotImplementedError()
def set(self, *args):
raise NotImplementedError()
def get(self, *args, **kwargs):
raise NotImplementedError()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/config/iniconfig.py 0000664 0000000 0000000 00000006440 11430720725 0027274 0 ustar 00root root 0000000 0000000 # -*- 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 __future__ import with_statement
from ConfigParser import SafeConfigParser
import logging
import os
from .iconfig import IConfig
__all__ = ['INIConfig']
class INIConfig(IConfig):
def __init__(self, path):
self.path = path
self.values = {}
self.config = SafeConfigParser()
def load(self, default={}):
def load_section(section):
sections = section.split(':')
if len(sections) > 1:
result = {}
for s in sections:
result[s] = load_section(s)
return result
else:
return {section: dict(self.config.items(section))}
self.values = default.copy()
if os.path.exists(self.path):
self.config.read(self.path)
for section in self.config.sections():
self.values = load_section(section)
self.values.update(self.config.items('DEFAULT'))
logging.debug(u'Application configuration file loaded: %s.' % self.path)
else:
self.save()
logging.debug(u'Application configuration file created with default values: %s. '
'Please customize it.' % self.path)
return self.values
def save(self):
def save_section(values, root_section=None):
for k, v in values.iteritems():
if isinstance(v, (int, float, str, unicode)):
if root_section is not None and not self.config.has_section(root_section):
self.config.add_section(root_section)
self.config.set(root_section, k, unicode(v))
elif isinstance(v, dict):
new_section = ':'.join((root_section, k)) if root_section else k
if not self.config.has_section(new_section):
self.config.add_section(new_section)
save_section(v, new_section)
save_section(self.values)
with open(self.path, 'w') as f:
self.config.write(f)
def get(self, *args, **kwargs):
default = None
if 'default' in kwargs:
default = kwargs['default']
v = self.values
for k in args[:-1]:
if k in v:
v = v[k]
else:
return default
try:
return v[args[-1]]
except KeyError:
return default
def set(self, *args):
v = self.values
for k in args[:-2]:
if k not in v:
v[k] = {}
v = v[k]
v[args[-2]] = args[-1]
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/config/yamlconfig.py 0000664 0000000 0000000 00000005163 11430720725 0027460 0 ustar 00root root 0000000 0000000 # -*- 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 os
import tempfile
import logging
import yaml
from .iconfig import IConfig, ConfigError
__all__ = ['YamlConfig']
class YamlConfig(IConfig):
def __init__(self, path):
self.path = path
self.values = {}
def load(self, default={}):
self.values = default.copy()
try:
with open(self.path, 'r') as f:
self.values = yaml.load(f)
logging.debug(u'Application configuration file loaded: %s.' % self.path)
except IOError:
self.save()
logging.debug(u'Application configuration file created with default values: %s. Please customize it.' % self.path)
if self.values is None:
self.values = {}
def save(self):
# write in a temporary file to avoid corruption problems
fd, path = tempfile.mkstemp(dir=os.path.dirname(self.path))
with os.fdopen(fd, 'w') as f:
yaml.dump(self.values, f)
os.rename(path, self.path)
def get(self, *args, **kwargs):
default = None
if 'default' in kwargs:
default = kwargs['default']
v = self.values
for a in args[:-1]:
try:
v = v[a]
except KeyError:
if not default is None:
v[a] = {}
v = v[a]
else:
raise ConfigError()
except TypeError:
raise ConfigError()
try:
v = v[args[-1]]
except KeyError:
v[args[-1]] = default
v = v[args[-1]]
return v
def set(self, *args):
v = self.values
for a in args[:-2]:
try:
v = v[a]
except KeyError:
v[a] = {}
v = v[a]
except TypeError:
raise ConfigError()
v[args[-2]] = args[-1]
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/decorators.py 0000664 0000000 0000000 00000003272 11430720725 0026227 0 ustar 00root root 0000000 0000000 # -*- 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
import time
__all__ = ['retry']
def retry(ExceptionToCheck, tries=4, delay=3, backoff=2):
"""
Retry decorator
from http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/
original from http://wiki.python.org/moin/PythonDecoratorLibrary#Retry
"""
def deco_retry(f):
def f_retry(*args, **kwargs):
mtries, mdelay = tries, delay
try_one_last_time = True
while mtries > 1:
try:
return f(*args, **kwargs)
try_one_last_time = False
break
except ExceptionToCheck, e:
logging.debug(u'%s, Retrying in %d seconds...' % (e, mdelay))
time.sleep(mdelay)
mtries -= 1
mdelay *= backoff
if try_one_last_time:
return f(*args, **kwargs)
return
return f_retry # true decorator
return deco_retry
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/mech.py 0000664 0000000 0000000 00000001546 11430720725 0025000 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright(C) 2010 Laurent Bachelier
#
# 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.
__all__ = ['ClientForm', 'mechanize']
import mechanize
if hasattr(mechanize, "FormParser"):
ClientForm = mechanize
else:
import ClientForm
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/misc.py 0000664 0000000 0000000 00000004614 11430720725 0025016 0 ustar 00root root 0000000 0000000 # -*- 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 dateutil import tz
import sys
import traceback
import types
__all__ = ['to_unicode', 'local2utc', 'html2text', 'get_backtrace', 'iter_fields']
def to_unicode(text):
r"""
>>> to_unicode('ascii')
u'ascii'
>>> to_unicode(u'utf\xe9'.encode('UTF-8'))
u'utf\xe9'
>>> to_unicode(u'unicode')
u'unicode'
"""
if isinstance(text, unicode):
return text
if not isinstance(text, str):
text = str(text)
try:
return unicode(text, "utf8")
except UnicodeError:
pass
return unicode(text, "ISO-8859-1")
def local2utc(d):
d = d.replace(tzinfo=tz.tzlocal())
d = d.astimezone(tz.tzutc())
return d
def utc2local(d):
d = d.replace(tzinfo=tz.tzutc())
d = d.astimezone(tz.tzlocal())
return d
try:
import html2text as h2t
h2t.UNICODE_SNOB = 1
h2t.SKIP_INTERNAL_LINKS = True
html2text = h2t.html2text
except ImportError:
def html2text(s):
return s
def get_backtrace(empty="Empty backtrace."):
"""
Try to get backtrace as string.
Returns "Error while trying to get backtrace" on failure.
"""
try:
info = sys.exc_info()
trace = traceback.format_exception(*info)
sys.exc_clear()
if trace[0] != "None\n":
return "".join(trace)
except:
# No i18n here (imagine if i18n function calls error...)
return "Error while trying to get backtrace"
return empty
def iter_fields(obj):
for attribute_name in dir(obj):
if attribute_name.startswith('_'):
continue
attribute = getattr(obj, attribute_name)
if not isinstance(attribute, types.MethodType):
yield attribute_name, attribute
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/ordereddict.py 0000664 0000000 0000000 00000007707 11430720725 0026361 0 ustar 00root root 0000000 0000000 # -*- 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.
try:
from ordereddict import OrderedDict
except:
## {{{ http://code.activestate.com/recipes/576693/ (r6)
from UserDict import DictMixin
class OrderedDict(dict, DictMixin):
def __init__(self, *args, **kwds):
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
try:
self.__end
except AttributeError:
self.clear()
self.update(*args, **kwds)
def clear(self):
self.__end = end = []
end += [None, end, end] # sentinel node for doubly linked list
self.__map = {} # key --> [key, prev, next]
dict.clear(self)
def __setitem__(self, key, value):
if key not in self:
end = self.__end
curr = end[1]
curr[2] = end[1] = self.__map[key] = [key, curr, end]
dict.__setitem__(self, key, value)
def __delitem__(self, key):
dict.__delitem__(self, key)
key, prev, next = self.__map.pop(key)
prev[2] = next
next[1] = prev
def __iter__(self):
end = self.__end
curr = end[2]
while curr is not end:
yield curr[0]
curr = curr[2]
def __reversed__(self):
end = self.__end
curr = end[1]
while curr is not end:
yield curr[0]
curr = curr[1]
def popitem(self, last=True):
if not self:
raise KeyError('dictionary is empty')
if last:
key = reversed(self).next()
else:
key = iter(self).next()
value = self.pop(key)
return key, value
def __reduce__(self):
items = [[k, self[k]] for k in self]
tmp = self.__map, self.__end
del self.__map, self.__end
inst_dict = vars(self).copy()
self.__map, self.__end = tmp
if inst_dict:
return (self.__class__, (items,), inst_dict)
return self.__class__, (items,)
def keys(self):
return list(self)
setdefault = DictMixin.setdefault
update = DictMixin.update
pop = DictMixin.pop
values = DictMixin.values
items = DictMixin.items
iterkeys = DictMixin.iterkeys
itervalues = DictMixin.itervalues
iteritems = DictMixin.iteritems
def __repr__(self):
if not self:
return '%s()' % (self.__class__.__name__,)
return '%s(%r)' % (self.__class__.__name__, self.items())
def copy(self):
return self.__class__(self)
@classmethod
def fromkeys(cls, iterable, value=None):
d = cls()
for key in iterable:
d[key] = value
return d
def __eq__(self, other):
if isinstance(other, OrderedDict):
return len(self)==len(other) and self.items() == other.items()
return dict.__eq__(self, other)
def __ne__(self, other):
return not self == other
## end of http://code.activestate.com/recipes/576693/ }}}
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/parsers/ 0000775 0000000 0000000 00000000000 11430720725 0025163 5 ustar 00root root 0000000 0000000 woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/parsers/__init__.py 0000664 0000000 0000000 00000003732 11430720725 0027301 0 ustar 00root root 0000000 0000000 # -*- 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.
import logging
__all__ = ['get_parser', 'NoParserFound']
class NoParserFound(Exception): pass
def load_lxml():
from .lxmlparser import LxmlHtmlParser
return LxmlHtmlParser
def load_lxmlsoup():
from .lxmlsoupparser import LxmlSoupParser
return LxmlSoupParser
def load_html5lib():
from .html5libparser import Html5libParser
return Html5libParser
def load_elementtidy():
from .elementtidyparser import ElementTidyParser
return ElementTidyParser
def load_builtin():
from .htmlparser import HTMLParser
return HTMLParser
def get_parser(preference_order=('lxml', 'lxmlsoup', 'html5lib', 'elementtidy', 'builtin')):
"""
Get a parser from a preference order list.
This allows Weboob to run on systems without lxml, which is the default parser.
Return a parser implementing IParser.
"""
if not isinstance(preference_order, (tuple, list)):
preference_order = [preference_order]
for kind in preference_order:
if not 'load_%s' % kind in globals():
continue
try:
return globals()['load_%s' % kind]()
except ImportError:
logging.debug('%s is not installed.' % kind)
raise NoParserFound("No parser found (%s)" % ','.join(preference_order))
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/parsers/elementtidyparser.py 0000664 0000000 0000000 00000004323 11430720725 0031277 0 ustar 00root root 0000000 0000000 # -*- 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.
# XXX Currently, elementtidy segfaults when there are no error, because of
# the behavior of libtidy.
# A patch has been sent to Debian:
# http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=576343
#
# As it is not integrated in Debian yet, and as this problem persists on other
# systems, using elementtidy is for now to avoid.
from elementtidy import TidyHTMLTreeBuilder
try:
from xml.etree import cElementTree as ElementTree
except ImportError:
from xml.etree import ElementTree
from .iparser import IParser
__all__ = ['ElementTidyParser']
class ElementTidyParser(IParser):
def parse(self, data, encoding=None):
TidyHTMLTreeBuilder.ElementTree = ElementTree
HTMLTreeBuilder = TidyHTMLTreeBuilder.TidyHTMLTreeBuilder
parser = HTMLTreeBuilder(encoding)
tree = ElementTree.parse(data, parser)
for elem in tree.getiterator():
if elem.tag.startswith('{'):
elem.tag = elem.tag[elem.tag.find('}')+1:]
return tree
def tostring(self, element):
e = ElementTree.Element('body')
e.text = element.text
e.tail = element.tail
for sub in element.getchildren():
e.append(sub)
s = ''
# XXX OK if it doesn't work with utf-8, the result will be fucking ugly.
for encoding in ('utf-8', 'ISO-8859-1'):
try:
s = ElementTree.tostring(e, encoding)
except UnicodeError:
continue
else:
break
return unicode(s)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/parsers/html5libparser.py 0000664 0000000 0000000 00000003272 11430720725 0030476 0 ustar 00root root 0000000 0000000 # -*- 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.
from html5lib import treebuilders, HTMLParser
try:
from xml.etree import cElementTree as ElementTree
except ImportError:
from xml.etree import ElementTree
from .iparser import IParser
__all__ = ['Html5libParser']
class Html5libParser(HTMLParser, IParser):
"""
Parser using html5lib.
Note that it is not available on every systems.
"""
# Default implementation for each type of API.
defaults = {'etree': ElementTree,
}
def __init__(self, api='etree'):
# if no default implementation is defined for this api, set it to None
# to let getTreeBuilder() using the corresponding implementation.
implementation = self.defaults.get(api, None)
HTMLParser.__init__(self, tree=treebuilders.getTreeBuilder(api, implementation))
def parse(self, data, encoding):
return HTMLParser.parse(self, data, encoding=encoding)
def tostring(self, elem):
# TODO
raise NotImplementedError()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/parsers/htmlparser.py 0000664 0000000 0000000 00000005345 11430720725 0027725 0 ustar 00root root 0000000 0000000 # -*- 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 HTMLParser import HTMLParser as _HTMLParser
import htmlentitydefs
try:
from xml.etree import cElementTree as ElementTree
except ImportError:
from xml.etree import ElementTree
from .iparser import IParser
__all__ = ['HTMLParser']
class HTMLTreeBuilder(_HTMLParser):
def __init__(self, encoding=None):
_HTMLParser.__init__(self)
self._target = ElementTree.TreeBuilder()
def doctype(self, name, pubid, system):
pass
def close(self):
tree = self._target.close()
return tree
def handle_starttag(self, tag, attrs):
self._target.start(tag, dict(attrs))
def handle_startendtag(self, tag, attrs):
self._target.start(tag, dict(attrs))
self._target.end(tag)
def handle_charref(self, name):
self._target.data(unichr(int(name)))
def handle_entityref(self, name):
try:
self._target.data(unichr(htmlentitydefs.name2codepoint[name]))
except KeyError:
self._target.data('&' + name)
def handle_data(self, data):
self._target.data(data)
def handle_endtag(self, tag):
try:
self._target.end(tag)
except:
pass
class HTMLParser(IParser):
def parse(self, data, encoding=None):
parser = HTMLTreeBuilder(encoding)
tree = ElementTree.parse(data, parser)
for elem in tree.getiterator():
if elem.tag.startswith('{'):
elem.tag = elem.tag[elem.tag.find('}')+1:]
return tree
def tostring(self, element):
e = ElementTree.Element('body')
e.text = element.text
e.tail = element.tail
for sub in element.getchildren():
e.append(sub)
s = ''
# XXX OK if it doesn't work with utf-8, the result will be fucking ugly.
for encoding in ('utf-8', 'ISO-8859-1'):
try:
s = ElementTree.tostring(e, encoding)
except UnicodeError:
continue
else:
break
return unicode(s)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/parsers/iparser.py 0000664 0000000 0000000 00000002245 11430720725 0027205 0 ustar 00root root 0000000 0000000 # -*- 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.
class IParser(object):
def parse(self, data, encoding=None):
"""
Parse a HTML document with a specific encoding to get a tree.
@param data [str] HTML document
@param encoding [str] encoding to use
@return an object with the structured document
"""
raise NotImplementedError()
def tostring(self, elem):
"""
Get HTML string from an element.
"""
raise NotImplementedError()
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/parsers/lxmlparser.py 0000664 0000000 0000000 00000005641 11430720725 0027734 0 ustar 00root root 0000000 0000000 # -*- 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 lxml.html
from .iparser import IParser
__all__ = ['LxmlHtmlParser', 'select', 'SelectElementException']
class SelectElementException(Exception):
pass
def select(element, selector, nb=None, method='cssselect'):
"""
Select one or many elements from an element, using lxml cssselect by default.
Raises SelectElementException if not found.
@param element [obj] element on which to apply selector
@param selector [str] CSS or XPath expression
@param method [str] (cssselect|xpath)
@param nb [int] number of elements expected to be found.
Use None for undefined number, and 'many' for 1 to infinite.
@return one or many Element
"""
if method == 'cssselect':
results = element.cssselect(selector)
if nb is None:
return results
elif isinstance(nb, basestring) and nb == 'many':
if results is None or len(results) == 0:
raise SelectElementException('Element not found with selector "%s"' % selector)
elif len(results) == 1:
raise SelectElementException('Only one element found with selector "%s"' % selector)
else:
return results
elif isinstance(nb, int) and nb > 0:
if results is None:
raise SelectElementException('Element not found with selector "%s"' % selector)
elif len(results) < nb:
raise SelectElementException('Not enough elements found (%d expected) with selector "%s"' % (nb, selector))
else:
return results[0] if nb == 1 else results
else:
raise Exception('Unhandled value for kwarg "nb": %s' % nb)
else:
raise NotImplementedError('Only cssselect method is implemented for the moment')
class LxmlHtmlParser(IParser):
"""
Parser using lxml.
Note that it is not available on every systems.
"""
def parse(self, data, encoding=None):
if encoding is None:
parser = None
else:
parser = lxml.html.HTMLParser(encoding=encoding)
return lxml.html.parse(data, parser)
def tostring(self, element):
return lxml.html.tostring(element, encoding=unicode)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/parsers/lxmlsoupparser.py 0000664 0000000 0000000 00000002174 11430720725 0030641 0 ustar 00root root 0000000 0000000 # -*- 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 lxml.html
import lxml.html.soupparser
from .iparser import IParser
__all__ = ['LxmlSoupParser']
class LxmlSoupParser(IParser):
"""
Parser using lxml elementsoup.
Note that it is not available on every systems.
"""
def parse(self, data, encoding=None):
return lxml.html.soupparser.parse(data)
def tostring(self, element):
return lxml.html.tostring(element, encoding=unicode)
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/property.py 0000664 0000000 0000000 00000003251 11430720725 0025743 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# For Python 2.5-, this will enable the simliar property mechanism as in
# Python 2.6+/3.0+. The code is based on
# http://bruynooghe.blogspot.com/2008/04/xsetter-syntax-in-python-25.html
# 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 sys
import __builtin__
class property(property):
def __init__(self, fget, *args, **kwargs):
self.__doc__ = fget.__doc__
super(property, self).__init__(fget, *args, **kwargs)
def setter(self, fset):
cls_ns = sys._getframe(1).f_locals
for k, v in cls_ns.iteritems():
if v == self:
propname = k
break
cls_ns[propname] = property(self.fget, fset, self.fdel, self.__doc__)
return cls_ns[propname]
def deleter(self, fdel):
cls_ns = sys._getframe(1).f_locals
for k, v in cls_ns.iteritems():
if v == self:
propname = k
break
cls_ns[propname] = property(self.fget, self.fset, fdel, self.__doc__)
return cls_ns[propname]
__builtin__.property = property
woob-92ca7d440f02b034d1a8fc27370ef6b5d9daf5d4-weboob/weboob/tools/storage.py 0000664 0000000 0000000 00000003432 11430720725 0025524 0 ustar 00root root 0000000 0000000 # -*- 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 copy import deepcopy
from .config.yamlconfig import YamlConfig
class IStorage:
def load(self, what, name, default={}):
raise NotImplementedError()
def save(self, what, name):
raise NotImplementedError()
def set(self, what, name, *args):
raise NotImplementedError()
def get(self, what, name, *args, **kwargs):
raise NotImplementedError()
class StandardStorage(IStorage):
def __init__(self, path):
self.config = YamlConfig(path)
self.config.load()
def load(self, what, name, default={}):
d = {}
if not what in self.config.values:
self.config.values[what] = {}
else:
d = self.config.values[what].get(name, {})
self.config.values[what][name] = deepcopy(default)
self.config.values[what][name].update(d)
def save(self, what, name):
self.config.save()
def set(self, what, name, *args):
self.config.set(what, name, *args)
def get(self, what, name, *args, **kwargs):
return self.config.get(what, name, *args, **kwargs)