Qooli Timelapse Calculator

Abgekürzt QTLC. Eine Anwendung für Google’s Android-Plattform mit welcher man bequem auf seinem Handy Timelapse-Berechnungen durchführen kann.

Qooli Timelapse Calculator LogoMan kann zum Beispiel ausrechnen, wie lange man bei eingem gewissen Shutter-Intervall und FPS (im resultierenden) Aufnahmen machen muss. Man kann aber auch umgekehrt ausrechnen wieviel Filmdauer man auf Grund einer Aufnahmezeit, dem Intervall und den FPS erreicht hat.

Die Anwendung wird ständig weiter ausgebaut und mit weiteren Features versehen, so ist z.B. eine Timerlapse-Stoppuhr mit Erinnerungsfunktion geplant oder der zu erwartende Speicherplatz verbrauch für die Aufnahmen während der Aufnahme-Session.

Die Anwendung kann im Play Store heruntergeladen werden:

Get it on Google Play

Direkt den QR-Code scannen um die App-Store-Seite der Anwendung auf dem Handy zu öffnen… wo man sie dann kaufen kann :-)

QR-Code von QTLC

QR-Code von QTLC: mit einem QR-Code-Scanner einlesen um die App-Store-Seite zu öffnen

Timelapse: Wolken thumbnail

Timelapse Videos erstellen

Vor einigen Monaten habe ich mir unter anderem für das Experimentieren mit Timelapse-Aufnahmen den “programmierbaren” Fernauslöser TC-80N3von Canon gekauft.

Leider habe ich mir lange nicht die Zeit genommen um Timelapse-Aufnahmen zu machen, jedoch hatte ich den Fernauslöser bereits einige Male für Langzeitaufnahmen (Bulb-Mode mit Einrastfunktion auf dem Fernauslöser) oder möglichst verwacklungsfreie Aufnahmen (kein Schütteln der Kamera durch das Drücken des Auslösers) benutzt.

Nun habe ich vor einigen Tagen auch mal einige Timelapse-Videos damit erstellt, respektive die dafür benötigten Fotos gemacht.

Tutorials im Internet bezüglich wie man solche Timelapse-Aufnahmen macht gibt es unzählige. Hier wäre ein ganz Gutes: http://www.enriquepacheco.com/10-tips-for-shooting-time-lapse

