Archive for the ‘Coding’ Category
JavaScript: onMouseMove Google Translation
I wanted to implement a JavaScript onMouseMove Event, that translates the word under the mouse pointer with the Google Translator. I want to share the following solution.
I split this into the following two separate problems:
First: Detect word for an onMouseMove Event
The onMouseMove Event can listen on any Element for mouse movements. To detect the word under the mouse pointers position, I utilize the W3C(DOM-2) specified Range Object.
Most modern browsers support the Range object, but the event.rangeParent Attribute seems to be only supported by Firefox. Currently I’ve no cross-browser solution for this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | /** * Apply offset to range and extract a single character at position. * * range -- the range object effected * originalOffsetStart, originalOffsetEnd -- Start offsetStart and offsetEnd * offset -- the relative offset to apply */ function getRangeCharacter(range, originalOffsetStart, originalOffsetEnd, offset) { var character = ' '; try { range.setStart(range.startContainer, originalOffsetStart + offset); range.setEnd(range.startContainer, originalOffsetEnd + offset); character = range.toString()[0]; range.setStart(range.startContainer, originalOffsetStart - offset); range.setEnd(range.startContainer, originalOffsetEnd - offset); } catch (e) {} return character; } /** * Returns a single word for the given event. */ function getEventWord(evt) { if (!evt.rangeParent || !document.createRange) { return ''; } var range = document.createRange(); range.setStart(evt.rangeParent, evt.rangeOffset); range.setEnd(evt.rangeParent, evt.rangeOffset); // the word ends when this characters appears var stop_character_pattern = /^[\W]$/m; // this "overwrites" some characters from the above pattern var ignore_stop_character_pattern = /^['|\-]$/; // I assume startOffset == endOffset so set 1 character selection ... var originalOffsetStart = range.startOffset; var originalOffsetEnd = range.startOffset + 1; try { // test: range.setStart(range.startContainer, originalOffsetStart); range.setEnd(range.startContainer, originalOffsetEnd); } catch (e) { // out of bounds originalOffsetStart -= 1; originalOffsetEnd -= 1; range.setStart(range.startContainer, originalOffsetStart); range.setEnd(range.startContainer, originalOffsetEnd); } // First Step: Find left end of word: var leftCharacterPos = 0; var iChar = ''; for (var iOffStart = 0; iOffStart <= originalOffsetStart; iOffStart++) { iChar = getRangeCharacter(range, originalOffsetStart, originalOffsetEnd, -1 * iOffStart); leftCharacterPos = iOffStart; if (stop_character_pattern.test(iChar) && !ignore_stop_character_pattern.test(iChar)) { // remove the stop character! leftCharacterPos -= 1; break; } } leftCharacterPos = originalOffsetStart - leftCharacterPos; // Last Step: Find right end of word: var rightCharacterPos = 0; for (iOffStart = 0; true; iOffStart++) { iChar = getRangeCharacter(range, originalOffsetStart, originalOffsetEnd, iOffStart); if (stop_character_pattern.test(iChar) && !ignore_stop_character_pattern.test(iChar)) { rightCharacterPos = iOffStart - 1; break; } } rightCharacterPos = originalOffsetEnd + rightCharacterPos; try { range.setStart(range.startContainer, leftCharacterPos); range.setEnd(range.startContainer, rightCharacterPos); } catch (e1) { range.detach(); return ''; } var retWord = range.toString(); range.detach(); return retWord; } |
Second: Translate Text with Google Translator
This is very easy because Google provides an very easy to use Ajax API for this (attend the possible google user tracking!):
1 | <script type="text/javascript" src="http://www.google.com/jsapi"></script> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | // read onMoveTranslate() var last_word = null; var timeout = null; var word_cache = []; /** * Translate text For onmousemove events. */ function onMoveTranslate(event) { // get element the translation should appear in: var translation = document.getElementById('translation'); var word = getEventWord(event); if (!word || word == '') { return; } if (typeof(word_cache[word]) !== 'undefined') { translation.innerHTML = word_cache[word]; //+" (cached)"; return; } if (word != last_word) { if (timeout) { window.clearTimeout(timeout); } translation.innerHTML = 'Translating ... ' + word; timeout = window.setTimeout(function(){ google.language.translate(word, 'en', 'de', function(result) { if (!result.error) { translation.innerHTML = result.translation; word_cache[word] = result.translation; } }); }, 800); } last_word = word; } google.load("language", "1"); |
At last I assign the onMouseMove Event to the onMoveTranslate() function:
1 | <div id="content" onmousemove="onMoveTranslate(event);"> |
Here is an full Example for this.
Tagged: Coding, Event, JavaScript, onMouseMove
mtget: ZDF Mediathek Download/Stream
Nachdem die ZDF Mediathek nun ihr lang erwartetes relaunch ‘feiern’ kann, habe ich mich daran gemacht mtget zu überarbeiten. Die neue, für Linux Anwender besonders interessante, “HTML Version”, macht, da ohne Flash, die ZDF Mediathek nun erstmals halbwegs benutzbar. =)
Die asx-Links fürs mms streaming werden jetzt auch nicht mehr versteckt, so im großen und ganzen also ein ziemlicher Fortschritt. Für die Nerds unter euch die dennoch gern eine Shell-Lösung hätten, pflege ich mtget trotzdem weiter.
Bei der Gelegenheit habe ich mtget in Python komplett neu geschrieben. Version 0.5 kommt ohne zusätzliche Libraries aus, es müssen lediglich Python(bei mir läuft 2.6) sowie die in CMD_STREAM und CMD_DOWNLOAD eingetragenen Programme(per default: mplayer) installiert sein. In der neuen Version werden erstmals Kanäle erkannt und können im interaktiven Modus(-i) ausgewählt werden, andernfalls wird ihnen automatisch gefolgt und die Einträge abgespielt. Anders als die vorigen Versionen lädt 0.5 nur die mms-Streams. mtget.py:

