version 0.0, not yet usable
authorSabine Engelhardt (Becker) <frosch@atari-frosch.de>
Thu, 6 Apr 2017 15:13:39 +0000 (17:13 +0200)
committerSabine Engelhardt (Becker) <frosch@atari-frosch.de>
Thu, 6 Apr 2017 15:13:39 +0000 (17:13 +0200)
36 files changed:
ToDo [new file with mode: 0644]
defaults.py [new file with mode: 0755]
filters/atarifrosch.py [new file with mode: 0755]
fullsettings.py [new file with mode: 0644]
htmlglue.py [new file with mode: 0755]
htmlglue.py.old [new file with mode: 0755]
preparesite.py [new file with mode: 0755]
sites/atarifrosch/CSS/akv.css [new file with mode: 0644]
sites/atarifrosch/CSS/brief.css [new file with mode: 0644]
sites/atarifrosch/CSS/frosch.css [new file with mode: 0644]
sites/atarifrosch/DEFAULTS/__init__.py [new file with mode: 0755]
sites/atarifrosch/DEFAULTS/settings.py [new file with mode: 0755]
sites/atarifrosch/SNIPPETS/akvsbody.htm [new file with mode: 0644]
sites/atarifrosch/SNIPPETS/akvstyle.htm [new file with mode: 0644]
sites/atarifrosch/SNIPPETS/bodytop.htm [new file with mode: 0644]
sites/atarifrosch/SNIPPETS/endoffile.htm [new file with mode: 0644]
sites/atarifrosch/SNIPPETS/flattrbutton.htm [new file with mode: 0644]
sites/atarifrosch/SNIPPETS/flattrscript.htm [new file with mode: 0644]
sites/atarifrosch/SNIPPETS/footer.htm [new file with mode: 0644]
sites/atarifrosch/SNIPPETS/head.htm [new file with mode: 0644]
sites/atarifrosch/SNIPPETS/logo.htm [new file with mode: 0644]
sites/atarifrosch/SNIPPETS/menu.cacert.htm [new file with mode: 0644]
sites/atarifrosch/SNIPPETS/menu.geocaching.htm [new file with mode: 0644]
sites/atarifrosch/SNIPPETS/meta.flattrverify.htm [new file with mode: 0644]
sites/atarifrosch/__init__.py [new file with mode: 0755]
sites/atarifrosch/impressum.htm [new file with mode: 0644]
sites/atarifrosch/impressum.py [new file with mode: 0755]
sites/atarifrosch/index.htm [new file with mode: 0644]
sites/atarifrosch/index.py [new file with mode: 0755]
sites/atarifrosch/internet/__init__.py [new file with mode: 0755]
sites/atarifrosch/internet/menu.py [new file with mode: 0755]
sites/atarifrosch/internet/stream-mit-linux.htm [new file with mode: 0644]
sites/atarifrosch/internet/stream-mit-linux.py [new file with mode: 0755]
sites/atarifrosch/menu.py [new file with mode: 0755]
sites/atarifrosch/start.py [new file with mode: 0755]
start.py [new file with mode: 0755]