Im Prinzip gibt es Folgendes zu beachten:

  • Eine Kamera verwenden, an welcher man mit einem Auslöser in einem Intervall Bilder aufnehmen kann (oder die Option schon eingebaut ist)
  • Kamera/Speicherkarte muss schnell genug sein um die Bilder abspeichern zu können, ohne dass der Zwischenspeicher überläuft und die nächste Aufnahme verzögert würde
  • Alle Kameraeinstellungen möglichst auf “manuell” (kein automatischer Weissabgleich, “M”-Modus mit fixer Blende und Shutter (oder Blendenpriorität, falls man sehr lange Aufnahmen macht mit grossem Kontrastumfang, wie z.B. Sonnenuntergänge), Fokus einstellen und dann auf “manuell”, etc.
  • Möglichst in RAW die Aufnahmen machen (höherer Farbumfang, mehr Nachbearbeitungsmöglichkeiten), JPEG ist aber auch möglich (spart zumindest viel Platz)
  • Stativ benutzen (oder natürlich ein Dolly für mehr Effekte) und eventuell befestigen (Sandsäcke bei starkem Wind)
  • Bildausschnitt wählen (keine Störenden Elemente im Bild/Vordergrund, keine zu starken Bewegungen, keine toten Stellen im Bild): es sollte immer etwas spannendes passieren und Bewegung im Bild sein
  • Bilder besser etwas zu dunkel aufnehmen als überbelichtet (da sonst die Bildinformationen in diesen Bereichen für immer verloren sind)
  • Grosse und schnelle Speicherkarte, da es eine Unmenge Bilder geben wird
  • Akku voll aufgeladen und idealerweise Ersatzakkus dabei
  • Probebilder machen um zuerest die Einstellungen zu überprüfen
  • Rückschaubild für Kamera-LCD deaktivieren um Akku zu sparen
  • Aufnahmezeitdauer berechnen (in der Regel möchte man 24, 25 oder 30 Bilder in der Sekunde im resultierenden Film. Wenn man einen Aufnahme-Intervall von 2 Sekunden hat und man am Ende einen 60 Sekündigen Film möchte  (mit 24 Bildern pro Sekunde) ergibt das folgende Anzahl benötigter Bilder (x):
    x = 60 * 24
    x = 1440
    1440 mal 2 Sekunden (Intverall) ergibt 48 Minuten (+ noch etwas Zeit dazu, da der Shutter ja auch noch etwas Zeit braucht bei jeder Aufnahme und man am Anfang und am Ende des Filmes immer etwas Puffer haben sollte)
  • Etwas mitnehmen um sich die Zeit zu vertreiben, während die Kamera die Aufnahmen macht

Der TC-80N3 hat einen Intervallometer (zweiter Modus: “INT.”), bei welchen man mit dem Wählrad auf der rechten Seite einfach den gewünschten Sekundenwert einstellen kann. Für Timelapse-Aufnahmen benötigt man die anderen Modi nicht unbedingt.

Hier einige meiner ersten Versuche… bei denen ich meine eigenen Tipps leider noch grösstenteils selber ignorierte… aber seht selbst:

Wolken über einem Feld, im Hintergrund die Alpen

Für meinen Geschmack noch zu langweilig/zu wenig speziell.

Wolkenformation am Abendhimmel bei Sonnenuntergang

Am Anfang überbelichtet – auch hier passiert eigentlich zu wenig.

Wolken


Gefällt mir schon gut. Hatte aber noch Sensorflecken auf dem Sensor, welche mir erst im Nachhinein aufgefallen sind :-( – ausserdem sind die Bewegungen stellenweise nicht flüssig (evtl. bräuchte es noch einen kürzeren Intervall und/oder eine etwas längere Verschlusszeit).

Zusammengefügt wurden alle diese Videos unter OSX mit dem Tool “Timelapse Assembler“. Das Programm hat sehr wenige Funktionen, aber erfüllt seinen Job perfekt. Unter Windows könnte man die Bilder mit Virtualdub zu einem Video konvertieren.

Wanderung Aletschgletscher

Meine Verlobte und ich waren dieses Wochenende mit unserem Trauzeugen Florian und seiner Freundin Kathi im Wallis und haben eine Wanderung zum Aletschgletscher unternommen.

Via Gondelbahn ging’s von Mörel rauf in die Riederalp und von dort weiter zur Gondel bis auf die Moosfluh. Dort begann dann die Wanderung, welche am Rande des Aletschgletschers entlang führte bis zur Gletscherstube Märjelensee. Da schon Nebel aufzog und es schon ziemlich spät am Nachmittag war, gingen wir durch den Tunnel (durchquert das Eggishorn) in Richtung Fiescheralp und von dort mit der grossen Gondel wieder zurück.

Hier einige Impressionen:

Und hier noch der aufgezeichnete GPS-Track:

Insgesamt eine nicht gerade anspruchsvolle Wanderung über ca. 14 Kilometer (in 4:20 Stunden), welche aber trotzdem einigermassen Trittsicherheit voraussetzt. Das viele Absteigen mit dem ganzen Geröll ging bei mir gegen Ende etwas in die Knie.. aber ich bin ja auch nicht mehr der Jüngste ;-)

P.S.: einige Fotos (Ponys/Bär) sind von einer kurzen Wanderung im Fieschertal am nächsten Tag

Ferien im Burgund

Vor einer Woche war ich für einige Tage mit meiner Freundin im Burgund in Frankreich in den Ferien.

Leider hat das Wetter nicht mitgespielt und es hat praktisch die ganze Zeit geregnet, was leider dafür gesorgt hat, dass wir einige geplante Wanderungen nicht machen konnten. Auch war das Besichtigen der Orte bei Dauerregen nicht wirklich amüsant.
Trotzdem konnten wir einige Orte Besichtigen und einen Einblick in das (sonst vermutlich schönere :-) ) Burgund erhalten.

Wir haben hauptsächliche Folgende Orte besichtigt, wobei wir dabei jeweils nur eine Nacht in jedem Hotel verbrachten, was natürlich etwas stressig war, aber immerhin kommt man so etwas in der Gegend herum und kann mehr Orte anschauen:

Besonders empfehlenswert fand ich Dijon (schöne Stadt, gute Einkaufsmöglichkeiten) und auch Auxerre (sehr malerisch, nette Architektur). Die Grotte bei D’Arcy ist nett, aber mit einer Dauer von einer Stunde ist die Führung fast schon etwas zu lange, zumal es jetzt nicht gerade die spektakulärste Grotte ist.

Hier noch einige Impressionen aus unseren Burgund-Ferien:

MySQL- und Website-Backup periodisch erstellen

Vor einigen Tagen konnte ich einen Punkt von meiner Aufgabenliste streichen, welchen ich schon längere Zeit offen hatte und eigentlich schon lange einmal hätte umsetzen wollen und sollen: das Einrichten eines Backups für meine Webseite.

Bisher machte ich nur sporadisch entweder manuelle Backups der Daten der Webverzeichnisse und der dazugehörigen MySQL-Datenbanken oder ich habe bei meinem VPS-Provider ein Backup für die ganze Maschine durchgeführt – beides nicht gerade sehr sexy, das muss besser und eleganter gelöst werden, dachte ich mir.

Als erstes schrieb ich ein Shell-Skript, welches mir alle Datenbanken (ausser den System-Datenbanken) in Archiv-Dateien exportiert (alle Pfade und Benutzerangaben wurden durch Beispieldaten ersetzt):

#!/bin/bash

BACKUP_DATE="$(date +%Y-%m-%d)"
DB_BACKUP_ROOT="/zielpfad/zum/backup/mysql"
DB_BACKUP="$DB_BACKUP_ROOT$BACKUP_DATE"
DB_USER="Datenbank-Benutzer"
DB_PASSWD="Datenbank-Kennwort"
HN=`hostname | awk -F. '{print $1}'`
# Create the backup directory
mkdir -p $DB_BACKUP

# Remove backups older than 30 days
find $DB_BACKUP_ROOT -maxdepth 1 -type d -mtime +30 -exec rm -rf {} \;

# Backup each database on the system using username and password
for db in $(mysql --user=$DB_USER --password=$DB_PASSWD -e 'show databases' -s --skip-column-names|grep -vi information_schema);
do
  BACKUP_FILE_PATH="$DB_BACKUP/mysqldump-$HN-$db-$BACKUP_DATE.gz"
  mysqldump --user=$DB_USER --password=$DB_PASSWD --opt $db | gzip > $BACKUP_FILE_PATH;
  # Set desired permissions
  chmod -R 600 $BACKUP_FILE_PATH
done

Das obenstehende Skript macht folgendes:

  • Zuerst setze ich einige Variablen, wie das Zielverzeichnis für die Backup-Dateien. Ich lese das aktuelle Datum aus und speichere es in einer Variable und benutze dies, um für die täglichen Backups jeweils ein neues Zielverzeichnis innerhalb des festgelegten “DB_BACKUP_ROOT” zu erstellen.
  • Danach setze ich die MySQL-Zugangsdaten und ermittle den Host-Namen des Rechners, um diesen später auch zum Dateipfad hinzuzufügen.
  • Mittels des “find”-Befehls ermittle ich alle Backups, welche älter als 30 Tage sind und lösche diese.
  • Danach iteriere ich in einer Schleife über alle relevanten Datenbanken und speichere diese mittels “mysqldump” an den gewünschten Zielort, dabei pipe ich das Resultat von “mysqldump” zuvor noch an “gzip” und sorge damit, dass ich eine Archivdatei erhalte.
  • Zum Schluss ändere ich mittels “chmod -R 600 …” noch die Dateirechte der zuvor erstellten Backup-Datei, so dass nur noch der Besitzer der Datei diese lesen und ändern kann.

Nun habe ich die Datenbank gesichert. Mittels eines weiteren Shells-Skripts sichere ich nun noch die Dateien der einzelnen Webseiten:

#!/bin/bash

WWW_ROOT="/var/wwwdir/websites/"
WWW_BACKUP_ROOT="/zielpfad/zum/backup/www"
WWW_BACKUP="$WWW_BACKUP_ROOT`date +%Y-%m-%d`"
HN=`hostname | awk -F. '{print $1}'`
# Create the backup directory
mkdir -p $WWW_BACKUP

# Remove backups older than 30 days
find $WWW_BACKUP_ROOT -maxdepth 1 -type d -mtime +30 -exec rm -rf {} \;

# get arguments
while [ "$1" != "" ]; do
  case $1 in
    -s | --site )     shift
                      site=$1
                      ;;
    * )               echo "Unknown argument $1."
                      exit 1
  esac
  shift