mtget.py: Interaktive Auswahl der Videos und Kanäle
ZDF Mediathek Download/Streaming Skript
v0.5 <apoc@sixserv.org> http://apoc.sixserv.org/
Stand: 2009-12-20
Syntax: ./mtget.py <URL/ID> [OPTIONS]
<URL/ID> mediathek video/kanal url oder id
-1 qualitaet DSL 1000
-2 qualitaet DSL 2000 (Standard)
-m, --mode <d/s> download(d) oder streaming(s)
-d, --dir <directory> das verzeichnis wohin gespeichert werden soll(.)
-t, --title benutzt nicht den stream dateinamen sondern titel
-s, --search <topic> suche in der mediathek
-l, --maxr <max> wieviele ergebnisse verarbeiten(suche/kategorie)
-c, --ignore-channel ignoriert kanaele
--no-colors deaktiviert die kursiv und fettschrift
-i interaktiv, auswahl der zu spielenden videos
-v erweiterte ausgabe, zu debugging zwecken
-h, --help zeigt diese hilfe
Mir sind übrigens noch zwei weitere Projekte bekannt die ebenfalls Mediathek streaming bieten: mtscrape und zdfmediathk.
Für Vorschläge, Bugreports und alles andere hinterlasst mir ein Kommentar oder schreibt eine eMail an apoc@sixserv.org!
Tagged: mediathek, Python, web scraping
Content Extraction Algorithmen: Density/CCB
Im Artikel, “Web scraping mit Ruby/Mechanize” zeigte ich wie man Webseiten von einem Ruby Skript laden lässt und definierte Inhalte daraus extrahieren kann. Diese Verfahren, z.B.: Regular expression, XPath oder CSS Selektoren, funktionieren nur für Seiten deren Struktur während der Implementierung bekannt sind. Diesmal allerdings geht es um Algorithmen die den Hauptinhalt(z.B. den Text einer Nachricht) von jeder beliebigen Seite erkennen und extrahieren können. Diese Möglichkeit ist vor allem für Suchmaschinen/Datamining oder für Anwendungen die die Lesbarkeit erhöhen wollen, z.B. für kleine Bildschirme(Handhelds) oder Screen Reader, interessant.
Auch wenn es ein recht neues Forschungsfeld der Informatik ist, gibt es schon einige Veröffentlichungen zu dem Thema. Für den Einstieg am Interessantesten ist wohl die Dissertation von Thomas Gottron, “Content Extraction: Identifiying the Main Content in HTML Documents“, die einen Überblick über die Algorithmen gibt, eine Möglichkeit der Evaluation beschreibt und einige neue Algorithmen einführt. Das erfolgversprechendste dieser Verfahren, der CCB(Content Code Blurring) Algorithmus ist auch Thema dieses Artikels.
Einen weiteren interessanten Ansatz liefern Arias Moreno, Deschacht und Moens in Ihrem Paper “Language Independent Content Extraction from Web Pages” deren Verfahren(Density) hier auch besprochen wird.
Natürlich kann man von keinem der Algorithmen Perfektion erwarten, dafür sind die Webseiten zu unterschiedlich strukturiert. Auch will ich nicht vergessen zu erwähnen das diese Algorithmen nur auf Seiten mit klar erkennbarem Hauptinhalt(Main Content) funktionieren, Weblogs z.B. verwirren so gut wie alle Verfahren die darauf Basieren die höchste “Text Konzentration” auf einer Seite zu finden, da die Kommentare unter dem Artikel nicht selten wesentlich umfangreicher als der Hauptinhalt sind(z.B. Slashdot).
Content Code Blurring (Python Skript / Modul)
Der CCB Algorithmus von Thomas Gottron stellt ein Verfahren dar, um die Teile einer HTML-Seite zu identifizieren die aus viel Text und nur wenigen Tags besteht. Dazu wird die Seite in eine Sequenz aus Tokens oder Zeichen aufgeteilt: Die TCCB Variante teilt die Sequenz in Tokens aus Tags oder Wörtern ein, sonst werden Zeichen unterschieden die sich innerhalb(Code) oder außerhalb(Inhalt/Content) eines Tags befinden. Der so entstandene Content Code Vektor(CCV) speichert für jeden Code ein 0 und für jeden Inhalt eine 1.
Jetzt wird der CCV verwischt(Blurring) dazu wird ein Gaussian Blur Filter verwendet, der im Prinzip einer Gaussian Distribution/Normal Distribution entspricht. Anders gesagt werden die Inhalts(1)-Elemente des CCV auf ihre Nachbar-Elemente verstreut oder verwischt. Dieser Vorgang wird n-mal wiederholt oder bis sich der Prozess normalisiert hat(wobei ich es leider nicht geschafft habe das zu implementieren(kA. woran das liegt, GERNE FEEDBACK!)). Meine Beispielimplementierung kann ein Diagramm des CCV “plotten”:

Dieser Plot zeigt, dass man schon auf der richtigen Spur ist. Klar erkennbare Spitze wo der Hauptinhalt liegt.
Am Ende werden die Teile des CCV welche über einen bestimmten Schwellwert liegen mit ziemlicher Sicherheit zum Hauptinhalt der Seite gehören. (Im Diagramm sehr leicht zu erkennen.)
Der CCB Algorithmus funktioniert sehr gut und gilt als einer der besten Algorithmen für Content Extraction überhaupt. Ein Nachteil besteht darin das Teile der Seite die ebenfalls viel Text enthalten mitunter zum Hauptinhalt gezählt werden, auch wenn sich diese Bereiche weit entfernt vom eigentlichen Inhalt befinden.
Density (Ruby Beispiel)
Der Density Algorithmus von Arias Moreno, Deschacht und Moens folgt einem ähnlichen Ansatz, die HTML-Seite wird durch einen (möglichst robusten) XML-Parser geparsed. Dann wird jeder (XML-)Node des Dokuments der Text enthält an den letzten String eines Arrays angehängt. Handelt es sich bei dem Node um einen HTML-Tag der die Struktur des Dokuments verändert(z.B. p, table, br, div, h1-6, li usw.) wird das Array um einen leeren String erweitert.
Wurde dieses Array fertig erstellt, wird ein Algorithmus angewandt, der den Bereich mit der größten Textdichte im Array zu bestimmen versucht(siehe Paper). Am Ende wird das Array nur noch sortiert und das Ergebnis steht als Plaintext fest.
Density arbeitet in der Praxis sehr gut, jedoch wird das Ergebnis sehr stark von den beiden Parametern beeinflusst, die den Algorithmus zum erkennen des Bereichs einstellen(besonders wieviele Absätze am Stück erkannt werden).
Update: Ich habe eine Implementierung für Density in Ruby eingefügt, er versucht den Hauptinhalt dieses Artikels zu extrahieren
Kleine Ruby Logger Klasse
Normalerweise sind die Artikel hier ja ziemlich umfangreich, was ja auch gut ist schließlich geht das Sixserv Blog ja schon sehr ins technische Detail. Doch diesmal wird es eher kurz, ich möchte eine kleine Logger Klasse vorstellen die ich in Ruby geschrieben habe und seither häufiger benutze.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | class Logger def self.init @logfile = LOG_FILE @buffer = [] # logging lines for buffering end def self.<< msg msg = format msg # append message to buffer @buffer << msg # flush the buffer if possible self.flush end def self.flush return if @buffer.empty? # try to lock the file for writing log = File.new(@logfile, 'a+') if log.flock(File::LOCK_EX | File::LOCK_NB) == false # Logfile is locked puts "Logfile is locked!" log.close return false end # ==> not locked, write buffer to logfile while @buffer.length > 0 log.puts @buffer.shift end log.flock(File::LOCK_UN) log.close end private def self.format msg time = Time.now.strftime '%H:%M:%S - %d.%m.%Y' "[#{time}] #{msg}" end end |
Nichts großes also, es gibt z.B. keine Log-Level usw. Außerdem kann man sich, wenn man etwas mehr Features braucht, auch eine Logging Bibliothek aus dem Gems bedienen. Die Anwendung ist ganz einfach:
1 2 3 4 5 6 7 8 | require '/pfad/zu/logger.rb' Logger.init # das kann man dann überall im Code verteilen: Logger << "Irgendeine Nachricht!" # hat man eine große Anwendung mit vielen Prozessen/Threads # kann man auch an strategischen Stellen ein Logger.flush # einbauen :) |
Hier noch eine kleine Erweiterung die einen Log-Server implementiert. Er öffnet einen TCP Port, jeder der sich verbindet(z.B. per Telnet/Netcat) kann daraufhin die Log-Nachrichten mitlesen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | class Logger def self.init ... @port = LOG_PORT @query = [] @query_mutex = nil @running = false @server = nil end def self.start_server @query_mutex = Mutex.new puts "Start logging console @ #{LOG_HOST}:#{LOG_PORT}" @server = TCPServer.new(LOG_HOST, LOG_PORT) clients = [] @running = true Thread.start do client = [] while @running do begin client = @server.accept_nonblock clients << client if client != nil rescue end begin message = nil @query_mutex.synchronize { message = @query.pop } if message != nil clients.each do |client| begin client.puts message rescue clients.delete client end end end end while not @query.empty? sleep LOG_SERVER_INTERVAL end clients.each { |client| client.close } @server.close @server = nil end end def self.destroy @running = false @fs.close # wait until server closed true while @server != nil end def self.<< msg if @query_mutex != nil @query_mutex.synchronize do @query << format(msg) end end puts ">>> #{msg}" end def self.flush # just wait until everything is sent/written if @query.length > 0 true while @query.length != 0 end end ... end |
So das wars schon, ich hoffe das ich das mit dem Mutex richtig gemacht habe und das hier überhaupt notwendig war, ich habe auf dem Gebiet nicht viel Erfahrung und würde mich über einen entsprechenden Kommentar freuen.
// I’m not sure I used the Mutex in this example correctly, I would appreciate a comment about this.
Web scraping mit Ruby/Mechanize
Praktisch jede Interaktion mit einer Website oder Webapplikation kann gescriptet, d.h. automatisiert werden. Das Abgrasen von Webseiten nach bestimmten Informationen wird auch als Scraping bezeichnet(für die nicht menschlichen Besucher dieser Seite sei das erwähnt *g*) Scripte können einem eine ganze Menge Arbeit abnehmen und sogar Dinge tun, die manuell unmöglich wären. Ich beschäftige mich mit dem Thema schon seit einer ganzen Weile und möchte hier nun die von mir favorisierte Methode dafür vorstellen, die Bibliothek Mechanize für die Scriptsprache Ruby.
Mechanize hat seinen Ursprung in Perl, mittlerweile gibt es jedoch auch Implementierungen der API für Python und eben Ruby. Für PHP gibt es mit Snoopy ein ähnliches Projekt, wenn es auch bei weitem nicht so fortgeschritten ist. Mechanize bietet die Möglichkeit mit einfachen Methoden eine art Webbrowser zu simulieren. Alle Beispiele wurden mit Mechanize Version 0.9.2 und Ruby 1.8.7 getestet.
Installation / Initialisierung
Mechanize kann mit Gems(ähnlich CPAN oder PEAR) installiert werden(# gem install mechanize --remote), einige Distributionen bieten aber auch eigene Pakete an. Ein Ruby-Script kann daraufhin Mechanize inkludieren und ein Objekt erstellen:
require 'rubygems' # ist unter Umständen notwendig require 'mechanize' agent = WWW::Mechanize.new
Jetzt ist Mechanize einsatzbereit, die folgenden Beispiele bauen darauf auf. Außerdem können mit dem agent nun noch grundlegende Einstellungen vorgenommen werden:
agent.set_proxy('localhost', '8000') agent.user_agent = 'Individueller User-Agent' agent.user_agent_alias = 'Linux Mozilla'
Die Einstellung ‘user_agent_alias‘ wählt einen User-Agent String aus dem folgenden Satz von Beispielen aus: Windows IE 6, Windows IE 7, Windows Mozilla, Mac Safari, Mac FireFox, Mac Mozilla, Linux Mozilla, Linux Konqueror, iPhone und Mechanize. Die Timing Einstellungen können ebenfalls sehr wichtig sein:
agent.open_timeout = 3 # setzt timeouts agent.read_timeout = 4 agent.keep_alive = false # default ist true
Hier folgen nun einige Beispiele, vielleicht werde ich mit der Zeit auch noch ein paar ergänzen, falls jemand Vorschläge hat, immer her damit. Ich habe auf http://apoc.sixserv.org/requestinfo ein kleines Skript am laufen das nützliche Informationen zum HTTP-Request liefert, das kann zum Experimentieren mit Mechanize sehr nützlich sein. Einige Beispiele findet man auch in den GUIDE und EXAMPLES Dateien des Mechanize Pakets. Read the rest of this entry »
Tagged: Coding, mechanize, Networking, Ruby
implementing bittorrent (teil 2)
Was macht ein BitTorrent-Tracker? In diesem Teil gehe ich auf die Funktion eines BitTorrent-Trackers ein. Zuerst sollte man sich den Grundsätzlichen Ablauf eines Torrent Downloads anschauen.
1. Download der .torrent-Datei
Der User sucht sich aus seiner Lieblings-Torrent-Quelle (zb TPB, Demonoid, dnbtracker.org ) einen den gewünschten Torrent heraus und lädt ihn auf Seine Festplatte oder öffnet ihn gleich mit seinem BitTorrent-Client.
2. Der Client parst die .torrent-Datei
Für die Kommunikation mit dem Tracker sind nur ein Paar Informationen aus der Metafile interessant (Metafile Infos im ersten Teil)
- der info hash
- die announce urls
3. Der Client kontaktiert den Tracker und sendet ihm den SHA Hash
Die Tracker-Kommunikation läuft über HTTP.
Der Client Schickt einen GET-Request an den Tracker.
Dieser Request besteht aus:
- announce url
- info hash – der SHA1 Hash des Info Dictionaries
- peer id
- port
- uploaded
- downloaded
- left
- compact
- event
Optional aber auch interessant ist:
- key (damit der Tracker einen Client auch nach einem reconnect mit neuer IP wiedererkennt)
Ein Request an einen Tracker könnte dann wie folgt aussehen ( der Tracker laeuft auf example.com):
- http://example.com/announceUrl+”?info_hash=”+urlencode(info)+”&peer_id=-ST-0001%29%7B%D6%40%F4%21%7B%5C%DA%05%DE&port=6881&uploaded=0&downloaded=0&left=0&compact=1&event=started”
Der Tracker erfährt damit von uns folgende Informationen:
- den Hash von der Info-Map (info_hash)
- nsere Peer-ID (peer_id) [zur ID bald mehr]
- den Port auf dem unser Client lauscht (port)
- wir haben noch 0 Pieces hochgeladen (uploaded=0)
- wir haben 0 Pieces heruntergeladen (downloaded=0)
- wir möchten eine Peer-Liste im Kompakten Format (compact=1), das ist auch üblich so. Das alte Format wird praktisch kaum noch genutzt.
- mit dem Event (event=started) teilen wir dem Tracker mit das wir gerade erst mit dem Download beginnen.
4. Tracker Antwort
Der Tracker antwortet mit einer Liste von Peers. Aus dieser Liste kann der Client entnehmen welche Peers die File besitzen. Ob Sie die ganze File besitzen oder nur Fragmente (aus dieser Information wird die obligatorische anzeige der Seeder [complete] bzw Peers [incomplete] realisiert).
Zu den Einzelnen Peers sind die Informationen die dieses bei ihrem Request an den Tracker übertragen haben enthalten, also:
- peer_id
- port
- ip
Zusätzlich teilt einem der Tracker den Intervall mit in dem man diese Informationen auffrischen sollte, bzw einen erneuten Request an den Tracker schicken sollte. Clients sollten sich daran halten um ein hämmern bzw Sinnlosen Overhead zu unterbinden.
Das war es für diesen Teil im großen und ganzen. Ein kleiner überblick auf die Tracker Kommunikation.
Ganz Informativ zu diesem Thema ist auch der Vortrag “Tracker fahrn” (@24C3 – download via http://chaosradio.ccc.de/24c3_m4v_2355.html) von Erdgeist, Denis und Cristian Yxen.
Im nächsten Teil wird es um die Kommunikation unter den Peers gehen.
Tagged: bittorrent, Coding, p2p
vBrowseFile 0.0.08-rc2 first file release
Habe gestern das erste testing Release von vBrowseFile veröffentlicht.
Installation(auszug aus der README-Datei):
- install HTTP_Download with pear
For Debian you could do this with:
# aptitude install php-pear
# pear install --alldeps HTTP_Download
- Make sure .htaccess Files are used
Change VHost setting: "AllowOverride All"
- Download vBroweFile Files to an webroot/vhost-root:
Download an packaged vbf version.
- or -
Checkout the latest SVN version: (will create vbrowsefile/ directory)
$ svn co svn://mount.at/geekosphere/vbrowsefile/trunk vbrowsefile/
- Make sure the following Directories are writeable by Webserver:
/flatfiles
/flatfiles/cache
/flatfiles/cache/tracks
/flatfiles/userquery
/smarty/templates_c
- Go with your browser to the setup.php script:
e.g. http://example.com/vbrowsefile/setup.php
- Now you could login, change configuration, add some virtual directories
and create some normal users
Tagged: Coding, PHP, vbrowsefile
PoC: Real Bandwidth Limiting with PHP
vBrowseFile uses a slightly modificated Version of HTTP_Download. HTTP_Download is a Pear-Module to send local Files to Browser:
Provides an interface to easily send hidden files or any arbitrary data to HTTP clients. HTTP_Download can gain its data from variables, files or stream resources.
It features:
- Basic caching capabilities
- Basic throttling mechanism
- On-the-fly gzip-compression
- Ranges (partial downloads and resuming)
- Delivery of on-the-fly generated archives through Archive_Tar and Archive_Zip
- Sending of PgSQL LOBs without the need to read all data in prior to sending
I added a ThrottleCallback functionality to HTTP_Download, a function that is executed after the BufferSize is transferred and the ThrottleDelay Time is waited. Modifications in HTTP/Download.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | var $throttleCallback = null; [...] function setThrottleCallback($callback) { $this->throttleCallback = $callback; } [...] function callThrottleCallback() { // execute callback if($this->throttleCallback != null) { $callback = $this->throttleCallback; $callback($this); } } [...] function sendChunk($chunk, $cType = null, $bound = null) { [...] // right after sleep is ex $this->callThrottleCallback(); [...] } |
This Callback could be used to track the download progress and to create a real bandwidth limitation. HTTP_Download supports a basic throttling mechanism to limit the bandwidth, but this only works for one connection, so e.g. you could limit 2 downloads to each 100 KiB/s but not all 2 downloads to each 50 KiB/s to limit the overall bandwidth to 100 KiB/s, instead it would be 200 KiB/s.
For this real bandwidth limiting you could use Apache modules like cband(seems not to work with php) or mod-bw, or you could use tc. Siyb from Geekosphere wrote an article about mod-bw: Limit Bandwidth per vHost in Apache2 (on Debian/Lenny)
Another possible way to limit the bandwidth is to implement it with HTTP_Download. The Theory is, to track all running downloads and change the BufferSize to := download limit * delay / active connections every
More detailed:
- We generate a random connection id(i call it rcid) for the tracking file.
- ThrottleDelay is set to 1 seconds.
- BufferSize is set to 100 KiB * 1 Seconds * 1024 to setup a maximum speed of 100KiB/s.
- The Callback would now be executed every 1 Seconds:
- create or overwrite our own rcid file with time() inside.
- search for other rcid files and check there time-stamp. tracking files older then 5 seconds would be deleted.
- count all rcid tracking files. e.g. 2 means there 2 open download connections.
- change the BufferSize. e.g. for 2 open connections: (100 KiB * 1 Seconds * 1024) / 2
This would limit the overall bandwidth of all active connections to a maximum of 100 KiB/s with a delay of ~ <5 Seconds. Without any Apache Modules or other standalone traffic sharping tools. For small installations this should work with no problem but for bigger installations a more efficient traffic shaping tool would be more effective.
Example Implementation: Read the rest of this entry »
Tagged: Coding, Networking, PHP, vbrowsefile
Soup.io rbot Plugin/ ruby API
Mit Twitter kann ich sehr leicht über meinen rbot publizieren, damit ich in der selben Frequenz auch soup.io verwende, was für spezielleren Content besser geeignet ist, muss es schon ebenfalls über den rbot funktionieren. Nach langem suchen stellte ich überrascht fest das das große Web 2.0 Projekt Soup.io keine API zum leichten publizieren(außerhalb des Browsers) besitzt. Da muss also erstmal eine art API gestrickt werden. Mit Hilfe von Mechanize baute ich die Browseranfragen nach und fasste es in einer kleinen Ruby Klasse zusammen. Es wird nicht jeder Content-Typ unterstützt aber für meine Zwecke reicht dies völlig aus. Dann noch schnell eine rbot-Plugin Klasse zum bedienen der eigenen “API” und fertig war die erste Version meines soupio-Plugins.
Hier eine Liste der möglichen Befehle(Argumente in eckigen Klammern sind Optional):
1 2 3 4 5 6 7 8 9 10 11 | soup identify <username> <password> => Jeder Benutzer im Channel kann dem Bot seine Soup.io-Zugangsdaten im Query mitteilen. soup login => Neuer Login falls die gespeicherte SessionId verloren oder ungültig wird. (Normalerweise nicht notwendig.) soup link <url> [<title>] soup image <url> [<description>] soup text <text> soup quote <source>: <quote> soup video <youtube-url> [<description>] |
Die SoupIoClass-Klasse kann übrigens auch außerhalb des Plugins, in jeder Ruby-Anwendung verwendet werden. Hier eine kleine Referenz, diesmal die optionalen Argumente in spitzen Klammern:
1 2 3 4 5 6 | soup = SoupIoClass.new('[Username]', '[Password]'<, '[Domain]', '[Session-ID]'>) soup.new_link '[URL]'<, '[Title]', '[Description]'> soup.new_image '[URL]'<, '[Description]'> soup.new_text '[Text]'<, '[Title]'> soup.new_quote '[Quote]'<, '[Source]'> soup.new_video '[Youtube-URL]'<, '[Description]'> |
Die Domain und SessionId kann mit soup.sessid und soup.domain abgefragt werden. Die SessionId ist praktisch unbegrenzt lange haltbar, weshalb diese beiden Daten zwischengespeichert werden können um bei häufiger Nutzung der Klasse sich nicht ständig neu Anmelden zu müssen.
Die Version 0.1 ist bereits veröffentlicht, ich muss mich noch um die Validierung und Fehlerabfragen kümmern aber sonst sollte das Plugin schon funktionieren. Fehler bitte bei mir Melden.
Update: Version 0.2 veröffentlicht. (nur kleine Änderungen)
Update: Version 0.3 veröffentlicht. (Bugfix für eigene Domains)
Update: Version 0.4 veröffentlicht. (Änderungen an soup.io)
Tagged: bot, Coding, irc, plugin, RBot, Ruby, soup.io, web2.0
vBrowseFile: development
Ich bin gerade dabei einen kleinen “Filebrowser” zu entwickeln. Dieser zeigt nicht zwangsläufig das reale Dateisystem an, sondern ist in der Lage mehrere real existierende Ordner zu einem virtuellen Ordner zusammen zu legen. Er ist nur zum einfachen herunterladen von Dateien gedacht, eine Upload-Funktion ist nicht geplant, eben sowenig wie das Umbenennen oder Löschen von Dateien. Zum Einsatz kommt PHP5, Smarty und das Pear-Paket HTML_Downloader.
siyb von Geekosphere(danke nochmal!) hat mir ein Redmine und Subversion eingerichtet.
Alles weitere wird es dann dort geben.
For our english speaking visitors:
Recently i began to develop a small Filebrowser. It does not necessarily show the real Filesystem but it can combine several directories to one virtual. The purpose is just to realise an easy way of downloading files, other features used in a common filemanager like the renaming or deleting of files are not planned. I use PHP5, Smarty and the Pear-Package HTTP_Download.
Thanks to siyb, now i can provide more informations at geekosphere wootcenter(redmine).
Tagged: Coding, PHP, vbrowsefile