diff --git a/ToDo b/ToDo
new file mode 100644 (file)
index 0000000..5b76fba
--- /dev/null
+++ b/ToDo
@@ -0,0 +1,79 @@
+get own path from sys.argv[0]
+get theme dir from sys.argv[1]
+opt: get dir or file to build from sys.argv[n]
+no additional parameter(s): (re)build whole site
+-d --dir $dir 
+-f --file $file
+-r --recursive (with -d, ignore if -f)
+
+does theme dir exist?
+opt: does dir/file exist?
+
+theme dir must have subdirs CSS, DEFAULT, SNIPPETS
+theme dir must have index.py, index.htm, menu.py
+subdir CSS must have at least one css file
+subdir DEFAULTS must have at least settings.py
+subdir SNIPPETS must have at least
+- bodytop.htm
+- footer.htm
+- endoffile.htm
+
+add DEFAULTS, SNIPPETS to path
+
+does theme/html exist, if not: mkdir
+
+full (re)build (no parameters given)
+or (recursive) (re)build of one folder
+
+if -d|--dir:
+- if menu.py:
+  - import menu.py
+  - if -r|--recursive (as function):
+    - read all folder names from menu.py in folderlist[]
+    - use folderlist to read menu.py from each subfolder
+    - add all found folders to folderlist[]
+      (need to remember current dir!)
+    - recursive, until all menu.py are processed
+
+    - for each folder in folderlist[]:
+      - import menu.py
+        - for each menuitem[]: function makemenuitem
+
+def makemenuitem(menuitem):
+- split into its parts (must be 4 parts) 
+- if menuitem is htm (part 0):
+  - check whether according content file (.htm) exists in currentdir
+    - if not: pass
+  - check whether according .py file exists in currentdir
+    - if not: pass
+  - call function buildpage with filename, currentdir
+
+def buildpage(filename, currentdir)
+- build page targetfile with settings, SNIPPETS, and menulist
+- import DEFAULTS/settings.py 
+  - add currentdir to path (in order to import .py files)
+  - import menu.py from currentdir
+  - if according menuitem exists:
+    - split into its parts (must be 4 parts)
+    - check whether according content file exists in currentdir
+    - check whether according content file (.htm) exists in currentdir
+      - if not: pass
+    - check whether according .py file exists in currentdir
+      - if not: pass
+    - call function buildpage with filename, currentdir
+    - build page targetfile with settings, SNIPPETS, and menulist
+      - import DEFAULTS/settings.py 
+      - add currentdir to path (in order to import .py files)
+      - import $page.py (menuitem part 2)
+      - own menu entry without link to itself
+          
+
+if -f|--file targetfile:
+- create currentdir and contentfile from targetfile
+- buildpage(contentfile, currentdir)
+
+----------------------------------------------------------------
+
+Additional function: Get content file date from original (old) html files
+and write to DEFAULTS/modif.lst
+delete entries from modif.lst when once used
diff --git a/defaults.py b/defaults.py
new file mode 100755 (executable)
index 0000000..3350f7d
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/env python3
+# -*- coding: utf8 -*-
+
+myversion = "0.0"
+generator = "htmlglue v" + myversion
diff --git a/filters/atarifrosch.py b/filters/atarifrosch.py
new file mode 100755 (executable)
index 0000000..e54fc8b
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+# -*- coding: utf8 -*-
+
+origpath = "/srv/atarifrosch/"
+filext = ".html"
+targetpath = "/home/frosch/projekte/htmlglue/themes/atarifrosch/"
+
+ignoredirs = ["bilder", "computer/hw", "computer/x100e", "depression/tagebuch"]
+ignorefiles = []
+
+ignoreparts = []
+# ignoreparts defines a keyword from the first line and a keyword from the
+# last line that has to be ignored. %% is the placeholder between start
+# and end.
+# Do NOT ignore the menu here, as it needs a special 'treatment'.
+
+ignorehtml[0] = '<body>%%<!--div#logo-->'
+ignorehtml[1] = '<div id="footer">%%</html>'
+ignorehtml[2] = '<!--[if gte IE 5.5]>%%<![endif]-->'
+ignorehtml[3] = '<!-- AKVS body start v1.6 -->%%<!-- AKVS body end -->'
+
+menustart = '<div id="menu">'
+menuend = '<!-- div#menu -->'
+
+headlinekey = "<h1>"
+
+contentstart = '<div id="content">'
+contentend = '<!-- div#content -->'
+
+# while reading the content, we can replace a few things
+# that are mostly left from the iso-latin age …
+replacement = {}
+replacement["&bdquo;"] = "„"
+replacement["&rdquo;"] = "“"
+replacement["&mdash;"] = "–"
+replacement["&reg;"] = "®"
+replacement["(tm)"] = "™"
+replacement["..."] = "…"
+replacement["&middot;"] = "·"
diff --git a/fullsettings.py b/fullsettings.py
new file mode 100644 (file)
index 0000000..e888ba1
--- /dev/null
@@ -0,0 +1,213 @@
+#!/usr/bin/env python3
+# -*- coding: utf8 -*-
+
+#############################################################################
+## These variables are expected to be set in the DEFAULTS/settings.py file
+## of each theme, so that they can be used as a standard for every single
+## page. All variables are expected to be of type string if not stated
+## otherwise. All settings in this file are just examples!
+#############################################################################
+
+# As I wanted to have some current information in the page foot, I wrote
+# this function that creates the footer section, using a SNIPPETS file, the
+# timestamp of the content file, and the current date/time. Of course you
+# don't have to use this and can build your footer the way you want it :-)
+def makefooter(contentfile, targetfile, myurl):
+    from os import path
+    from datetime import datetime
+    from time import strftime
+
+    f = open("../SNIPPETS/footer.htm", "r")
+    footer = f.read()
+    f.close()
+
+    if path.exists("./modif.lst"):
+        # TODO: pick according line from that file and use the timestamp from there
+        # also touch contentfile with that timestamp and then delete line from file
+        filetimestamp = ""
+    else:
+        # TODO: contentfile needs to be expanded to its full path before using it
+        filetime = path.getmtime(contentfile)
+        filetimestamp = str(strftime(filetime, "%Y-%m-%d %H:%M"))
+
+    currenttime = datetime.now()
+    curtimestamp = str(strftime(currenttime, "%Y-%m-%d %H:%M"))
+
+    footer = footer.replace("[FILETIME]", filetime)
+    footer = footer.replace("[CURTIME]", curtimestamp)
+    footer = footer.replace("[MYURL]", myurl)
+
+    return footer
+
+
+# Name of the theme and also the name of the theme's directory, so better
+# don't use special characters here.
+theme = "mytheme"
+
+# Standard protocol of the site (one out of "http", "https")
+# This statement can NOT be overwritten by a page's .py file!
+proto = "https"
+
+# domain name of the site
+# This statement can NOT be overwritten by a page's .py file!
+domain = "www.atari-frosch.de"
+
+## The following variables can be overwritten in a page's .py file.
+
+# Position of menu: "above" or "below" content. Be reminded that this is
+# only the menu's position in the HTML code; where it will really be placed
+# within the rendered page depends on your stylesheet.
+menupos = "above"
+
+# Document type to be stated at the beginning of each html document
+doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
+
+# HTML version that is used in a document
+htmlver = '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">'
+
+## Standard settings for each document's <head> section
+
+# content of the <title> tag, usually to be overwritten in each page's .py file
+title = "Froschs Homepage"
+# will become: <title>Froschs Homepage</title>
+
+# settings for the robots statement
+robots = 'index,follow'
+# will become: <meta name="robots" content="index,follow" />
+
+# general language setting of the site
+language = 'de'
+# will become: <meta http-equiv="content-language" content="de" />
+# and: <meta name="DC.Language" content="de" />
+
+# author of the page
+author = "Atari-Frosch"
+# will become: <meta name="author" content="Atari-Frosch" />
+
+# Keywords for search engines – if you want to use them. And if, they should
+# be overwritten individually by each page's .py file.
+metakeywords = "atari-frosch, atari, computer, depression"
+# will become: <meta name="keywords" content="atari-frosch, atari, computer, depression" />
+
+# The content of the DC.Title meta tag
+dctitle = "Atari-Frosch"
+# will become: <meta name="DC.Title" content="Atari-Frosch" />
+
+# Creator of the page (not necessarily the same person as the author of
+# the page content)
+dccreator = 'Atari-Frosch'
+# will become: <meta name="DC.Creator" content="Atari-Frosch" />
+
+# Publisher of the page (not necessarily the same person as the author
+# or the creator of the page)
+dcpublisher = 'Atari-Frosch'
+# will become: <meta name="DC.Publisher" content="Atari-Frosch" />
+
+# Coverage: This meta tag is only useful if the page is about a special
+# area, region, town, etc.
+dccoverage = "Düsseldorf - Deutschland"
+# will become: <meta name="DC.Coverage" content="Düsseldorf - Deutschland" />
+
+# copyright holder for the page content
+dcrights = "Sabine 'Atari-Frosch' Becker"
+# will become: <meta name="DC.Rights" content="Sabine 'Atari-Frosch' Becker" />
+
+# file name of the favicon (leave empty if there is none)
+favicon = 'favicon.ico'
+# will become: <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
+
+# Primary style sheet for the site, file must exist in the CSS directory of
+# the theme
+stylesheet = "frosch.css"
+stylesheetmedia = "all"
+# will become: <link rel="stylesheet" media="all" href="/frosch.css" type="text/css" />
+
+# The meta tags 'generator' and 'DC.Date' will be created for each page
+# during runtime of htmlglue.
+
+
+
+#########################################################################
+## The following variables can or need to be used additionally in .py
+## files for single pages. If the optional variables are not set here,
+## their data will not appear in the target file.
+#########################################################################
+
+# Name of the file that should be created, including directory path
+# (= URL without protocol and domain name).
+# If not set, the target file's name will be created by the name of the
+# content file and the directory it sits in.
+targetfile = "/index.html"
+
+# This will be the complete URL of the page; we need this variable later.
+# This setting is needed in each page's .py file and is NOT optional. You
+# should not change anything here.
+myurl = settings.proto + "://" + settings.domain + targetfile
+
+# You can add additional css files here; each entry needs a css file name
+# and the media type it is meant for. The added css files must exist in
+# the theme's CSS directory. Optional.
+# Variable type: list of strings with 2 statements per list item.
+addcssfile = ["akv.css all"]
+# will become: <link rel="stylesheet" media="all" href="/akv.css" type="text/css" />
+
+# You can add more meta tags to the <head> section here; the stated
+# .htm files must exist in the SNIPPETS directory of the theme. Don't add
+# css files this way, use the addcssfile variable instead. Optional.
+# Variable type: list of strings
+addmeta = ["meta.flattrverify.htm"]
+# (BTW, flattrverify is needed only once: in the <head> section of the
+# start page in order to identify your site to flattr.com.)
+
+# You can add additional HTML code to the <head> section. Optional. You can
+# also add inline (!) CSS for an individual page by adding another SNIPPETS
+# file to this list in the .py file of that page.
+# Variable type: list of strings
+addhead = ["flattrscript.htm", "akvhead.htm"]
+
+## Now for the content section:
+
+# In this file we should find the content section of the current page. It is
+# expected to be in the same directory as the according .py file. If it does
+# not exist, the according page and menu entries will not be created. So this
+# statement is NOT optional.
+contentfile = "index.htm"
+
+# Here you can state keywords with a SNIPPETS file, and while creating the
+# page the keywords will be replaced by the snippet. If the snippet does not
+# exist in the SNIPPETS directory of the theme, the keyword will just be deleted
+# from the content.
+# Variable type: dictionary with keywords and SNIPPET file names
+addtobody = {}
+addtobody["[MYURL]"] = myurl
+addtobody["[AKVSBODY]"] = "akvsbody.htm"
+addtobody["[LOGO]"] = "logo.htm"
+
+# You will need to add these variables in order to be able to use a flattr
+# button on the current page. myurl (created earlier), Thing number, and
+# Thing name are needed to call the flattr api correctly.
+# Be reminded that you need to place the 'flattrscript.htm' snippet in the
+# <head> section of the page in order to get the flattr button to work.
+addtobody["[FLATTRBUTTON]"] = "flattrbutton.htm"
+# Although the 'thing' is a number, we need a string here, not a digit!
+addtobody["[FLATTRTHING]"] = "000000"
+# You also need to state the name of your flattr thing.
+addtobody["[THINGNAME]"] = "this-is-my-flattr-thing"
+
+# As the menu is not given as an .htm file, but built by htmlglue while
+# processing a menu.py of a (sub)directory of the theme, we cannot use a
+# dictionary like addtobody to add static menu entries from the SNIPPETS on
+# single pages. Instead the files from this list get added to a page's menu.
+# This also means that static menu entries from the SNIPPETS cannot be
+# placed anywhere in the menu list, but only added to its end.
+# Variable type: list of strings
+menuadd = ["menu.geocaching.htm", "menu.cacert.htm"]
+
+# This will be the headline of the current page. Depending on the way your
+# theme is built it is possible that the headline is not part of the page
+# content section, but only the content section is to be placed into the
+# content file, so the headline should be stated in the .py file, at least
+# if necessary.
+headline = "Qua®k!"
+
+pagefoot = makefoot(contentfile, targetfile, myurl)
diff --git a/htmlglue.py b/htmlglue.py
new file mode 100755 (executable)
index 0000000..932afce
--- /dev/null
@@ -0,0 +1,339 @@
+#!/usr/bin/env python3
+# -*- coding: utf8 -*-
+
+import sys, os
+from time import strftime
+from datetime import datetime
+
+def checkfiles(sourcepath):
+    filelist = os.listdir(sourcepath)    
+    cleanfiles = []
+    for i in range(len(filelist)):
+        currentfilename = filelist[i]
+        ignorelist = ['', '.', '..']
+        if not currentfilename in ignorelist:
+            if not os.path.isfile(currentfilename):
+                cleanfiles.append(currentfilename)
+            if currentfilename.endswith(".htm"):
+                cleanfiles.append(currentfilename)
+    return cleanfiles
+
+def buildpage(sourcepath, sourceconf, theme):
+    from sourcepath import sourceconf as pageconfig
+    filetime = os.path.getmtime(pageconfig.contentfile)
+    fileday = strftime(filetime, "%Y-%m-%d")
+    filetimestamp = strftime(filetime, "%Y-%m-%d %H:%M:%S")
+    targetfile = pageconfig.targetfile
+
+    from defaults import myversion
+
+    metaend = '" />\n'
+
+    if pageconfig.doctype in locals():
+        htmlpage = settings.doctype + "\n"
+    elif settings.doctype in locals():
+        htmlpage = pageconfig.doctype + "\n"
+    else:
+        htmlpage = ""
+
+    if pageconfig.htmlver in locals():
+        htmlpage += htmlver + "\n"
+    elif settings.htmlver in locals():
+        htmlpage += htmlver + "\n"
+    else:
+        htmlpage += '<html>\n'
+    htmlpage += '  <head>\n'
+
+    if pageconfig.title in locals():
+        htmlpage += '    <title>' + pageconfig.title + '</title>\n'
+    elif settings.title in locals():
+        htmlpage += '    <title>' + settings.title + '</title\n'
+    elif pageconfig.dctitle in locals():
+        htmlpage += '    <title>' + pageconfig.dctitle + '</title>\n'
+    elif settings.dctitle in locals():
+        htmlpage += '    <title>' + settings.dctitle + '</title>\n'
+    else:
+        htmlpage += '    <title>' + settings.domain + '</title>\n'
+
+    if pageconfig.description in locals():
+        htmlpage += '    <meta name="description" content="' + pageconfig.description + metaend
+    elif settings.description in locals():
+        htmlpage += '    <meta name="description" content="' + settings.description + metaend
+
+    if pageconfig.creator in locals():
+        htmlpage += '    <meta name="author" content="' + pageconfig.creator + metaend
+    elif settings.creator in locals():
+        htmlpage += '    <meta name="author" content="' + settings.creator + metaend
+
+    if pageconfig.robots in locals():
+        htmlpage += '    <meta name="robots" content="' + pageconfig.robots + metaend
+    elif settings.robots in locals():
+        htmlpage += '    <meta name="robots" content="' + settings.robots + metaend
+    else:
+        htmlpage += '    <meta name="robots" content="index,follow' + metaend
+
+    htmlpage += '    <meta name="generator" content="HTMLglue v' + myversion + metaend
+
+    if pageconfig.language in locals():
+        htmlpage += '    <meta http-equiv="content-language" content="' + pageconfig.language + metaend
+    elif settings.language in locals():
+        htmlpage += '    <meta http-equiv="content-language" content="' + settings.language + metaend
+    else:
+        pass
+
+    if pageconfig.dctitle in locals():
+        htmlpage += '    <meta name="DC.Title" content="' + pageconfig.dctitle + metaend
+    elif settings.dctitle in locals():
+        htmlpage += '    <meta name="DC.Title" content="' + settings.dctitle + metaend
+    else:
+        pass
+
+    if pageconfig.creator in locals():
+        htmlpage += '    <meta name="DC.Creator" content="' + pageconfig.creator + metaend
+    elif settings.creator in locals():
+        htmlpage += '    <meta name="DC.Creator" content="' + settings.creator + metaend
+    else:
+        pass
+
+    if pageconfig.subject in locals():
+        htmlpage += '    <meta name="DC.Subject" content="' + pageconfig.subject + metaend
+    elif settings.subject in locals():
+        htmlpage += '    <meta name="DC.Subject" content="' + settings.subject + metaend
+    else:
+        pass
+
+    if pageconfig.description in locals():
+        htmlpage += '    <meta name="DC.Description" content="' + pageconfig.description + metaend
+    elif settings.description in locals():
+        htmlpage += '    <meta name="DC.Description" content="' + settings.description + metaend
+    else:
+        pass
+
+    if pageconfig.publisher in locals():
+        htmlpage += '    <meta name="DC.Publisher" content="' + pageconfig.publisher + metaend
+    elif settings.publisher in locals():
+        htmlpage += '    <meta name="DC.Publisher" content="' + settings.publisher + metaend
+    else:
+        pass
+
+    htmlpage += '    <meta name="DC.Date" content="' + fileday + metaend
+
+    if pageconfig.dclang in locals():
+        htmlpage += '    <meta name="DC.Language" content="' + pageconfig.dclang + metaend
+    elif settings.dclang in locals():
+        htmlpage += '    <meta name="DC.Language" content="' + settings.dclang + metaend
+    elif pageconfig.language in locals():
+        htmlpage += '    <meta name="DC.Language" content="' + pageconfig.language + metaend
+    elif settings.language in locals():
+        htmlpage += '    <meta name="DC.Language" content="' + settings.language + metaend
+    else:
+        pass
+
+    if pageconfig.dccoverage in locals():
+        htmlpage += '    <meta name="DC.Coverage" content="' + pageconfig.dccoverage + metaend
+    elif settings.dccoverage in locals():
+        htmlpage += '    <meta name="DC.Coverage" content="' + settings.dccoverage + metaend
+    else:
+        pass
+
+    if pageconfig.dcrights in locals():
+        htmlpage += '    <meta name="DC.Rights" content="' + pageconfig.dcrights + metaend
+    elif settings.dcrights in locals():
+        htmlpage += '    <meta name="DC.Rights" content="' + settings.dcrights + metaend
+    else:
+        pass
+
+    # only in start page of the whole site
+    if pageconfig.flattr in locals():
+        htmlpage += '    <meta name="verify-v1" content="' + flattr + metaend
+
+    if pageconfig.favicon in locals():
+        htmlpage += '    link rel="shortcut icon" href="' + pageconfig.favicon + 'type="image/x-icon' + metaend
+    elif settings.favicon in locals():
+        htmlpage += '    link rel="shortcut icon" href="' + settings.favicon + 'type="image/x-icon' + metaend
+    else:
+        pass
+
+    if pageconfig.stylesheet in locals():
+        htmlpage += '   <link rel="stylesheet" media="all" href="' + pageconfig.stylesheet + 'type="text/css' + metaend
+    elif settings.stylesheet in locals():
+        htmlpage += '   <link rel="stylesheet" media="all" href="' + settings.stylesheet + 'type="text/css' + metaend
+    else:
+        pass
+
+    # additional css files are only expected in single pages, not for the whole site
+    if pageconfig.addcss in locals():
+        addcss = pageconfig.addcss
+        for i in range(len(addcss)):
+            cssinfoparts = addcss[i].split(" ")
+            htmlpage += '    <link rel="stylesheet" media="' + cssinfoparts[1] + '"'
+            htmlpage += 'href="' + cssinfoparts[0] + 'type="text/css' + metaend
+
+    # same for additional entries in <head>
+    if pageconfig.addhead in locals():
+        htmlpage += pageconfig.addhead
+
+    htmlpage += '  </head>\n'
+
+    bodytopfile = themepath + "/bodytop.htm"
+    logofile = themepath + "/logo.htm"
+
+    if os.path.exists(bodytopfile):
+        bodytop = open(bodytopfile, "r")
+        bodytophtml = bodytop.read()
+        bodytop.close()
+        htmlpage += bodytophtml
+    else:
+        print("bodytop in theme " + theme + " does not exist, aborting.")
+        exit()
+
+
+    if os.path.exists(logofile):
+        logo = open(logofile, "r")
+        logohtml = logo.read()
+        logo.close()
+        htmlpage += logohtml
+
+    if settings.menupos == "above":
+        # like in below
+
+    contentfile = open(sourcehtml, "r")
+    content = contentfile.read()
+    contentfile.close()
+    htmlpage += content
+
+    if settings.menupos == "below":
+        if pageconfig.menuadd in locals():
+            menu = buildmenu(sourcepath, pageconfig.menuentry, pageconfig.menuadd, pageconfig.menuclass, settings.menuhome)
+        else:
+            menu = buildmenu(sourcepath, pageconfig.menuentry, "", pageconfig.menuclass, settings.menuhome)
+        htmlpage += menu
+
+    # footer        
+
+
+def buildmenu(sourcepath, menuadd, mymenuclass, menuhome):
+
+    from sourcepath import menu
+
+    # TODO: replace this with a menustart.htm snippet
+    menulist  = '      <div id="menu">\n'
+    menulist += '        <ul>'
+
+    listitemend = '</a></li>\n'
+
+    # Startpage
+
+    for item in range(len(menu.menuitem)):
+        menuitemparts = menu.menuitem[item].split(", ")
+        if len(menuitemparts) == 4:
+            menutype = menuitemparts[0]
+            menuclass = menuitemparts[1]
+            menufile = menuitemparts[2]
+            menuitemname = menuitemparts[3]
+            
+            if item == 0:
+                # start page
+                pass
+            else:
+                pass
+        
+        else:
+            print("Error in menu.py in", sourcepath)
+            exit()
+
+
+
+    if mymenuclass == "home":
+        menu += '<li class="home">' + menuentry + '</li>\n'
+    else:
+        menu += '<li class="home"><a href="/">' + menuhome + listend
+
+    allfiles = os.listdir(sourcepath)
+    cleanfiles = checkfiles(allfiles)
+    if cleanfolders != "":
+        cleanfolders = cleanfolders.sort()
+        for i in range(len(cleanfolders)):
+            if cleanfo
+            from cleanfolders[1] import index
+            menu += 
+            
+                
+
+    if menuadd in locals():
+        for i in range(len(menuadd)):
+            snippetpath = mypath + "/theme/" + theme
+            snippetfile = snippetpath + "/menu." + menuadd[i] + ".htm"
+            snippet = open(snippetfile, "r")
+            menusnippet = snippet.read()
+            snippet.close()
+            htmlpage += menusnippet + "\n"
+
+# End of functions
+
+
+# check arguments
+mypath = "/".join(sys.argv[0]("/")[:-1])
+
+if len(sys.argv) > 1:
+    arguments = {"theme" = "", "targetdir" = "", "targetfile" = ""}
+    for args in range(len(sys.argv)):
+        if sys.argv[args] == "-t" or sys.argv[args] == "--theme":
+            arguments["theme"] = sys.argv[args + 1]
+        elif sys.argv[args] == "-d" or sys.argv[args] == "--dir":
+            arguments["targetdir"] = sys.argv[args + 1]
+        elif sys.argv[args] == "-f" or sys.argv[args] == "--file":
+            arguments["targetfile"] = sys.argv[args + 1]
+else:
+    print("Cannot work without at least a given theme.")
+    exit()
+
+# ToDo instead: compare file timestamps of pages and html, build
+# new html if page is newer or html does not exist at all.
+# Needs no more arguments from the command line.
+# How to find out if theme has been changed (and everything must
+# be rebuilt)?
+
+if arguments("theme") == "":
+    print("Cannot work without at least a given theme.")
+    exit()
+else:
+    themepath = mypath + "/themes/" + arguments["theme"]
+    sys.path.insert(0, themepath)
+
+
+targetpath = mypath + "/html/" + arguments["theme"] 
+if not targetpath.endswith("/"):
+    targetpath += "/"
+
+if arguments["directory"] != "":
+    targetpath += arguments["directory"]
+    if os.path.exists(targetpath):
+        os.mkdir(targetpath)
+
+# In this version the first file with given name gets used!
+# If there is another file with same name in another directory, it will not be found.
+if arguments("targetfile") != "":
+    targetfile = arguments["targetfile"]
+    if not targetfile.endswith(".html"):
+        print("invalid target file")
+        exit()
+
+    if "/" in targetfile:
+        targetfileparts = targetfile.split("/")
+        targetpath = mypath
+        for i in range(len(targetfileparts) -1):
+            targetpath += targetfileparts[i]
+        targetfile = targetfileparts[len(targetfileparts)]
+    else:
+        searchfile = targetpath + targetfile
+        if not os.path.exists(searchfile):
+            allfolders = os.listdir(filepath)
+            cleanfolders = checkfolders(allfolders)
+            for curfold in range(len(cleanfolders)):
+                currentfolder = cleanfolders[curfold]
+                currentpath = filepath + currentfolder + "/"
+                filelist = filesoffolder(currentpath)
+                if targetfile in filelist:
+                    targetpath = currentpath
+                    break
diff --git a/htmlglue.py.old b/htmlglue.py.old
new file mode 100755 (executable)
index 0000000..0b614ad
--- /dev/null
@@ -0,0 +1,319 @@
+#!/usr/bin/env python3
+# -*- coding: utf8 -*-
+
+import sys, os
+from time import strftime
+from datetime import datetime
+
+def checkfiles(sourcepath):
+    filelist = os.listdir(sourcepath)    
+    cleanfiles = []
+    for i in range(len(filelist)):
+        currentfilename = filelist[i]
+        ignorelist = ['', '.', '..']
+        if not currentfilename in ignorelist:
+            if not os.path.isfile(currentfilename):
+                cleanfiles.append(currentfilename)
+            if currentfilename.endswith(".htm"):
+                cleanfiles.append(currentfilename)
+    return cleanfiles
+
+def buildpage(sourcepath, sourceconf, theme):
+    from sourcepath import sourceconf as pageconfig
+    filetime = os.path.getmtime(pageconfig.contentfile)
+    fileday = strftime(filetime, "%Y-%m-%d")
+    filetimestamp = strftime(filetime, "%Y-%m-%d %H:%M:%S")
+    targetfile = pageconfig.targetfile
+
+    from defaults import myversion
+
+    metaend = '" />\n'
+
+    if pageconfig.doctype in locals():
+        htmlpage = settings.doctype + "\n"
+    elif settings.doctype in locals():
+        htmlpage = pageconfig.doctype + "\n"
+    else:
+        htmlpage = ""
+
+    if pageconfig.htmlver in locals():
+        htmlpage += htmlver + "\n"
+    elif settings.htmlver in locals():
+        htmlpage += htmlver + "\n"
+    else:
+        htmlpage += '<html>\n'
+    htmlpage += '  <head>\n'
+
+    if pageconfig.title in locals():
+        htmlpage += '    <title>' + pageconfig.title + '</title>\n'
+    elif settings.title in locals():
+        htmlpage += '    <title>' + settings.title + '</title\n'
+    elif pageconfig.dctitle in locals():
+        htmlpage += '    <title>' + pageconfig.dctitle + '</title>\n'
+    elif settings.dctitle in locals():
+        htmlpage += '    <title>' + settings.dctitle + '</title>\n'
+    else:
+        htmlpage += '    <title>' + settings.domain + '</title>\n'
+
+    if pageconfig.description in locals():
+        htmlpage += '    <meta name="description" content="' + pageconfig.description + metaend
+    elif settings.description in locals():
+        htmlpage += '    <meta name="description" content="' + settings.description + metaend
+
+    if pageconfig.creator in locals():
+        htmlpage += '    <meta name="author" content="' + pageconfig.creator + metaend
+    elif settings.creator in locals():
+        htmlpage += '    <meta name="author" content="' + settings.creator + metaend
+
+    if pageconfig.robots in locals():
+        htmlpage += '    <meta name="robots" content="' + pageconfig.robots + metaend
+    elif settings.robots in locals():
+        htmlpage += '    <meta name="robots" content="' + settings.robots + metaend
+    else:
+        htmlpage += '    <meta name="robots" content="index,follow' + metaend
+
+    htmlpage += '    <meta name="generator" content="HTMLglue v' + myversion + metaend
+
+    if pageconfig.language in locals():
+        htmlpage += '    <meta http-equiv="content-language" content="' + pageconfig.language + metaend
+    elif settings.language in locals():
+        htmlpage += '    <meta http-equiv="content-language" content="' + settings.language + metaend
+    else:
+        pass
+
+    if pageconfig.dctitle in locals():
+        htmlpage += '    <meta name="DC.Title" content="' + pageconfig.dctitle + metaend
+    elif settings.dctitle in locals():
+        htmlpage += '    <meta name="DC.Title" content="' + settings.dctitle + metaend
+    else:
+        pass
+
+    if pageconfig.creator in locals():
+        htmlpage += '    <meta name="DC.Creator" content="' + pageconfig.creator + metaend
+    elif settings.creator in locals():
+        htmlpage += '    <meta name="DC.Creator" content="' + settings.creator + metaend
+    else:
+        pass
+
+    if pageconfig.subject in locals():
+        htmlpage += '    <meta name="DC.Subject" content="' + pageconfig.subject + metaend
+    elif settings.subject in locals():
+        htmlpage += '    <meta name="DC.Subject" content="' + settings.subject + metaend
+    else:
+        pass
+
+    if pageconfig.description in locals():
+        htmlpage += '    <meta name="DC.Description" content="' + pageconfig.description + metaend
+    elif settings.description in locals():
+        htmlpage += '    <meta name="DC.Description" content="' + settings.description + metaend
+    else:
+        pass
+
+    if pageconfig.publisher in locals():
+        htmlpage += '    <meta name="DC.Publisher" content="' + pageconfig.publisher + metaend
+    elif settings.publisher in locals():
+        htmlpage += '    <meta name="DC.Publisher" content="' + settings.publisher + metaend
+    else:
+        pass
+
+    htmlpage += '    <meta name="DC.Date" content="' + fileday + metaend
+
+    if pageconfig.dclang in locals():
+        htmlpage += '    <meta name="DC.Language" content="' + pageconfig.dclang + metaend
+    elif settings.dclang in locals():
+        htmlpage += '    <meta name="DC.Language" content="' + settings.dclang + metaend
+    elif pageconfig.language in locals():
+        htmlpage += '    <meta name="DC.Language" content="' + pageconfig.language + metaend
+    elif settings.language in locals():
+        htmlpage += '    <meta name="DC.Language" content="' + settings.language + metaend
+    else:
+        pass
+
+    if pageconfig.dccoverage in locals():
+        htmlpage += '    <meta name="DC.Coverage" content="' + pageconfig.dccoverage + metaend
+    elif settings.dccoverage in locals():
+        htmlpage += '    <meta name="DC.Coverage" content="' + settings.dccoverage + metaend
+    else:
+        pass
+
+    if pageconfig.dcrights in locals():
+        htmlpage += '    <meta name="DC.Rights" content="' + pageconfig.dcrights + metaend
+    elif settings.dcrights in locals():
+        htmlpage += '    <meta name="DC.Rights" content="' + settings.dcrights + metaend
+    else:
+        pass
+
+    # only in start page of the whole site
+    if pageconfig.flattr in locals():
+        htmlpage += '    <meta name="verify-v1" content="' + flattr + metaend
+
+    if pageconfig.favicon in locals():
+        htmlpage += '    link rel="shortcut icon" href="' + pageconfig.favicon + 'type="image/x-icon' + metaend
+    elif settings.favicon in locals():
+        htmlpage += '    link rel="shortcut icon" href="' + settings.favicon + 'type="image/x-icon' + metaend
+    else:
+        pass
+
+    if pageconfig.stylesheet in locals():
+        htmlpage += '   <link rel="stylesheet" media="all" href="' + pageconfig.stylesheet + 'type="text/css' + metaend
+    elif settings.stylesheet in locals():
+        htmlpage += '   <link rel="stylesheet" media="all" href="' + settings.stylesheet + 'type="text/css' + metaend
+    else:
+        pass
+
+    # additional css files are only expected in single pages, not for the whole site
+    if pageconfig.addcss in locals():
+        addcss = pageconfig.addcss
+        for i in range(len(addcss)):
+            cssinfoparts = addcss[i].split(" ")
+            htmlpage += '    <link rel="stylesheet" media="' + cssinfoparts[1] + '"'
+            htmlpage += 'href="' + cssinfoparts[0] + 'type="text/css' + metaend
+
+    # same for additional entries in <head>
+    if pageconfig.addhead in locals():
+        htmlpage += pageconfig.addhead
+
+    htmlpage += '  </head>\n'
+
+    bodytopfile = themepath + "/bodytop.htm"
+    logofile = themepath + "/logo.htm"
+
+    if os.path.exists(bodytopfile):
+        bodytop = open(bodytopfile, "r")
+        bodytophtml = bodytop.read()
+        bodytop.close()
+        htmlpage += bodytophtml
+    else:
+        print("bodytop in theme " + theme + " does not exist, aborting.")
+        exit()
+
+
+    if os.path.exists(logofile):
+        logo = open(logofile, "r")
+        logohtml = logo.read()
+        logo.close()
+        htmlpage += logohtml
+
+    if settings.menupos == "above":
+        # like in below
+
+    contentfile = open(sourcehtml, "r")
+    content = contentfile.read()
+    contentfile.close()
+    htmlpage += content
+
+    if settings.menupos == "below":
+        if pageconfig.menuadd in locals():
+            menu = buildmenu(sourcepath, pageconfig.menuentry, pageconfig.menuadd, pageconfig.menuclass, settings.menuhome)
+        else:
+            menu = buildmenu(sourcepath, pageconfig.menuentry, "", pageconfig.menuclass, settings.menuhome)
+        htmlpage += menu
+
+    # footer        
+
+
+def buildmenu(sourcepath, menuadd, mymenuclass, menuhome):
+    # file/dir names have leading numbers to claim their position in the menu
+    # like 0-index.htm 1-vorstellung 2-blog (special case, external link)
+    # ... 15-impressum.htm
+    # read folders from own directory and their index.py
+    # get index.menuentry
+
+    menu = '      <div id="menu">\n'
+    menu += '        <ul>'
+    listend = '</a></li>\n'
+
+    # Startpage
+    if mymenuclass == "home":
+        menu += '<li class="home">' + menuentry + '</li>\n'
+    else:
+        menu += '<li class="home"><a href="/">' + menuhome + listend
+
+    allfiles = os.listdir(sourcepath)
+    cleanfiles = checkfiles(allfiles)
+    if cleanfolders != "":
+        cleanfolders = cleanfolders.sort()
+        for i in range(len(cleanfolders)):
+            if cleanfo
+            from cleanfolders[1] import index
+            menu += 
+            
+                
+
+    if menuadd in locals():
+        for i in range(len(menuadd)):
+            snippetpath = mypath + "/theme/" + theme
+            snippetfile = snippetpath + "/menu." + menuadd[i] + ".htm"
+            snippet = open(snippetfile, "r")
+            menusnippet = snippet.read()
+            snippet.close()
+            htmlpage += menusnippet + "\n"
+
+# End of functions
+
+
+# check arguments
+mypath = "/".join(sys.argv[0]("/")[:-1])
+
+if len(sys.argv) > 1:
+    arguments = {"theme" = "", "targetdir" = "", "targetfile" = ""}
+    for args in range(len(sys.argv)):
+        if sys.argv[args] == "-t" or sys.argv[args] == "--theme":
+            arguments["theme"] = sys.argv[args + 1]
+        elif sys.argv[args] == "-d" or sys.argv[args] == "--dir":
+            arguments["targetdir"] = sys.argv[args + 1]
+        elif sys.argv[args] == "-f" or sys.argv[args] == "--file":
+            arguments["targetfile"] = sys.argv[args + 1]
+else:
+    print("Cannot work without at least a given theme.")
+    exit()
+
+# ToDo instead: compare file timestamps of pages and html, build
+# new html if page is newer or html does not exist at all.
+# Needs no more arguments from the command line.
+# How to find out if theme has been changed (and everything must
+# be rebuilt)?
+
+if arguments("theme") == "":
+    print("Cannot work without at least a given theme.")
+    exit()
+else:
+    themepath = mypath + "/themes/" + arguments["theme"]
+    sys.path.insert(0, themepath)
+
+
+targetpath = mypath + "/html/" + arguments["theme"] 
+if not targetpath.endswith("/"):
+    targetpath += "/"
+
+if arguments["directory"] != "":
+    targetpath += arguments["directory"]
+    if os.path.exists(targetpath):
+        os.mkdir(targetpath)
+
+# In this version the first file with given name gets used!
+# If there is another file with same name in another directory, it will not be found.
+if arguments("targetfile") != "":
+    targetfile = arguments["targetfile"]
+    if not targetfile.endswith(".html"):
+        print("invalid target file")
+        exit()
+
+    if "/" in targetfile:
+        targetfileparts = targetfile.split("/")
+        targetpath = mypath
+        for i in range(len(targetfileparts) -1):
+            targetpath += targetfileparts[i]
+        targetfile = targetfileparts[len(targetfileparts)]
+    else:
+        searchfile = targetpath + targetfile
+        if not os.path.exists(searchfile):
+            allfolders = os.listdir(filepath)
+            cleanfolders = checkfolders(allfolders)
+            for curfold in range(len(cleanfolders)):
+                currentfolder = cleanfolders[curfold]
+                currentpath = filepath + currentfolder + "/"
+                filelist = filesoffolder(currentpath)
+                if targetfile in filelist:
+                    targetpath = currentpath
+                    break
diff --git a/preparesite.py b/preparesite.py
new file mode 100755 (executable)
index 0000000..15f88e0
--- /dev/null
@@ -0,0 +1,218 @@
+#!/usr/bin/env python3
+# -*- coding: utf8 -*-
+
+import sys, os
+from time import strftime, gmtime
+from datetime import datetime
+from subprocess import call
+
+mypath = sys.argv[0]
+theme = sys.argv[1]
+sys.path.insert(0, mypath)
+flt = __import__(theme)
+
+if not flt.origpath.startswith("/"):
+    print("This is no full path. Stop.")
+    exit()
+else:
+    origpath = flt.origpath
+    
+if not flt.fileext.startswith("."):
+    fileext = "." + fileext
+else:
+    fileext = flt.fileext
+
+if not flt.targetpath.endswith("/"):
+    targetpath = flt.targetpath + "/"
+else:
+    targetpath = flt.targetpath
+
+if not os.path.exists(targetpath):
+    os.mkdir(targetpath, "0755")
+    newdir = targetpath + "/CSS"
+    os.mkdir(newdir, "0755")
+    newdir = targetpath + "/DEFAULTS"
+    setfile = newdir + "/settings.py"
+    call(["/bin/touch", setfile])
+    call(["/bin/chmod", "755", setfile])
+    os.mkdir(newdir, "0755")
+    newdir = targetpath + "/SNIPPETS"
+    os.mkdir(newdir, "0755")
+    print("Please create at least DEFAULTS/settings.py.")
+    print("Without that file the site cannot be prepared for htmlglue.")
+    exit()
+
+settingpath = targetpath + "DEFAULTS/"
+sys.path.insert(1, settingpath)
+import settings
+
+
+if len(flt.ignorehtml) > 0:
+    for i in range(len(flt.ignorehtml)):
+        ignorehtmlparts = flt.ignoreparts[i].split("%%")
+        ignorehtml[ignorehtmlparts[0]] = ignorehtmlparts[1]
+
+for dirpath, dirs, files in os.walk(origpath):
+    for filename in files:
+        curfile = os.path.join(dirpath, filename)
+        if dirs not in flt.ignoredirs and curfile not in flt.ignorefiles and curfile.endswith(fileext):
+            htmlfile = open(curfile, "rt")
+            html = htmlfile.read()
+            htmlfile.close()
+            htmllines = html.split("\n")
+
+            targethtm = targetpath + dirpath + "/" + filename.replace(".html", ".htm")
+            targetpy = targetpath + dirpath + "/" + filename.replace(".html", ".py")
+
+            htm = open(targethtm, "w")
+            pyfile = open(targetpy, "w")
+
+            curtime = strftime("%Y-%m-%d %H:%M", datetime.now())
+            pyfile = open(targetpy, "w")
+            pyhead = "#!/usr/bin/env python3\n"
+            pyhead += "# -*- coding: utf8 -*-\n\n"
+            pyhead += "######################################################################\n"
+            pyhead += "# THIS FILE WAS GENERATED AUTMATICALLY FROM\n"
+            pyhead += "# " + curfile + "\n"
+            pyhead += "# BY preparesite.py FROM htmlglue WITH FILTER AND SETTINGS FROM\n"
+            pyhead += "# " + theme + ", " + curtime + "\n"
+            pyhead += "######################################################################\n\n\n"
+            pyfile.write(pyhead)
+
+            ln = "'\n"
+            addcssfile = []
+            ignorestop = ""
+            menuread = False
+            contentread = False
+
+            for i in range(len(htmllines)):
+                curline = htmllines[i]
+                # ignoring unwanted parts
+                if ignorestop != "":
+                    if ignorestop in curline:
+                        ignorestop = ""
+                    continue
+
+                for ign in ignorehtml:
+                    if ign in curline:
+                        ignorestop = ignorehtml[ign]
+
+                # special case: reading the menu from an index.html
+                if menuread:
+                    origmenu += curline + "\n"
+                    if flt.menuend in curline:
+                        menuread = False
+                    continue
+
+                if flt.menustart in curline:
+                    if curfile.endswith("index.html"):
+                        menuread = True
+                        origmenu = curline + "\n"
+                    else:
+                        ignorestop = flt.menuend
+                    continue
+
+                if contentread:
+                    for item in flt.replacement:
+                        if item in curline:
+                            curline = curline.replace(item, ftl.replacement[item])
+                    htm.write(curline + "\n")
+                    if flt.contentend in curline:
+                        contentread = False
+                        htm.close()
+                        call(["/bin/touch", targethtm, "-t", htmtouchtime])
+                    continue
+
+                if flt.contentstart in curline:
+                    contentread = True
+                    htm.write(curline + "\n")
+                    continue
+
+                if curline.startswith('<DOCTYPE'):
+                    if curline != settings.doctype:
+                        outline = "doctype = '" + curline + ln
+                        pyfile.write(outline)
+                elif curline.startswith('<html'):
+                    if curline != settings.htmlver:
+                        outline = "htmlver = '" + curline + ln
+                        pyfile.write(outline)
+                elif "<title>" in curline:
+                    lineparts = curline.split("<title>")
+                    title = lineparts[1].replace("</title>", "")
+                    title = title.strip()
+                    if title != settings.title:
+                        outline = "title = '" + title + ln
+                        pyfile.write(outline)
+                elif "<meta" in curline:
+                    curline = curline.replace(" = ", "=")
+                    lineparts = curline.split('content="')
+                    contentpart = contentpart.replace("/>", "")
+                    contentpart = contentpart.strip()
+                    if 'name="robots"' in curline:
+                        if contentpart != settings.robots:
+                            pyfile.write("robots = '" + contentpart + ln)
+                    elif 'http-equiv="content-language"' in curline or 'DC.Language' in curline:
+                        if contentpart != settings.language:
+                            pyfile.write("language = '" + contentpart + ln)
+                            # might write this line twice into the .py file if both exist!
+                    elif 'name="author"' in curline:
+                        if contentpart != settings.author:
+                            pyfile.write("author = '" + contentpart + ln)
+                    elif 'name="keywords"' in curline:
+                        if contentpart != settings.metakeywords:
+                            pyfile.write("metakeywords = '" + contentpart + ln)
+                    elif "DC.Title" in curline:
+                        if contentpart != settings.dctitle:
+                            pyfile.write("dctitle = '" + contentpart + ln)
+                    elif "DC.Creator" in curline:
+                        if contentpart != settings.dcreator:
+                            pyfile.write("dcreator = '" + contentpart + ln)
+                    elif "DC.Publisher" in curline:
+                        if contentpart != settings.dcpublisher:
+                            pyfile.write("dcpublisher = '" + contentpart + ln)
+                    elif "DC.Coverage" in curline:
+                        if contentpart != settings.dccoverage:
+                            pyfile.write("dccoverage = '" + contentpart + ln)
+                    elif "DC.Rights" in curline:
+                        if contentpart != settings.dcrights:
+                            pyfile.write("dcrights = '" + contentpart + ln)
+                    elif "DC.Date" in curline:
+                        pyfile.write("dcdate = '" + contentpart + ln)
+                        filetime = gmtime(os.path.getmtime(curfile))
+                        fileday = strftime('%Y-%m-%d', filetime)
+                        # this is needed later for touching the .htm file
+                        # after it was closed
+                        if contentpart == fileday:
+                            htmtouchtime = strftime("%Y%m%d%H%M.%S", filetime)
+                        else:
+                            htmtouchtime = dcdate.replace("-", "") + "000000"
+                elif '<link rel=' in curline:
+                    lineparts = curline.split('href="')
+                    contentpart = lineparts[0].split('"', 1)[0]
+                    contentpart = contentpart.replace("/", "")
+                    if "favicon" in curline:
+                        if contentpart != settings.favicon:
+                            pyfile.write("favicon = '" + contentpart + ln)
+                    elif "stylesheet" in curline:
+                        if contentpart != settings.stylesheet:
+                            media = curline.split('media="')[0]
+                            media = media.split('"', 1)[0]
+                            addcss = contentpart + " " + media
+                            addcssfile += addcss
+                elif flt.headlinekey in curline:
+                    headline = curline.split(flt.headlinekey)[1]
+                    headlineend = flt.headlinekey.replace("<h", "</h")
+                    headline = headline.split(headlineend)[0]
+                    headline = headline.strip()
+                    pyfile.write("headline = '" + headline + ln)
+
+            pyfile.write("addcssfile = " + addcssfile)
+            pyfile.close()
+
+            if curfile.endswith("index.html"):
+                # build menu.py using origmenu and os.dirlist(dir)
+                pass
+
+
+# with dirlisting of current dir (file/dirnames) and the menu structure of
+# the index.html of current dir build menu.py for current output path
diff --git a/sites/atarifrosch/CSS/akv.css b/sites/atarifrosch/CSS/akv.css
new file mode 100644 (file)
index 0000000..eb8610f
--- /dev/null
@@ -0,0 +1,45 @@
+div#akct {
+  position            : absolute; top:0px; right: 0px; z-index: 2342; width:113px; height:88px;
+  background-image    : url(http://wiki.vorratsdatenspeicherung.de/images/Akvse.gif);
+  background-repeat   : no-repeat;
+  background-position : right top;
+  border              : none;
+  padding             : 0;
+  margin              : 0;
+  text-align          : right;
+}
+
+div#akct img {
+  border              : none;
+  padding             : 0;
+  margin              : 0;
+  background          : none;
+}
+
+div#akct a#akpeel img {
+  width               : 113px;
+  height              : 88px;
+}
+
+div#akct a, div#akct a:hover {
+  text-decoration     : none;
+  border              : none;
+  padding             : 0;
+  margin              : 0;
+  display             : block;
+  background          : none;
+}
+
+div#akct a#akpeel:hover {
+  position            : absolute; top:0px; right: 0px; z-index: 4223; width:500px; height:500px;
+  display             : block;
+  background-image    : url(http://wiki.vorratsdatenspeicherung.de/images/Akvsb.gif);
+  background-repeat   : no-repeat;
+  background-position : right top;
+}
+
+div#akct a#akpreload {
+  background-image    : url(http://wiki.vorratsdatenspeicherung.de/images/Akvsb.gif);
+  background-repeat   : no-repeat;
+  background-position : 234px 0px;
+}
diff --git a/sites/atarifrosch/CSS/brief.css b/sites/atarifrosch/CSS/brief.css
new file mode 100644 (file)
index 0000000..3815801
--- /dev/null
@@ -0,0 +1,10 @@
+div#content {
+  background-color : #ffffff;
+  color            : #000000;
+  border           : 1px #000000 solid;
+  margin           : .5em 1.5em -0.8em 17em;
+}
+
+#content p {
+  text-indent      : 0;
+}
diff --git a/sites/atarifrosch/CSS/frosch.css b/sites/atarifrosch/CSS/frosch.css
new file mode 100644 (file)
index 0000000..ad27f41
--- /dev/null
@@ -0,0 +1,444 @@
+html, body {
+  width            : 100%;
+}
+
+#canvas {
+  overflow         : auto;
+}
+
+body {
+  color            : #000000;
+  background-color : #88bb99;
+  font-family      : sans-serif;
+}
+
+body, #canvas, #kopf, #kopf h1, #menu {
+  margin           : 0;
+  padding          : 0;
+}
+
+
+div#kopf {
+  background-color : inherit;
+  color            : #ffffff;
+  min-height       : 170px;
+  line-height      : 170px;
+  margin           : 0;
+  padding          : .2em 0 .5em 0;
+}
+
+#kopf h1 {
+  text-align       : center;
+  vertical-align   : middle;
+  font-family      : ITC Zapf Chancery, URW Chancery, Apple Chancery, cursive, sans-serif;
+  font-size        : 250%;
+}
+
+#kopf div#logo {
+  margin           : 5px 0 0 20px;
+  padding          : 10px;
+  border           : 0;
+  border-radius    : 90px;
+  width            : 140px;
+  height           : 140px;
+  float            : left;
+  background-color : #ffffff;
+  color            : #ffffff;
+}
+
+
+div#menu {
+  float            : left;
+  width            : 16em;
+  margin           : 0 0 0 0;
+  background-color : #ccffcc;
+  color            : #225522;
+}
+
+#menu li {
+  font-weight      : bolder;
+  text-align       : left;
+  background-color : inherit;
+  color            : #2266aa;
+}
+
+#menu li.home {
+  margin           : 0 1em 0 0;
+  padding          : 0;
+  list-style-image : url(/bilder/datei.png)
+}
+
+#menu li.file {
+  margin           : 0 1em 0 0;
+  text-align       : left;
+  list-style-image : url(/bilder/datei.png)
+}
+
+#menu li.file1 {
+  margin           : 0 1em 0 1em;
+  list-style-image : url(/bilder/datei.png)
+}
+
+#menu li.file2 {
+  margin           : 0 1em 0 2em;
+  list-style-image : url(/bilder/datei.png)
+}
+
+#menu li.file3 {
+  margin           : 0 1em 0 3em;
+  list-style-image : url(/bilder/datei.png)
+}
+
+#menu li.close {
+  margin           : 0 1em 0 0;
+  list-style-image : url(/bilder/ordnerzu.png)
+}
+
+#menu li.close1 {
+  margin           : 0 0 0 1em;
+  list-style-image : url(/bilder/ordnerzu.png)
+}
+
+#menu li.close2 {
+  margin           : 0 0 0 2em;
+  list-style-image : url(/bilder/ordnerzu.png)
+}
+
+#menu li.open {
+  list-style-type  : none;
+  margin           : 0;
+  list-style-image : url(/bilder/ordnerauf.png)
+}
+
+#menu li.open1 {
+  list-style-type  : none;
+  margin           : 0 0 0 1em;
+  list-style-image : url(/bilder/ordnerauf.png)
+}
+
+#menu li.open2 {
+  margin           : 0 0 0 2em;
+  list-style-image : url(/bilder/ordnerauf.png)
+}
+
+#menu hr {
+  border           : 1px #88bb99 solid;
+  margin           : .5em 0 .5em 0;
+}
+
+#menu a:link, #menu a:visited {
+  color            : #225522;
+  background-color : inherit;
+  text-decoration  : none;
+}
+
+#menu a:hover, #menu a:active {
+  color            : #ccffcc;
+  background-color : #225522;
+}
+
+#menu a.graflink, a.graflink:hover {
+  border           : 0;
+  color            : #eeeeee;
+  background-color : transparent;
+}
+
+#menu p.center {
+  width            : 13.5em;
+  margin           : 0 1em .5em 1em;
+  text-align       : center;
+  text-indent      : 0;
+}
+
+
+div#content {
+  background-color : #ccffcc;
+  color            : #000000;
+  margin           : 0 2em 0 17em;
+  padding          : .5em 1em .5em 2em;
+}
+
+#content h2,#content h3,#content h4 {
+  padding          : .2em 0 0 0;
+  background-color : inherit;
+  color            : #2266aa;
+  text-align       : center;
+}
+
+#content p {
+  text-indent      : 3em;
+  text-align       : justify;
+}
+
+#content p.back {
+  text-align       : right;
+  text-indent      : 0;
+  font-size        : 80%;
+}
+
+#content p.small {
+  font-size        : 80%;
+}
+
+#content p.center {
+  font-style       : italic;
+  text-align       : center;
+  margin           : 0 3em 0 3em;
+  text-indent      : 0;
+}
+
+#content p.doai {
+  font-family      : Times, serif;
+  font-style       : italic;
+  text-align       : left;
+  text-indent      : 0;
+}  
+
+#content p.list {
+  text-align       : justify;
+  text-indent      : 0;
+}
+
+#content p.noindent {
+  text-indent      : 0;
+}
+
+#content p.cite {
+  margin           : 0 3em 0 3em;
+  font-family      : Times, serif;
+  font-style       : italic;
+  text-indent      : 0;
+}
+
+#content p.break {
+  clear            : both;
+}
+
+#content li {
+  margin           : 0;
+  padding          : .1em 0 0 0;
+  text-align       : justify;
+}
+
+#content li.text {
+  padding-top      : 0.5em;
+}  
+
+#content li.frosch {
+  list-style-image : url("/bilder/but_green.jpg");
+}
+
+#content li.comp {
+  list-style-image : url("/bilder/minicomp.png");
+  margin           : .7em 0 0 0;
+}
+
+#content .invers {
+  padding-top      : 0;
+  background-color : #000000;
+  color            : #ccffcc;
+  text-align       : center;
+}
+
+#content hr {
+  border           : 2px #2266aa solid;
+}  
+
+#content table {
+  margin           : 0;  
+  border           : 1px #000000 solid;
+  padding          : 0;
+}
+
+#content table.left {
+  float            : left;
+  margin           : 0 1em 0 0;
+}
+
+#content tr {
+  margin           : 0;
+  padding          : 0;
+}
+
+#content th {
+  border-right     : 1px #000000 solid;
+  border-bottom    : 1px #000000 solid;
+  margin           : 0;
+  padding          : 0 .2em 0 .2em;
+  font-weight      : bold;
+  text-align       : center;
+  vertical-align   : middle;
+}
+
+#content td {
+  border-right     : 1px #000000 solid;
+  border-bottom    : 1px #000000 solid;
+  margin           : 0;
+  padding          : 0 .2em 0 .2em;
+  vertical-align   : top;
+}
+
+#content td.greycol {
+  background-color : #cccccc;
+  color            : #000000;
+}
+
+#content td.right {
+  text-align       : right;
+}
+
+#content td p {
+  vertical-align   : top;
+  margin           : 0;
+  text-indent      : 0;
+}  
+
+#content td.sub {
+  font-size        : 80%;
+  letter-spacing   : -.1em;
+}
+
+#content div.picleft {
+  margin           : 0;
+  padding          : 0 2em 0 0;
+  float            : left;
+}
+
+#content div.picright {
+  margin           : 0;
+  padding          : 0 0 0 2em;
+  float            : right;
+}
+
+#content div.console {
+  padding          : .5em 0 .5em 0;
+  background-color : #000000;
+  color            : #00ff00;
+  text-indent      : 0;
+}
+
+#content .console p {
+  text-align       : left;
+  text-indent      : 0;
+  margin           : 0;
+  padding          : 0;
+  font-family      : Courier, monospace;
+  font-weight      : bold;
+}
+
+#content pre {
+  color            : #000000;
+  background-color : inherit;
+  font-family      : Lucida Console, Courier New, Courier, monospace;
+}
+
+#content img.links {
+  padding-right    : 1em;
+}
+
+#content .write {
+  font-family      : Times, serif;
+  font-style       : italic;
+}
+
+#content .unprop {
+  font-family      : Lucida Console, Courier New, Courier, monospace;
+  font-style       : normal;
+  font-weight      : bold;
+}
+
+#content span.strike {
+  font-style       : normal;
+  text-decoration  : line-through;
+}
+
+#content dt {
+  padding          : 1em 0 .5em 0;
+  font-weight      : bold;
+  font-style       : italic;
+}
+
+#content dd {
+  padding-top      : 0.5em;
+  text-align       : justify;
+}
+
+#content blockquote {
+  margin           : 1em .5em 1em .5em;
+  padding          : .2em .5em .2em .5em;
+  text-align       : justify;
+  background-color : #ffffff;
+}
+
+#content blockquote.tweet {
+  width            : 545px;
+  background-color : #ffffff;
+  color            : #000000;
+  border           : 1px #000000 solid;
+}
+
+#content blockquote.tweet img {
+  float            : left;
+  margin           : 0 1em .5em 0;
+  border           : 0;  
+}
+
+#content blockquote.tweet div.tweetcontent {
+  margin           : 0 .5em 0 60px;
+}
+
+#content blockquote.tweet p {
+  margin           : .2em 0 0 0;
+  text-align       : left;
+  text-indent      : 0;
+}
+
+#content blockquote.tweet a.permlink {
+  background-color : inherit;
+  color            : #666666;
+  text-decoration  : none;
+  font-size        : 80%;
+  font-style       : italic;
+}
+
+#content a:link, #content a:visited {
+  color            : #225522;
+  background-color : #ccffcc;
+  text-decoration  : none;
+}
+
+#content a:hover,#content a:active {
+  color            : #ccffcc;
+  background-color : #225522;
+}
+
+#content a.graflink,#content a.graflink:hover {
+  border           : 0;
+  color            : #ffffff;
+  background-color : transparent;
+}
+
+
+div#footer {
+  clear            : both;
+  margin           : 0;
+  background-color : #2266aa;
+  color            : #ffffff;
+  padding          : .2em .2em .5em .5em;
+  margin           : 0 0 1em 0;
+}
+
+#footer p {
+  margin           : .2em 0 0 0;
+  padding          : 0;
+  text-align       : left;
+  font-size        : 90%;
+  text-indent      : 0;
+}
+
+#footer a.graflink,#footer a.graflink:hover {
+  border           : 0;
+  background-color : transparent;
+  margin           : .2em 0 .2em 0;
+  float            : right;
+}
diff --git a/sites/atarifrosch/DEFAULTS/__init__.py b/sites/atarifrosch/DEFAULTS/__init__.py
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/sites/atarifrosch/DEFAULTS/settings.py b/sites/atarifrosch/DEFAULTS/settings.py
new file mode 100755 (executable)
index 0000000..0231d71
--- /dev/null
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+# -*- coding: utf8 -*-
+
+theme = atarifrosch
+
+doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
+htmlver = '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">'
+robots = 'index,follow'
+# language will also be used for DC.Language
+language = 'de'
+author = "Atari-Frosch"
+
+dctitle = "Atari-Frosch"
+dccreator = 'Atari-Frosch'
+dcpublisher = 'Atari-Frosch'
+dcrights = "Sabine 'Atari-Frosch' Becker"
+
+favicon = 'favicon.ico'
+stylesheet = 'frosch.css'
+
+proto = "https"
+domain = "www.atari-frosch.de"
+
+# position of menu: "above" or "below" content
+menupos = "above"
+
+# This can be set to False if no Footer is wanted in general or
+# for a single page
+footer = True
+
+# dictionary of variables within content files which need to be replaced
+# can be extended in $file.py files
+
+replacement["[FLATTRBUTTON]"] = "flattrbutton.htm"
+replacement["[FLATTRSCRIPT]"] = "flattrscript.htm"
diff --git a/sites/atarifrosch/SNIPPETS/akvsbody.htm b/sites/atarifrosch/SNIPPETS/akvsbody.htm
new file mode 100644 (file)
index 0000000..539fa55
--- /dev/null
@@ -0,0 +1,12 @@
+        <!-- AKVS body start v1.6 -->
+        <div id="akct"><a id="akpeel" href="http://www.vorratsdatenspeicherung.de/aktuell"
+             title="Stoppt die Vorratsdatenspeicherung! Jetzt klicken &amp; handeln!"><img
+             src="http://wiki.vorratsdatenspeicherung.de/images/Akvst.gif"
+             alt="Stoppt die Vorratsdatenspeicherung! Jetzt klicken &amp; handeln!" /></a>
+         <a id="akpreload" href="http://wiki.vorratsdatenspeicherung.de/?title=Online-Demo"
+             title="Willst du auch an der Aktion teilnehmen? Hier findest du alle
+                    relevanten Infos und Materialien:"><img
+             src="http://wiki.vorratsdatenspeicherung.de/images/Akvsi.gif"
+             alt="Willst du auch an der Aktion teilnehmen? Hier findest du alle
+                  relevanten Infos und Materialien." /></a></div>
+        <!-- AKVS body end -->'''
diff --git a/sites/atarifrosch/SNIPPETS/akvstyle.htm b/sites/atarifrosch/SNIPPETS/akvstyle.htm
new file mode 100644 (file)
index 0000000..f0fd33d
--- /dev/null
@@ -0,0 +1,9 @@
+    <link rel="stylesheet" media="all" href="/akv.css" type="text/css" />
+    <!--[if gte IE 5.5]>
+    <![if lt IE 7]>
+      <style type="text/css">
+          div#akct a#akpeel:hover {
+            right: -1px; }
+      </style>
+    <![endif]>
+    <![endif]-->
diff --git a/sites/atarifrosch/SNIPPETS/bodytop.htm b/sites/atarifrosch/SNIPPETS/bodytop.htm
new file mode 100644 (file)
index 0000000..d2a28ed
--- /dev/null
@@ -0,0 +1,3 @@
+  <body>
+    <div id="canvas">
+      <div id="kopf">
diff --git a/sites/atarifrosch/SNIPPETS/endoffile.htm b/sites/atarifrosch/SNIPPETS/endoffile.htm
new file mode 100644 (file)
index 0000000..2a239ae
--- /dev/null
@@ -0,0 +1,3 @@
+    </div><!-- div#canvas -->
+  </body>
+</html>
diff --git a/sites/atarifrosch/SNIPPETS/flattrbutton.htm b/sites/atarifrosch/SNIPPETS/flattrbutton.htm
new file mode 100644 (file)
index 0000000..23b2d0a
--- /dev/null
@@ -0,0 +1,9 @@
+        <div class="picright">
+          <a class="FlattrButton" style="display:none;"
+             href="[MYURL]"></a>
+          <noscript>
+            <a class="graflink" href="https://flattr.com/thing/[THING]/[THINGNAME]">
+            <img src="http://api.flattr.com/button/flattr-badge-large.png"
+                 alt="Flattr this" title="Flattr this" border="0"/></a>
+          </noscript>
+        </div>
diff --git a/sites/atarifrosch/SNIPPETS/flattrscript.htm b/sites/atarifrosch/SNIPPETS/flattrscript.htm
new file mode 100644 (file)
index 0000000..2b1e093
--- /dev/null
@@ -0,0 +1,12 @@
+    <script type="text/javascript">
+     /* <![CDATA[ */
+       (function() {
+         var s = document.createElement('script'), t =
+          document.getElementsByTagName('script')[0];
+          s.type = 'text/javascript';
+          s.async = true;
+          s.src = 'http://api.flattr.com/js/0.6/load.js?mode=auto';
+          t.parentNode.insertBefore(s, t);
+       })();
+     /* ]]> */
+    </script>
diff --git a/sites/atarifrosch/SNIPPETS/footer.htm b/sites/atarifrosch/SNIPPETS/footer.htm
new file mode 100644 (file)
index 0000000..4690e89
--- /dev/null
@@ -0,0 +1,8 @@
+      <div id="footer">
+        <a class="graflink" href="http://validator.w3.org/check?uri=referer"> 
+          <img src="/bilder/valid-xhtml10.png" width="88" height="31"
+               alt="W3C – valid XHTML 1.0" /></a>
+        <p>URL: [MYURL]</p>
+        <p>last content modification: [FILETIME]</p>
+        <p>last file modification: [CURTIME]</p>
+      </div><!-- div#footer -->
diff --git a/sites/atarifrosch/SNIPPETS/head.htm b/sites/atarifrosch/SNIPPETS/head.htm
new file mode 100644 (file)
index 0000000..fc5b665
--- /dev/null
@@ -0,0 +1 @@
+  <head>
\ No newline at end of file
diff --git a/sites/atarifrosch/SNIPPETS/logo.htm b/sites/atarifrosch/SNIPPETS/logo.htm
new file mode 100644 (file)
index 0000000..85b2cc5
--- /dev/null
@@ -0,0 +1,4 @@
+        <div id="logo">
+          <a class="graflink" href="/">
+            <img src="/bilder/atarifrosch.png" width="135" height="114" alt="" /></a>
+        </div><!--div#logo-->
diff --git a/sites/atarifrosch/SNIPPETS/menu.cacert.htm b/sites/atarifrosch/SNIPPETS/menu.cacert.htm
new file mode 100644 (file)
index 0000000..70970f8
--- /dev/null
@@ -0,0 +1,7 @@
+        <p class="center">
+          <a class="graflink" href="http://www.cacert.org/">
+            <img src="/bilder/cacert-100.png" width="100" height="23"
+                 alt="CaCert" /></a><br />
+            Wer im Raum Düsseldorf eine CaCert Assurance benötigt,
+            kann sich gern bei <a href="/impressum.html">mir melden</a>.
+       </p><hr />
diff --git a/sites/atarifrosch/SNIPPETS/menu.geocaching.htm b/sites/atarifrosch/SNIPPETS/menu.geocaching.htm
new file mode 100644 (file)
index 0000000..820084a
--- /dev/null
@@ -0,0 +1,5 @@
+        <p class="center">
+          <a class="graflink"
+             href="http://www.geocaching.com/profile/?guid=6a47d0cc-706d-4d48-8db0-d6af51d43344">
+            <img src="http://img.geocaching.com/stats/img.aspx?txt=View+my+profile&amp;uid=6a47d0cc-706d-4d48-8db0-d6af51d43344" width="200" height="50"
+                 alt="Geocaching.com Profil" /></a></p>
diff --git a/sites/atarifrosch/SNIPPETS/meta.flattrverify.htm b/sites/atarifrosch/SNIPPETS/meta.flattrverify.htm
new file mode 100644 (file)
index 0000000..721bba2
--- /dev/null
@@ -0,0 +1,2 @@
+    <meta name="verify-v1" content="iE1JAwy4KmLfpfiA7Fyyj5ddtgTnhKjWvnbgj6aUsmo=" />
+
diff --git a/sites/atarifrosch/__init__.py b/sites/atarifrosch/__init__.py
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/sites/atarifrosch/impressum.htm b/sites/atarifrosch/impressum.htm
new file mode 100644 (file)
index 0000000..ede3b46
--- /dev/null
@@ -0,0 +1,27 @@
+      <div id="content">
+        <dl><dt> Postanschrift </dt>
+          <dd> Sabine Becker <br /> Helmholtzstraße&nbsp;53 <br />
+               40215&nbsp;Düsseldorf </dd>
+          <dt> Telefon </dt>
+          <dd> 0211/3368730 </dd>
+          <dt> E-Mail </dt>
+          <dd> Privat: <a href="mailto:frosch@atari-frosch.de">frosch@atari-frosch.de</a><br />
+            <a href="/public-key.asc">PGP-Key</a> ID: 0xBFC81ADE <br />
+               Fingerprint: 88AA 2116 E284 DF71 4310 8C87 D0CF 3AA9 BFC8 1ADE</dd>
+          <dd> Technisches zur Website: <a href="mailto:webmaster@atari-frosch.de">webmaster@atari-frosch.de</a></dd>
+          <dt> Instant Messaging </dt>
+          <dd> Jabber: atarifrosch@jabber.ccc.de oder froschdus@jabber.hot-chilli.net
+            <br />Yahoo: froschdus </dd>
+          <dt> Social Media </dt>
+          <dd> Twitter: <a href="https://twitter.com/AtariFrosch">@AtariFrosch</a><br />
+               GNUsocial: <a href="https://social.stopwatchingus-heidelberg.de/atarifrosch/">@atarifrosch@social.stopwatchingus-heidelberg.de</a><br />
+               Google+: <a href="https://plus.google.com/100090088462847995035/posts">+Sabine „Atari-Frosch“ Engelhardt</a></dd></dl>
+        <p>Diese Seiten wurden erstellt mit dem Programm htmlglue und dem
+           Texteditor joe auf <a href="http://www.debian.org/">Debian GNU/Linux</a>
+           unter Zuhilfenahme von <a href="http://selfhtml.teamone.de/">SelfHTML</a>
+           und (stichprobenartig) überprüft mit dem
+          <a href="http://validator.w3.org/">W3-Validator</a>.
+            Sie benötigen keinen bestimmten Browser, keine bestimmte
+            Bildschirmauflösung, keine Plugins und keine besondere Hardware.</p>
+        <p>Das Atari-Frosch-Logo erstellte Marius Sommer.</p>
+      </div><!-- div#content -->
diff --git a/sites/atarifrosch/impressum.py b/sites/atarifrosch/impressum.py
new file mode 100755 (executable)
index 0000000..38792ea
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/env python3
+# -*- coding: utf8 -*-
+
+targetfile = "/impressum.html"
+title = "atari-frosch.de: Impressum"
+description = "Impressum für atari-frosch.de"
+dctitle = "Impressum für atari-frosch.de"
+dcsubject = dctitle 
+
+addhead  = '    <style type="text/css"> tr {border: none;}\n'
+addhead += '      td {border: none; margin: .2em;} td p {margin-top: 1em;}\n'
+addhead += '    </style>\n'
+
+headline = "Impressum"
+
+contentfile = "impressum.htm"
diff --git a/sites/atarifrosch/index.htm b/sites/atarifrosch/index.htm
new file mode 100644 (file)
index 0000000..bb83bcd
--- /dev/null
@@ -0,0 +1,10 @@
+      <div id="content">
+        <h4>Die meisten Leute nennen mich Frosch, und das hier ist meine
+             „Houmpäidsch“. Also immer herein in die gute Stube!</h4><hr />
+
+        <p>Hier sind gerade Dinge im Umbau. Deshalb können Seiten auch mal
+           ein wenig seltsam aussehen. Wenn irgendwo wirklich was fehlt,
+           was eigentlich da sein sollte, sagt mir bitte
+         <a href="mailto:webmaster@atari-frosch.de">Bescheid</a>.</p>
+
+      </div><!-- div#content -->
diff --git a/sites/atarifrosch/index.py b/sites/atarifrosch/index.py
new file mode 100755 (executable)
index 0000000..b171a3d
--- /dev/null
@@ -0,0 +1,13 @@
+targetfile = "/index.html"
+title = "Willkommen auf Froschs Homepage!"
+description = "Website von Atari-Frosch"
+
+addhead = "SNIPPETS/akvstyle.htm"
+
+replacement["[AKVSBODY]"] = "akvsbody.htm"
+replacement["[GEOCACHING]"] = "menu.geocaching.htm"
+replacement["[CACERT]"] = "menu.cacert.htm"
+
+headline = "Qua®k!"
+
+contentfile = "index.htm"
diff --git a/sites/atarifrosch/internet/__init__.py b/sites/atarifrosch/internet/__init__.py
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/sites/atarifrosch/internet/menu.py b/sites/atarifrosch/internet/menu.py
new file mode 100755 (executable)
index 0000000..f21965e
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+# -*- coding: utf8 -*-
+
+menuitem[0]  = "htm, file, index.html, Startseite"
+menuitem[1]  = "dir, close, vorstellung, Vorstellung"
+menuitem[2]  = "dir, close, https://blog.atari-frosch.de/, Blog"
+menuitem[3]  = "dir, close, computer, Computer"
+menuitem[4]  = "dir, open, internet, Internet"
+menuitem[5]  = "htm, file1, iptables, IPtables"
+menuitem[6]  = "htm, file1, spammail, Spammail-Stats"
+menuitem[7]  = "htm, file1, internetsperren, Internetsperren"
+menuitem[8]  = "htm, file1, pgp, PGP"
+menuitem[9]  = "htm, file1, stream-mit-linux, Internet-Radio"
+menuitem[10] = "htm, file1, usenet, Usenet"
+menuitem[11] = "htm, file1, zcheaderfaq, ZC-Header-FAQ"
+menuitem[12] = "htm, file1, netzserie, Netz-Serie"
+menuitem[13] = "htm, file1, webseiten, Webseiten"
+menuitem[14] = "htm, file1, html-richtig, Richtiges HTML"
+menuitem[15] = "htm, file1, dsl, DSL-Erlebnis"
+menuitem[16] = "htm, file1, telekom-post, Anschluß-Sperre"
+menuitem[17] = "htm, file1, weihnachtsmail, Weihnachtsmails"
+menuitem[18] = "htm, file1, dmmv, DMMV und Spam"
+menuitem[19] = "htm, file1, hommingberger, SEO Gepardenforelle"
+menuitem[20] = "dir, close, politik, Politik"
+menuitem[21] = "dir, close, depression, Depression"
+menuitem[22] = "dir, close, hobby, Hobbies"
+menuitem[23] = "dir, close, musik, Musik"
+menuitem[24] = "dir, close, fotografie, Fotografie"
+menuitem[25] = "dir, close, pretender, Pretender"
+menuitem[26] = "dir, close, flohmarkt, Flohmarkt"
+menuitem[27] = "dir, close, misc, Vermischtes"
+menuitem[28] = "htm, file, impressum.html, Impressum"
diff --git a/sites/atarifrosch/internet/stream-mit-linux.htm b/sites/atarifrosch/internet/stream-mit-linux.htm
new file mode 100644 (file)
index 0000000..808d3d5
--- /dev/null
@@ -0,0 +1,395 @@
+      <div id="content">
+        <p>Auf dieser Seite möchte ich darüber berichten, wie ich ein
+            Internet-Radio ausschließlich mit Linux-Rechnern betreibe. Der
+            Aufbau ist etwas kompliziert; ich hatte einiges zu lesen, zu
+            basteln und zu suchen, bis alles funktionierte. Hier zeige ich,
+            was ich herausgefunden habe, und vor allem auch, mit welchen
+            Tücken ich zu kämpfen hatte.</p>
+        <p>Die Tücken entstanden vor allem deshalb, weil ich nicht einfach
+            Musik irgendwohin streamen, sondern „richtiges“ Radio
+            mit Moderation machen möchte. Wenn man einfach nur eine Playlist
+            laufen lassen will, die im LAN oder im Internet empfangen werden
+            kann, ist alles viel einfacher.</p>
+        <blockquote>
+          <strong> Update 2011-12-29</strong> Komplette Überarbeitung:
+            Anweisungen für Debian sarge, etch und lenny raus, alles auf
+            Debian squeeze abgestimmt, und alles gelöscht, was mit jackd zu
+            tun hat, weil der nun wirklich nicht mehr gebraucht wird.
+        </blockquote>
+        <h2>Streaming-Vorgang</h2>
+        <p>Um die folgenden Erklärungen besser zu verstehen, ist es
+            sinnvoll, ein bißchen was darüber zu wissen, was beim Streamen
+            eigentlich so alles passiert.</p>
+        <p>Klangdaten (Sprache, Musik) aus verschiedenen Quellen (zum
+            Beispiel Musikdateien, CD, Mikrofon) werden, teils mit Hilfe
+            entsprechender Software, von der Soundkarte wiedergegeben. 
+            Währenddessen werden sie von einer weiteren Software sofort
+            wieder als Rohdaten abgegriffen und in ein einheitliches Format
+            gebracht („encoded“; das kann MP3 sein, aber auch
+            OggVorbis, AAC oder FLAC).  Daraus wird ein Datenstrom erzeugt,
+            der an einen Server geschickt wird.  Dieser Server fungiert als
+            Verteiler; von dort aus können Hörer sich mit einem Player
+            einklinken und den erzeugten Datenstrom (engl: data stream oder
+            einfach stream) empfangen.  Für den Player sieht das dann so
+            aus, als ob er eine einzige Musikdatei abspielt, deren Größe er
+            nicht kennt. </p>
+        <h2>Hardware</h2>
+        <h3>Stream-PC</h3>
+        <p>Beim streamenden Rechner sind zwei Komponenten entscheidend: CPU
+            und Soundkarte. Bei aktuelleren Rechnern ist das meist kein
+            Thema; wer aber wie ich auf überwiegend ältere Hardware
+            zurückgreifen muss, könnte hier schnell an Grenzen stoßen.</p>
+        <h4>CPU</h4>
+        <p>Der Prozessor des streamenden Rechners sollte nicht weniger als
+            500&nbsp;MHz haben. Der Grund ist das rechenaufwendige Encoden
+            der Klangdaten von der Soundkarte in das Zielformat des Streams. 
+            Mit seinen 500&nbsp;MHz hatte mein
+          <a href="/computer/pc/seerose-piii500.html">PIII</a> schon teils
+            heftig zu kämpfen; die CPU-Auslastung lag zwischen 80 und
+            90&nbsp;%. Mit meinem
+          <a href="/computer/pc/seerose-athlon.html">Athlon XP1800+</a>
+            komme ich immer noch auf 30 - 35&nbsp;% Auslastung.</p>
+        <p>Höhere Taktraten sind insbesondere dann angeraten, wenn</p>
+        <ul>
+          <li>die abgespielten Musikdateien variable Bitraten haben;</li>
+          <li class="text"> nicht MP3, sondern OggVorbis erzeugt werden
+               soll. Der Rechenaufwand ist dafür erheblich höher als für MP3
+               (den für AAC kenne ich nicht, habe ich nicht getestet).</li>
+          <li class="text"> bereits das Abspielen eines Soundformats
+               rechenaufwendig ist. So lastet eine .sid-Datei (Klangdatei
+               vom C64) die CPU selbst schon ordentlich aus; ich habe es gar
+               nicht erst versucht, mit dem PIII eine solche Datei auf
+               meinen MP3-Stream zu bringen.</li></ul>
+        <p>Was passiert, wenn die CPU nicht leistungsfähig genug ist? Nun,
+            dann können die Ausgangsdaten nicht schnell genug encoded
+            werden, und es entstehen „buffer underruns“. Das
+            heißt, der Server bekommt weniger Daten, als er senden will. Das
+            kann soweit gehen, daß die Player der Hörer ins timeout laufen
+            und vom Stream getrennt werden.</p>
+        <p>Wenn kein schnellerer Rechner zur Verfügung steht, kann man wohl
+            dadurch Abhilfe schaffen, daß man zwei PCs miteinander verbindet
+            und die Aufgaben verteilt. PC&nbsp;#1 spielt dann die Daten auf
+            seiner Soundkarte ab. Ein Kabel verbindet Line-Out dieser
+            Soundkarte mit Line-In der Soundkarte in PC&nbsp;#2. Dieser
+            kümmert sich um das Encoden und sendet an den Server. Auf diese
+            Weise sollen pro PC 350 bis 400&nbsp;MHz genügen; ich habe das
+            allerdings selbst noch nicht ausprobiert.</p>
+        <h4>Soundkarte</h4>
+        <p>Nicht jede Soundkarte ist auch zum Streamen geeignet! Bei mir
+            funktionieren folgende Karten:</p>
+        <ul><li>Creative SoundBlaster mit Chip Emu10K1</li>
+          <li>C-Media CMI87 (8738)</li></ul>
+        <p>Ich hatte es bei dem PIII tagelang mit der onboard-Soundkarte,
+            einer ESS Solo1 1938, probiert, um dann bei einer
+            Google-Recherche zu erfahren, daß das alles keinen Zweck hat.</p>
+        <p>Eine Soundkarte zum Streamen muß nämlich unbedingt Vollduplex
+            können. Der Grund ist wieder das Abgreifen der Klangdaten von
+            der Karte: Dafür wird ein „Rückkanal“ benötigt. Die
+            ESS Solo1 hat sowas einfach nicht. Egal ob man also eine der
+            oben genannten Karten benutzt oder eine andere: Sie muß
+            Vollduplex beherrschen, sonst kann man sie nicht einsetzen. Ich
+            habe eine C-Media 8738 bekommen, das funktioniert einwandfrei.
+            Und so nebenbei klingt sie auch wesentlich besser als die ESS
+            Solo1.</p>
+        <p>Allerdings gibt es mit der C-Media wieder ein anderes Problem:
+            Ein Mono-Mikrofon hört man lokal zwar auf beiden Kanälen, aber
+            die encodeten und gestreamten Klangdaten vom Mikrofon kommen bei
+            den Hörern nur noch auf einem Kanal an. Das kann bei Sendungen
+            mit größerem Redeanteil sehr störend sein.</p>
+        <h4>Mikrofon bzw. Headset</h4>
+        <p>Ein richtig gutes Mikrofon ist natürlich schick; allein, ich
+            kann mir keines leisten. Ich habe derzeit ein billiges
+            Logitech-Mikrofon in Betrieb. Das tut es zumindest für den
+            Anfang auch; Hörer bemängelten nur, daß das Mikrofon auch bei
+            höchster Sendeleistung, und obwohl ich es sehr dicht vorm Mund
+            hatte, etwas zu leise sei. Mittlerweile bin ich bei einem
+            einfachen Logitech-Mikrofon, das die gleichen Probleme zeigt;
+            billig ist halt billig.</p><hr />
+        <h3>Server</h3>
+        <p>Der Server, der wie gesagt einfach nur ein Verteiler ist,
+            braucht vor allem eine „dicke“ Leitung. Er muß die
+            Daten vom Stream-Rechner entgegennehmen und an die gewünschte
+            Anzahl Hörer wieder parallel raussenden können. Bei zwei Hörern
+            mag das vielleicht noch über eine DSL-Leitung gehen; wenn es
+            mehr sind, sollte es schon ein Rechner mit Standleitung sein.</p>
+        <p>Ansonsten vermute ich, daß der Server nicht zu wenig RAM haben
+            sollte, um die Daten zwischenspeichern zu können. Meinem alten
+          <a href="/computer/pc/seeadler.html">Rootserver</a> schienen seine
+            256&nbsp;MB RAM aber wohl bereits zu genügen.</p>
+        <h4>Vorsicht bei Traffic-Limits!</h4>
+        <p>Das einzige ernsthafte Problem, das hier auftauchen könnte, ist
+            ein Traffic-Limit des Server-Providers. Wenn man eines hat und
+            dieses überschreitet, kann es schnell teuer werden.</p>
+        <p>Ich habe ausgerechnet, daß man bei einer Bitrate
+            von 64&nbsp;kBit/s pro Hörer und Stunde mit ca. 30&nbsp;MB Traffic
+            rechnen muß. Dazu kommt der eingehende Datenstrom, also die
+            Datenmenge, die der Stream-PC an den Server sendet.
+            Rechenbeispiel:</p>
+        <p>Angenommen, ich sende täglich 10 Stunden lang. Dann entstehen
+            pro Hörer (plus Stream-PC) je 10 x 30&nbsp;MB Traffic pro Tag. 
+            Damit würden in einem Monat 30 x 300&nbsp;MB = 9.000&nbsp;MB
+            oder 9&nbsp;GB Traffic erzeugt.  Rein rechnerisch und rein auf
+            den Traffic bezogen könnte ich so mit einem Limit von
+            500&nbsp;GB locker 50&nbsp;Hörer versorgen (500&nbsp;GB / 9 =
+            55,55) – davon ausgehend, daß auf dem Server sonst keine
+            traffic-relevanten Dienste laufen. </p><hr />
+        <h2>Software</h2>
+        <p>Zum Streamen unter Linux ist, wenn man moderiertes
+            Internet-Radio machen möchte, ein kleines Konglomerat an
+            Software erforderlich.  Es gibt zwar auch fertige Pakete, aber
+            dabei lernt man ja nichts.&nbsp;;-) – abgesehen davon, daß
+            sich diese Pakete bei mir größtenteils gar nicht zum
+            Installieren überreden lassen wollten und das einzige, das ich
+            installiert bekommen habe, eine für mich völlig unverständliche
+            Oberfläche mitbringt.</p>
+        <h3>Stream-PC</h3>
+        <p>Derzeit installiert ist 
+          <a href="http://www.debian.org/">Debian</a> GNU/Linux 6.0 Squeeze.
+            Im Gegensatz zu früheren Installationen baue ich den Kernel
+            mittlerweile nicht mehr selbst, weil der Distributionskernel
+            alles mitbringt, was ich brauche.</p>
+        <h4>Was wird benötigt?</h4>
+        <ul><li><em> Mixer:</em> Es werden zwei Mixer benötigt, und zwar der
+               alsamixer für Grundeinstellungen und ein
+               zusätzlicher, der die „Aussteuerung“ zum Senden
+               regelt. Das kann der alsamixer nämlich nicht. Ich habe dafür
+               aumix-gtk genommen.</p>
+          <li class="text"><em> Player:</em> Um Musik abzuspielen, können
+               beliebige Player genommen werden; er sollte der Einfachheit
+               halber mal mindestens Playlisten kennen. Ich bevorzuge
+               mittlerweile audacious.</p>
+          <li class="text"> CDs spiele ich nicht direkt ab; die rippe ich
+               vorher und packe sie auf meine Musik-Partition. Allerdings
+               kann audacious über ein Plugin auch CDs abspielen.</li>
+          <li class="text"><em> Streamer:</em> Dieses Programm übernimmt die
+               wieder eingesammelten Klang-Rohdaten von der Soundkarte
+               und kümmert sich sowohl um das Encoding als auch darum,
+               daß der Datenstrom zum Server gelangt. Hier habe ich darkice
+               gewählt, und zur besseren Bedienung die grafische Oberfläche
+               darksnow.</li></ul>
+        <p>So, und jetzt dröseln wir das mal in die nötigen Pakete auf.
+          <em>Für die nächsten Schritte muß man root sein!</em>
+            Zuerst ist es notwendig, in
+          <em class="unprop">/etc/apt/sources.list</em> eine zusätzliche
+            Paketquelle einzutragen:</p>
+        <div class="console">
+          <p>deb http://www.debian-multimedia.org $distri main</p></div>
+        <p>In diesem Fall steht $distri für squeeze oder stable. Nach
+            diesem Eintrag kann man einen Teil der benötigten Pakete direkt
+            mit apt-get ziehen, und zwar:</p>
+        <div class="console">
+          <p>$ apt-get install alsa-utils aumix-gtk lame audacious darksnow</p></div>
+        <p>Die nötigen Libs werden nachgezogen und sind hier nicht extra
+            aufgeführt; statt audacious kann, wie erwähnt, ein beliebiger
+            anderer Player gewählt werden. Wer einen Teil der Pakete schon
+            installiert hat, kann diese natürlich hier rauslassen.</p>
+        <p>darksnow zieht automatisch das (nur OGG-fähige) Paket darkice
+            nach; daher ist es für einen MP3-Stream immer noch sinnvoll,
+            darkice zusätzlich selbst zu bauen. Das liegt daran, daß MP3
+            kein freies Format ist und daher von Software in Debian nicht
+            erzeugt werden darf. Wer nur in OGG Vorbis streamen will, kann
+            sich diese Zusatzarbeit natürlich sparen.</p>
+        <h4>darkice compilieren</h4>
+        <p>Wenn man sich den Tarball für darkice heruntergeladen hat, kann
+            man die Sourcen noch lange nicht compilieren. Zunächst werden
+            nämlich einige development files von libraries benötigt, ohne die
+            darkice nicht gebaut werden kann.</p>
+        <p>Ich war hier insbesondere über einen Bug in der Version 0.17.1
+            von darkice gestolpert, die mir beim ./configure bestätigte, daß
+            alle benötigten Dateien vorhanden seien. Tatsächlich fehlte aber
+            eine Datei, die darkice zur Kommunikation mit jackd benötigt. In
+            der Version 0.18.1 war dieser Bug dann behoben.</p>
+        <p>Also, erstmal devel-files „nachladen“:</p>
+        <div class="console">
+          <p>$ apt-get install libasound2-dev libmp3lame-dev</p></div>
+        <p>Jetzt kann es losgehen: Erst noch in das Verzeichnis wechseln, in
+            welchem der Tarball steht (ich nehme dafür meist
+          <em class="unprop">/usr/src/</em> bzw. verschiebe Tarballs
+            dorthin). Dann:</p>
+        <div class="console">
+          <p>$ tar -xzf darkice-0.18.1.tar.gz</p>
+          <p>$ cd darkice-0.18.1</p>
+          <p>$ ./configure --with-alsa --with-lame</p>
+          <p>$ make</p>
+          <p>$ make install</p></div>
+        <p>Die Versionsnummer ist natürlich nur ein Beispiel, die 0.18.1
+            ist nicht mehr aktuell.</p>
+        <p>Wenn beim ./configure Warnhinweise auftauchen, fehlt was. Am
+            besten findet man das entsprechende Paket mit apt-cache search
+            auf das, was er als fehlend bemängelt, und dev. Wird also
+            beispielsweise lame als fehlend angezeigt, hilft wahrscheinlich
+            ein</p>
+        <div class="console">
+          <p>$ apt-cache search lame dev</p></div>
+        <p>Es fehlt also dann nicht das eigentliche Paket, sondern
+            die dazugehörigen development files.</p><hr />
+        <h3>Server</h3>
+        <p>Hier ist alles viel einfacher:</p>
+        <div class="console">
+          <p>$ apt-get install icecast2</p></div>
+        <p>… und die Installation ist gegessen.</p>
+        <hr /> 
+        <h3>Konfiguration</h3>
+        <p>Zur Konfiguration sämtlicher Programme habe ich überwiegend das
+          <a href="http://de.gentoo-wiki.com/XBox360">XBox360-HowTo</a> aus
+            dem Gentoo-Wiki genutzt, vielen Dank an den Autor!</p>
+        <p>(Die Bilder sind aus den Sarge-Versionen; die Fenster
+            unterscheiden sich manchmal in neueren Versionen, aber
+            eigentlich nur in der Anordnung, und auch nur minimal.)</p>
+        <h4>darkice mit darksnow konfigurieren</h4>
+        <p>Der erste Kandidat ist <em>darkice.</em> Dazu startet man
+            darksnow und trägt dort ein paar Angaben ein (anklicken zum
+            vergrößern):</p>
+        <div class="picleft">
+          <a class="graflink" href="/bilder/darksnow-screen.jpg">
+            <img src="/bilder/darksnow-screen-preview.jpg" width="300"
+                 height="323" alt=" " />
+          </a></div>
+        <p>„radio.meinserver.de“ muss natürlich durch den
+            echten Namen (oder die IP) des Stream-Servers ersetzt werden.</p>
+        <p>Am meisten zu schaffen machte mir der Begriff <em>Mount
+            Point</em>. Denn eigentlich verstehe ich darunter ein
+            Verzeichnis in meinem Verzeichnisbaum, in welches ich ein Medium
+            (bzw. ein Filesystem) einklinken (mounten) kann. Schließlich kam
+            ich auf den Trichter: Das ist einfach ein Dateiname, den der
+            Server den Playern anbieten kann, weil die einen benötigen. Denn
+            den Playern wird ja der Stream wie eine Datei übergeben. Das
+            wird aber nirgendwo erklärt oder auch nur erwähnt!</p>
+        <p>Das Paßwort muß natürlich mit dem in der icecast2-Konfiguration
+            auf dem Server (siehe weiter unten) übereinstimmen.</p>
+        <p>Wenn der Stream-PC und/oder der Server genügend Plattenplatz
+            anbieten, kann man sich von den Sendungen einen Dump
+            (Mitschnitt) anlegen lassen. Dann wird die gesamte Sendung in
+            eine Datei im Zielformat des Streams gespeichert. Auf dem PIII
+            hatte das bei mir nie funktioniert, der Athlon packt es
+            inzwischen (vermutlich auch deshalb, weil die Musikdateien
+            mittlerweile auf einer SATA- und nicht mehr auf einer IDE-Platte
+            liegen). Auf dem Server tut es bei mir nicht; möglicherwiese
+            kann icecast2 das nicht, oder es fehlt noch etwas.</p>
+        <div class="picleft">
+          <a class="graflink" href="/bilder/darksnow-screen1.jpg">
+            <img src="/bilder/darksnow-screen1-preview.jpg" width="300"
+                 height="319" alt="Textaufarbeitung des Screenshots folgt noch!" />
+          </a></div>
+        <p>Im zweiten Reiter wird unter anderem die Bitrate festgelegt. Sie
+            bestimmt primär über die Qualität des Streams, aber auch über
+            die benötigte Bandbreite auf den Leitungen.</p>
+        <p>Der letzte Eintrag in diesem Reiter, „Device input“,
+            muß nur für jackd wie abgebildet „jack_auto“ heißen. 
+            Wenn ohne jackd gearbeitet wird, wird hier „hw0,0“
+            eingetragen (davon ausgehend, daß bei mehreren vorhandenen
+            Soundkarten die erste zum Streamen gewählt wird).</p>
+        <p>Bei der Stream-Beschreibung im dritten Reiter ist man recht frei
+            (der Servername muß nur stimmen). Ein rechtlich relevanter
+            Eintrag ist aber der letzte: Ich habe meinen Stream auf nicht
+            öffentlich gesetzt, weil ich sonst GEMA-Gebühren bezahlen und
+            mich nach deren ziemlich kruden Regeln richten müßte. Statt
+            dessen gebe ich die Adresse für den Stream nur gezielt an
+            Freunde, die ich mithören lassen möchte.</p>
+        <p>Am Ende nicht vergessen, die Config abzuspeichern, darksnow
+            macht das nicht von allein. Etwas störend ist auch, daß darksnow
+            diese Config auch beim Start nicht wieder automatisch lädt, das
+            muß man jedesmal von Hand machen.</p>
+        <h4>icecast2 konfigurieren</h4>
+        <p>Ich sollte vielleicht erwähnen, daß der icecast2 nicht nur ein
+            reiner Streamserver ist. Er bringt auch noch einen kleinen
+            Webserver mit, der ebenfalls auf Port 8000 läuft (sofern das
+            nicht geändert wird).</p>
+        <p>icecast2 wird komplett über die Datei
+          <em class="unprop">/etc/icecast2/icecast.xml</em> gesteuert. Darin
+            sind die folgenden Einträge relevant:</p>
+        <div class="console">
+          <p>&lt;limits&gt;</p>
+          <p>&lt;clients&gt;20&lt;/clients&gt;</p>
+          <p>&lt;!-- Legt die Höchstzahl der Hörer fest --&gt;</p>
+          <p>&lt;/limits&gt;</p>
+          <p>&nbsp;</p>
+          <p>&lt;authentication&gt;</p>
+          <p>&lt;source-password&gt;xxxxxxxxxxxx&lt;/source-password&gt;</p>
+          <p>&lt;!-- Muss mit dem in darkice/darksnow übereinstimmen! --&gt;</p>
+          <p>&lt;/authentication&gt;</p>
+          <p>&nbsp;</p>
+          <p>&lt;hostname&gt;radio.meinserver.de&lt;/hostname&gt;</p>
+          <p>&nbsp;</p>
+          <p>&lt;listen-socket&gt;</p>
+          <p>&lt;port&gt;8000&lt;/port&gt;</p>
+          <p>&lt;!--  &lt;bind-address&gt;127.0.0.1&lt;/bind-address&gt; --&gt;</p>
+          <p>&lt;/listen-socket&gt;</p>
+          <p>&nbsp;</p>
+          <p>&lt;fileserve&gt;1&lt;/fileserve&gt;</p>
+          <p>&nbsp;</p>
+          <p>&lt;security&gt;</p>
+          <p>&lt;chroot&gt;0&lt;/chroot&gt;</p>
+          <p>&lt;changeowner&gt;</p>
+          <p>&lt;user&gt;icecast2&lt;/user&gt;</p>
+          <p>&lt;group&gt;icecast&lt;/group&gt;</p>
+          <p>&lt;/changeowner&gt;</p>
+          <p>&lt;/security&gt;</p>
+        </div>
+        <p>Der Eintrag unter ChangeOwner bewirkt, daß der icecast2 nur mit
+            den Rechten eines normalen Users läuft und eben nicht mit
+            root-Rechten. Trotzdem kann er seine Logs nach /var/log
+            schreiben. – Eine komplette
+          <span class="strike">icecast.xml</span>
+            (die aber auch angepaßt werden muß) findet sich (leider nicht
+            mehr, 2011-02-01) im XBox360-HowTo.</p>
+        <p>Auch beim icecast2 gibt es eine kleine Stolperfalle: Die Rechte
+            der Dateien in <em class="unprop">/etc/icecast2/web/</em> müssen
+            auf 755 geändert werden, sonst bekommen Seitenbesucher einen
+            404.</p>
+        <hr />
+        <h2>Stream ab!</h2>
+        <p>… oder der kürzeste Computerwitz aller Zeiten: „Müßte
+            laufen“ …</p>
+        <p>Als erstes muß natürlich der icecast2 auf dem Server laufen. Er
+            wird – von root – gestartet mit</p>
+        <div class="console">
+          <p>$ icecast2 -c /etc/icecast2/icecast.xml -b</p></div>
+        <p>Das -b bewirkt, daß icecast2 in den Hintergrund geht und die
+            Konsole wieder freiräumt.</p><hr />
+        <h3>Stream-PC startklar machen</h3>
+        <p>Als erstes spreche ich hier nochmal die CPU-Leistung an. Ich
+            hatte beim PIII alles, was nicht direkt zum Streamen gehört (vor
+            allem XChat und Browser) kurzerhand aufs Notebook verlegt, denn
+            obwohl sie nicht direkt zum Streamen gehören, brauche ich sie
+            doch gleichzeitig. Der Grund für die Verlegung war, daß jeder
+            Fensterwechsel, jede Mausbewegung und auch schnelles Tippen im
+            Stream als Störungen zu hören sind, und das nervt auf Dauer doch
+            sehr. Auch ein zwischendurch eingesetzter Duron mit
+            850&nbsp;MHz hatte das Problem noch; erst ab dem Athlon mit 1533
+            MHz ist das Problem Geschichte.</p>
+        <p>Als erstes startet man audacious, aumix und darksnow und ordnet
+            die Fenster so an, daß man möglichst alles sehen kann (incl. 
+            Playliste). Den alsamixer braucht man nur anfangs, um
+            die Soundkarte, falls nötig, vorzukonfigurieren.</p>
+        <p>Als nächstes aumix richtig einstellen: Dabei nehme ich immer
+            erstmal alle Anzeigen raus, die ich nicht benötige; leider merkt
+            der sich das nicht (auch nicht durch save). Dann drehe ich PCM
+            bzw. Master auf 70 bis 80 und Mic auf 100&nbsp;% und regle die
+            Lautstärke (das ist die des eigenen Kopfhörers bzw. 
+            Lautsprechers) auf einen mir angenehmen Wert. Im alsamixer
+            schalte ich ggf. noch den Mic-Booster ein.  Vorsicht, wenn mit
+            Lautsprecherboxen statt Kopfhörer am Stream-PC gearbeitet wird! 
+            Den Booster dann nicht <em>zu</em> hoch drehen, sonst gibt es
+            eine Rückkopplung, und das pfeift ganz eklig :-)</p>
+            
+        <p>Ja, und schließlich im darksnow auf Start Streaming klicken.</p>
+        <p>Erste Kontrolle: Mit einem Browser auf die Webseite des Servers
+            gehen (http://radio.meinserver.de:8000). Dort sollte die
+            „status page“ des icecast2 zu finden sein. Wenn im
+            grauen Bereich die Angaben zu finden sind, die man vorher im
+            darksnow bei „Server Description“ eingetragen hat,
+            ist die Verbindung zum Server aufgebaut.</p>
+        <p>Das heißt aber noch nicht, daß aufgeschaltete Hörer etwas hören
+            können. Um etwas auf den Stream abzuspielen, muß im aumix bei
+            PCM (oder Master) der Rec-Knopf gedrückt (rot) sein. Wenn man
+            sprechen möchte, muß der Rec-Knopf vor Mic rot sein. Bei
+            manchen (nicht allen) Soundkarten geht auch beides gleichzeitig,
+            wobei es empfehlenswert ist, beim Abspielen von Musik das Mikro
+            auszuschalten (Rec auf grün) – vor allem, wenn man nicht
+            beim Mitsingen erwischt werden will&nbsp;;-)</p>
+
+[FLATTRBUTTON]
+        <p>Noch Fragen? Frag
+          <a href="mailto:frosch@atari-frosch.de">mich</a> doch!</p>
+      </div><!-- div#content -->
diff --git a/sites/atarifrosch/internet/stream-mit-linux.py b/sites/atarifrosch/internet/stream-mit-linux.py
new file mode 100755 (executable)
index 0000000..5eae347
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/env python3
+# -*- coding: utf8 -*-
+
+targetfile = "/internet/stream-mit-linux.html"
+title = "Internet-Radio streamen mit Linux HowTo"
+description = "Internet-Radio streamen mit Debian Linux HowTo"
+dctitle = "Internet-Radio streamen mit Debian GNU/Linux"
+dcsubject = dctitle
+
+headline = "Streamen mit Linux"
+
+contentfile = "stream-mit-linux.htm"
+myurl = proto + "://" + domain + "/" + targetfile
+
+replacement["[FLATTRTHING]"] = "457983"
+replacement["[FLATTRNAME]"] = "Internet-Radio-streamen-mit-Debian-GNULinux"
diff --git a/sites/atarifrosch/menu.py b/sites/atarifrosch/menu.py
new file mode 100755 (executable)
index 0000000..f928eb2
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+# -*- coding: utf8 -*-
+
+menuitem[0]  = "htm, home, index.html, Startseite"
+menuitem[1]  = "dir, close, vorstellung, Vorstellung"
+menuitem[2]  = "dir, close, https://blog.atari-frosch.de/, Blog"
+menuitem[3]  = "dir, close, computer, Computer"
+menuitem[4]  = "dir, close, internet, Internet"
+menuitem[5]  = "dir, close, politik, Politik"
+menuitem[6]  = "dir, close, depression, Depression"
+menuitem[7]  = "dir, close, hobby, Hobbies"
+menuitem[8]  = "dir, close, musik, Musik"
+menuitem[9]  = "dir, close, fotografie, Fotografie"
+menuitem[10] = "dir, close, pretender, Pretender"
+menuitem[11] = "dir, close, flohmarkt, Flohmarkt"
+menuitem[12] = "dir, close, misc, Vermischtes"
+menuitem[13] = "htm, file, impressum.html, Impressum"
+menuitem[14] = "htm, file, probleme.html, Darstellungsprobleme?"
+menuitem[15] = "dir, close, hist, Website-History"
diff --git a/sites/atarifrosch/start.py b/sites/atarifrosch/start.py
new file mode 100755 (executable)
index 0000000..285399c
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env python3
+# -*- coding: utf8 -*-
+
diff --git a/start.py b/start.py
new file mode 100755 (executable)
index 0000000..285399c
--- /dev/null
+++ b/start.py
@@ -0,0 +1,3 @@
+#!/usr/bin/env python3
+# -*- coding: utf8 -*-
+