done

# Backup website
if [ "$site" != "" ]; then
  BACKUP_PATH="$WWW_BACKUP/www-$HN-$site-$(date +%Y-%m-%d).tar.gz"
  WWW_PATH="$WWW_ROOT$site"
  echo "Backing up site $WWW_PATH to $BACKUP_PATH" 
  cd $WWW_ROOT
  tar -czf $BACKUP_PATH $site
  chmod -R 600 $BACKUP_PATH
else
  echo "You need to specifiy a site to backup with the -s argument."
  exit 1
fi

Dieses Skript macht folgendes:

  • Als erstes setze ich wieder einige Variablen. Das Quellverzeichnis der Webseiten-Daten wird angegeben (WWW_ROOT) und das (Wurzel-)Zielverzeichnis für die Backup-Daten wird festgelegt (WWW_BACKUP_ROOT). Ausserdem lege ich den Ordernnamen für die täglichen Backups fest (WWW_BACKUP), dazu verwende ich wie schon beim MySQL-Backup das “date”-Befehl.
  • Mittels mkdir -p $WWW_BACKUP wird der tägliche Backup-Ziel-Ordner erstellt, es sei denn er ist schon vorhanden (dazu ist der “-p”-Parameter da).
  • Erneut suche ich mittels “find” alle Backup-Dateien, welche älter sind als 30 Tage und lösche diese.
  • Danach kommt eine “while”-Schleife, mit der ich die Argumente, welche dem Skript übergeben wurden auslese, dies ist nötig, da ich das Skript später mittels Cron-Job aufrufen möchte und jeweils die Quell-Webseite mittels eines Parameters angeben möchte. Wenn keine Website als Argument übergeben oder falls ein unbekanntes Argument übergeben wurde, wird das Skript beendet (exit 1).
  • Danach wird die eigentliche Backup-Datei erstellt. Dazu wird der Zielpfad der Archivdatei zusammengesetzt (BACKUP_PATH) und dann mittels dem “tar”-Befehl das Backup erstellt und gepackt.
  • Am Ende passe ich wieder die Dateirechte mittels “chmod” an, so dass nur der Besitzer die Datei lesen und ändern kann.

Nun haben wir zwei Skripts, welche die für mich relevanten Komponenten der Webseiten sichern können, jedoch müssten diese immernoch manuell ausgeführt werden. Damit automatisch täglich die Backups erstellt werden, richtete ich unter einem Account einige Cronjobs (Befehl: crontab -e) ein, welche die (ausführbaren) Skripte einmal am Tag laufen lassen:

# Daily website backup
15 2 * * * /usr/local/bin/mysql_backup.sh >/dev/null 2>&1
15 2 * * * /usr/local/bin/www_backup.sh -s website_dir_1 >/dev/null 2>&1
15 2 * * * /usr/local/bin/www_backup.sh -s website_dir_2 >/dev/null 2>&1
15 2 * * * /usr/local/bin/www_backup.sh -s website_dir_3 >/dev/null 2>&1
45 4 * * * /usr/local/bin/copy_backup_to_different_server.sh >/dev/null 2>&1

Die Backups werden mit dieser Konfiguration also immer um 2:15 Uhr durchgeführt. “/dev/null 2>&1” bewirkt, dass die Ausgaben unterdrückt werden, sie landen im Nirvana.

In der letzten Zeile der obenstehenden Crontab seht ihr, dass noch ein weiteres Skript “copy_backup_to_different_server.sh” zu einem späteren Zeitpunkt aufgerufen wird.
Da die Daten bisher nur lokal auf dem Server gesichert werden, nützen sie mir nichts, falls es zu einem generellen Datenverlust auf dem Server käme. Zur Sicherheit kopiere ich also die Backup-Archive noch an einen weiteren Ort, auf einen anderen Server.

Das  Skript ist wie folgt aufgebaut:

#!/bin/bash

rsync -rlthvz --delete --owner=1111 --group=333 -e '/usr/bin/ssh -i /home/userXy/.ssh/id_rsa' /zielpfad/zum/backup/ user@remote.server.com:/volumeXy/home/userMn/MyFiles/Backup/websites
  • Das Skript kopiert via RSYNC alle neuen Dateien von “/zielpfad/zum/backup” auf den Server “remote.server.com” in den Pfad “/volumeXy/home/userMn/MyFiles/Backup/websites”. Die Verbindung wird im Beispiel mit dem Benutzer “user” durchgeführt.
  • Die Verbindung geschieht durch den SSH-Aufruf innerhalb des RSYNC-Befehls über eine verschlüsselte SSH-Verbindung.
  • “–delete” bewirkt, dass im Ziel die Dateien gelöscht werden, welche nicht mehr in der Quelle vorhanden sind.
  • Mittels “–owner=…” und “–group=…” setze ich den Besitzer und die Gruppe der neuen Dateien und Ordner auf dem Zielsystem, da diese nicht mit denen auf dem Quellsystem identisch sind.

Damit RSYNC via SSH auch ohne Kennwort-Eingabe funktioniert, muss man natürlich zuerst ein Schlüssel-Paar (öffentlich/privat) erzeugen (sofern man noch keines hat) und den öffentlichen Schlüssel auf dem Zielsystem in den “Authorized Keys” eintragen. Hier findet ihr eine Anleitung dazu: http://www.thegeekstuff.com/2008/11/3-steps-to-perform-ssh-login-without-password-using-ssh-keygen-ssh-copy-id/

Nun ist das Backup also eingerichtet. Es sichert täglich automatisch die Websites mitsamt der Datenbank und kopiert diese Dateien zur Sicherheit auch noch auf einen anderen Server.

Rund-Wanderung von Abbaye Notre-Dame de Sénanque nach Gordes und zurück

Letzten Juni war ich mit meiner Freundin für einige Tage in Südfrankreich, in der Provence.

Wir machten einige Wanderungen, unter anderem eine von der Abbaye Notre-Dame de Sénanque nach Gordes und dann auf einem anderen Weg wieder zurück zur Abtei.

Ich habe die Strecke mit meinem HTC Hero S mittels Endomondo aufgezeichnet (am Anfang scheint es falsche Daten aufgezeichnet zu haben):

Insgesamt waren wir etwas längers als 5 Stunden unterwegs und wanderten dabei ca. 16 Kilometer.

Wir hatten einen Wanderführer (DuMont Wanderführer Provence) was die Wegfindung erleichterte, allerdings auch nicht immer. An gewissen Stellen verliefen wir uns nur per Zufall nicht und ab ca. Kilometer 11 konnten wir den Weg gemäss Wanderführer nicht mehr ermitteln und orientierten uns an den  Trampelpfaden und Wegweisern.

Die AbteiLos ging es auf dem Parkplatz der Abtei Notre-Dame de Sénanque, welche wir zuvor noch kurz besichtigten. Leider war gerade keine Blütezeit des Lavendels, diese hätte erst einige Wochen später angefangen und die Lavendelfelder hätten dann viel schöner ausgeschaut, aber auch so ist die Abtei schön anzuschauen.

EinödeZuerst ging es nacWegweiser zur Abteih Norden und der Weg wurde bereits ziemlich unwegsam (schmal und voll von Ginsterbüschen). Ein Wegweiser welcher im Wanderführer vermerkt war, bestätigte uns, dass wir noch auf dem richtigen Pfad waren.

Danach wurde das Gelände immer steineriger, bis schliesslich gar kein Weg mehr ersichtlich war. Wir wussten einfach, dass wir uns weiter hinauf bewegen mussten, bis wir in der Ferne ein grosses weisses Schild sehen würden, was wir dann auch taten.Steiniges Gelände ohne sichtbaren Weg

Wilde WieseOben angekommen, wurde der Weg wieder breiter und flacher. Hier trafen wir auch das erste Mal wieder auf andere Menschen, welche ebenfalls wanderten, allerdings andere Wege einschlugen als wir.

Kurz vor Gordes, Aussicht auf die ProvenceNach einer Weile ging der Weg dann flach bergab, führt durch einige kleinere Siedlungen mit schönen Häusern und auch teilweise für kurze Strecken über gut befestigte Strassen. Nach kurzer Zeit kamen wir dann an den Rand des Hochplateaus von dem wir eine gute Übersicht über einen Teil der Provence hatten.

Auf dem Weg kurz vor GordesDann gingen wir weiter am Rand dieses Plateaus entlang und nach wenigen hundert Metern tauchten vor uns die ersten Häuser von Gordes auf, einem malerischen, an einem Hang gelegenen Ort. Bei Kilometer 7,5 hatten wir einen guten Blick auf die Ortschaft, man sieht schön die Häuser, Villas und Hotels, welche sich auf und am Hang befinden, wie nebenstehendes Bild zeigt.Gordes

BorieNach Gordes liefen wir in Richtung der “Village des Bories“, einem Freilichtmuseum mit Uralten Steinhäusern, welches wir dann auch noch kurz besichtigten (KM 9,4), bevor wir die Wanderung fortsetzten.

Autowrack am WegesrandAuf dem weiteren Weg stiessen wir auf ein altes Autowrack, welches vermutlich vor Jahrzehnten hier illegal entsorgt wurde. An der Tür hatte es auch einige Löcher.. aber bestimmt keine Einschusslöcher und wenn, dann wahrscheinlich nur, weil das Auto später für Zielübungen verwendet wurde – ganz bestimmt.

EinödeTrampelpfadWie breits erwähnt verliessen wir ab Kilometer 11 den Weg gemäss unserem Wanderführer und versuchten uns selbst zu orientieren. Sollte das gut gehen?

SchotterwegBei KM 13,8 verliefen wir uns dann für ein kurzes Stückchen, kehrten aber noch rechtzeitig um, da es langsam Abend wurde und wir uns in einer unwegsam und verlassenen Schlucht befanden.  Wäre nicht so toll gewesen dort zu übernachten :-)

Weg durch die SchluchtWeg in der SchluchtIMG_8295 423

Es wird unwegsamerAn gewissen Stellen in dieser Schlucht mussten wir sogar einige Meter an einer Felswand hochklettern. Diese war zum Glück nicht senkrecht. Auch sonst wurde der Weg immer enger, verwachsener und abenteuerlicher.

Schlussendlich endete die Schlucht dann doch noch irgendwann und wir konnten sie bei KM 15,7 wieder verlassen und befanden uns nun auf der Rückseite der Abtei.Hinterer Bereich der Abtei

Alles in allem eine tolle Wanderung, welche teilweise über freies Gelände ohne Wege führt und auch manchmal nicht gut mit Wegweisern markiert ist, aber mit ein bisschen studieren und ausprobieren sollte man das Ziel finden :-) Ein Wanderführer oder eine GPS-Route erleichert die Wegfindung.

Abtei Notre-Dame de Sénanque

Feeds parsen und schreiben mit Python

Ich hatte letzthin das Problem, dass ich auf meiner neuen Fotografie-Seite einen Feed meiner letzten iStockphoto-Uploads einbinden wollte, aber dies nicht ging, da der offizielle Feed scheinbar nicht validiert:
http://validator.w3.org/feed/check.cgi?url=http%3A%2F%2Fwww.istockphoto.com%2Fistock_myfiles_rss.php%3FID%3D8197370

Auf WordPress verwendete ich zuerst das “Embed RSS“-Plugin um Feeds in die Seite zu integrieren, jedoch kam dies nicht mit diesem ungültigen Feed klar. Einige RSS-Reader-Programme (z.B. “Vienna RSS“) hatten jedoch keine Probleme mit dem Feed. Vermutlich korrigierten sie intern die Fehler oder ignorierten diese einfach.

Zum Glück hat mich einmal ein guter Freund in die Welt von Python eingeführt und mir die nötigen Basics dieser tollen Programmiersprache beigebracht.

Mit Hilfe der Bibliotheken “feedparser” und “PyRSS2Gen” konnte ich ein Python-Skript schreiben um dieses Problem umgehen.

Zuerst hole ich mit feedparser den Feed ab und baue später mit PyRSS2Gen einen neuen Feed im “RSS 2.0″-Format neu auf. Abschliessend schreibe ich diesen neuen Feed als XML-Datei in das Verzeichnis des Webservers.

Das Skript habe ich so umgebaut, dass eine beliebige iStockphoto-Benutzer-ID als Parameter übergeben werden kann. Auch die Ziel-Datei kann als Paramter übergeben werden.

Hier das Skript, welches in einem Repository auf Github gehosted ist:

#!/usr/bin/python

import datetime
import feedparser
import getopt
import os
import PyRSS2Gen
import sys

def create_latest_uploads_feed(targetPath, feedTitle, feedUrl, feedDescription, entries):
  rssItems = []
  for entry in entries:
    rssItem = PyRSS2Gen.RSSItem(
        title = entry['title'],
        link = entry['link'],
        description = entry['description'],
        guid = PyRSS2Gen.Guid(entry['link']),
        pubDate = entry['pubDate'],
        author = entry['author']
        )
    rssItems.append(rssItem)
  rss = PyRSS2Gen.RSS2(
      title = feedTitle,
      link = feedUrl,
      description = feedDescription,
      lastBuildDate = datetime.datetime.now(),
      items = rssItems)
  print 'Writing RSS feed: ' + targetPath
  rss.write_xml(open(targetPath, 'w'))
  print 'Done!'


def fetch_feed_and_create_rss(userId, targetPath):
  feedUrl = "http://www.istockphoto.com/istock_myfiles_rss.php?ID=" + userId
  print 'Fetching feed: ' + feedUrl
  feed = feedparser.parse(feedUrl)
  feedTitle = feed['feed']['title']
  feedDescription = ""
  entries = feed['entries']
  preparedEntries = []
  print 'Processing fetched entries. Entries found: ' + str(len(entries))
  for entry in entries:
    entryPubDate = entry['published']
    pubDate = datetime.datetime.strptime(entryPubDate[:-6], '%Y-%m-%dT%H:%M:%S')
    #pubDateClean = pubDate.strftime('%d.%m.%Y')
    author = entry['author']
    entryTitle = entry['title']
    print ' * Entry title: ' + entryTitle.encode('utf-8')
    link = entry['link']
    summaryHtml = entry['summary']
    preparedEntries.append({"pubDate":pubDate,"author":author,"title":entryTitle,
      "description":summaryHtml,"link":link})
  create_latest_uploads_feed(targetPath, feedTitle, feedUrl, feedDescription,
      preparedEntries)

def main(argv):
  scriptName = os.path.basename(__file__)
  usageInfo = 'usage: ' + scriptName + ' -i  -t '
  userId = ''
  targetPath = ''
  try:
    opts, args = getopt.getopt(argv,"hi:t:",["id=","targetpath="])
  except getopt.GetoptError:
    print usageInfo
    sys.exit(2)
  for opt, arg in opts:
    if opt == '-h':
      print usageInfo
      sys.exit()
    elif opt in ("-i", "--id"):
      userId = arg
    elif opt in ("-t", "--targetpath"):
      targetPath = arg
  if (userId and targetPath):
    print 'User ID is: ' + userId
    print 'Target path is: ' + targetPath
    fetch_feed_and_create_rss(userId, targetPath)
  else:
    print usageInfo
    sys.exit()


if __name__ == "__main__":
  main(sys.argv[1:])
istock_latest_uploads.pyview rawview file on GitHub

Genau genommen validiert der neue Feed immer noch nicht :-) allerdings sind die Fehler nun scheinbar weniger tragisch und er kann nun von “Embed RSS” gelesen werden. Auch verwende ich nun zur Einbettung in WordPress “HungryFEED” anstelle von “Embed RSS”, da dieses etwas mächtiger ist und bessere Templating-Option mitbringt.

Damit ich für die Webseite immer einen relativ aktuellen Feed erhalte, lasse ich das Skript mittels Crontab alle vier Stunden laufen:

#m h dom mon dow command
0 */4 * * * python /usr/local/bin/scriptXy.py -i 8197370 -t /dir/subdir/targetfile.xml

Der Blog von Patric Schielke