Sie sind nicht angemeldet.

Lieber Besucher, herzlich willkommen bei: GentooForum.de. Falls dies Ihr erster Besuch auf dieser Seite ist, lesen Sie sich bitte die Hilfe durch. Dort wird Ihnen die Bedienung dieser Seite näher erläutert. Darüber hinaus sollten Sie sich registrieren, um alle Funktionen dieser Seite nutzen zu können. Benutzen Sie das Registrierungsformular, um sich zu registrieren oder informieren Sie sich ausführlich über den Registrierungsvorgang. Falls Sie sich bereits zu einem früheren Zeitpunkt registriert haben, können Sie sich hier anmelden.

1

16.11.2008, 10:22

Python problem

Ich habe ein Python skkript gefunden welches von dem Audio part (also ohne tags) eine md5 checksum errechnet und diese in die Tags selbst schreibt.


Ich persönlich hätte dieses skript gerne so geändert, das die checksumme nicht in die tags geschrieben wird, sondern nur auf die standart ausgabe/ in eine extra datei oder so.


Vielleicht kennt sich jemand von euch ganz super toll mit Python aus, und will mir helfen.

Ich kenne mich mit python nämlich nicht aus ;)


PS: Hier das Skript


Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#!/usr/bin/python

"""mp3md5: MP3 checksums stored in ID3v2

mp3md5 calculates MD5 checksums for all MP3's on the command line
(either individual files, or directories which are recursively
processed).

Checksums are calculated by skipping the ID3v2 tag at the start of the
file and any ID3v1 tag at the end (does not however know about APEv2
tags).  The checksum is stored in an ID3v2 UFID (Unique File ID) frame
with owner 'md5' (the ID3v2 tag is created if necessary).

Usage: mp3md5.py [options] [files or directories]

-h/--help
  Output this message and exit.

-l/--license
  Output license terms for mp3md5 and exit.

-n/--nocheck
  Do not check existing checksums (so no CONFIRMED or CHANGED lines
  will be output). Causes --update to be ignored.

-r/--remove
  Remove checksums, outputting REMOVED lines (outputs NOCHECKSUM for
  files already without them).  Ignores --nocheck and --update.

-u/--update
  Instead of printing changes, update the checksum aand output UPDATED
  lines.

Depends on the eyeD3 module (http://eyeD3.nicfit.net)

Copyright 2007 G raham P oulter
"""

__copyright__ = "2007 G raham P oulter"
__author__ = "G raham P oulter"
__license__ = """This program is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your option)
any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>."""

import eyeD3
from getopt import getopt
import md5
import os
import struct
import sys

pretend = False # Whether to pretend to write tags
nocheck = False # Whether to not check existing sums
remove = False  # Whether to remove checksums
update = False  # Whether to update changed checksums
    
def log(head, body, *args):
    """Print a message to standard output"""
    print head + " "*(12-len(head)) + (body % args)

def openTag(fpath):
    """Attempt to open ID3 tag, creating a new one if not present"""
    if not eyeD3.tag.isMp3File(fpath):
        raise ValueError("NOT AN MP3: %s" % fpath)
    try:
        audioFile = eyeD3.tag.Mp3AudioFile(fpath, eyeD3.ID3_V2)
    except eyeD3.tag.InvalidAudioFormatException, ex:
        raise ValueError("ERROR IN MP3: %s" % fpath)
    tag = audioFile.getTag()
    if tag is None:
        tag = eyeD3.Tag(fpath)
        tag.header.setVersion(eyeD3.ID3_V2_3)
        if not pretent:
            tag.update()
    return tag

### WARNING: REMEMBER TO UPDATE THE COPY IN MD5DIR
def calculateUID(filepath):
    """Calculate MD5 for an MP3 excluding ID3v1 and ID3v2 tags if
    present. See www.id3.org for tag format specifications."""
    f = open(filepath, "rb")
    # Detect ID3v1 tag if present
    finish = os.stat(filepath).st_size;
    f.seek(-128, 2)
    if f.read(3) == "TAG":
        finish -= 128
    # ID3 at the start marks ID3v2 tag (0-2)
    f.seek(0)
    start = f.tell()
    if f.read(3) == "ID3":
        # Bytes w major/minor version (3-4)
        version = f.read(2)
        # Flags byte (5)
        flags = struct.unpack("B", f.read(1))[0]
        # Flat bit 4 means footer is present (10 bytes)
        footer = flags & (1<<4)
        # Size of tag body synchsafe integer (6-9)
        bs = struct.unpack("BBBB", f.read(4))
        bodysize = (bs[0]<<21) + (bs[1]<<14) + (bs[2]<<7) + bs[3]
        # Seek to end of ID3v2 tag
        f.seek(bodysize, 1)
        if footer:
            f.seek(10, 1)
        # Start of rest of the file
        start = f.tell()
    # Calculate MD5 using stuff between tags
    f.seek(start)
    h = md5.new()
    h.update(f.read(finish-start))
    f.close()
    return h.hexdigest()

def readUID(fpath):
    """Read MD5 UID from ID3v2 tag of fpath."""
    tag = openTag(fpath)
    for x in tag.getUniqueFileIDs():
        if x.owner_id == "md5":
            return x.id
    return None

def removeUID(fpath):
    """Remove MD5 UID from ID3v2 tag of fpath"""
    tag = openTag(fpath)
    todel = None
    for i, x in enumerate(tag.frames):
        if isinstance(x, eyeD3.frames.UniqueFileIDFrame) \
               and x.owner_id == "md5":
            todel = i
            break
    if todel is not None:
        del tag.frames[i]
        if not pretend:
            tag.update(eyeD3.ID3_V2_3)
        return True
    else:
        return False

def writeUID(fpath, uid):
    """Write the MD5 UID in the ID3v2 tag of fpath."""
    tag = openTag(fpath)
    present = False
    for x in tag.getUniqueFileIDs():
        if x.owner_id == "md5":
            present = True
            x.id = uid
            break
    if not present:
        tag.addUniqueFileID("md5", uid)
    if not pretend:
        tag.update(eyeD3.ID3_V2_3)

def mungeUID(fpath):
    "Update the MD5 UID on the tag"""
    if remove:
        if removeUID(fpath):
            log("REMOVED", fpath)
        else:
            log("NOCHECKSUM", fpath)
    else:
        cur_uid = readUID(fpath)
        if cur_uid is None:
            new_uid = calculateUID(fpath)
            writeUID(fpath, new_uid)
            log("ADDED", fpath)
        elif not nocheck:
            new_uid = calculateUID(fpath)
            if cur_uid == new_uid:
                log("CONFIRMED", fpath)
            elif update:
                writeUID(fpath, new_uid)
                log("UPDATED", fpath)
            else:
                log("BROKEN", fpath)

if __name__ == "__main__":
    optlist, args = getopt(sys.argv[1:], "hlnru", ["help","license","nocheck","remove","update"])
    for key, value in optlist:
        if key in ("-h","--help"):
            print __doc__
            sys.exit(0)
        elif key in ("-l","--license"):
            print license
            sys.exit(0)
        elif key in ("-n","--nocheck"):
            nocheck = True
        elif key in ("-r", "--remove"):
            remove = True
        elif key in ("-u", "--update"):
            update = True
    for start in args:
        if os.path.isfile(start):
            if start.endswith(".mp3"):
                mungeUID(start)
        elif os.path.isdir(start):
            for root, dirs, files in os.walk(start):
                dirs.sort()
                files.sort()
                for fname in files:
                    if fname.endswith(".mp3"):
                        mungeUID(os.path.join(root,fname))
        else:
            log("WARNING", "%s does not exist", start)
Intel core 2 duo E6600
XFX Geforce 7950 gt
4096 MB DDR2
Intel DP35DP

2

16.11.2008, 16:08

Hi!

Also ich habe selber noch nie mit Python gearbeitet, sondern hauptsächlich Perl-Erfahrung, aber ich denke, dass ist ganz einfach möglich.
Der Autor hat nämlich schon die Variable "pretend" eingefügt, sie lässt sich nur noch nicht interaktiv setzen. Es muss folgendes Angepasst werden:

Quellcode

1
2
3
4
Zeile 184 von
optlist, args = getopt(sys.argv[1:], "hlnru", ["help","license","nocheck","remove","update"])
zu
optlist, args = getopt(sys.argv[1:], "hlnrup", ["help","license","nocheck","remove","update","pretend"])

und

Quellcode

1
2
3
hinter Zeile 197 eingefügt:
elif key in ("-p", "--pretend"):
             pretend = True

jetzt wird schonmal keine Änderung an der Datei mehr vorgenommen. Die Ausgabe der Checksum muss jetzt noch an passender Stelle eingefügt werden. Dafür würde ich folgende Änderung machen:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Zeile 168-181 zu:
cur_uid = readUID(fpath)
new_uid = calculateUID(fpath)
if pretend:
 log($new_uid, fpath)
if cur_uid is None:
 writeUID(fpath, new_uid)
 log("ADDED", fpath)
elif not nocheck:
 if cur_uid == new_uid:
  log("CONFIRMED", fpath)
 elif update:
  writeUID(fpath, new_uid)
  log("UPDATED", fpath)
 else:
  log("BROKEN", fpath)

Die Zeilenummer beziehen sich alle auf den Quellcode in deinem Post.

Habe jetzt nicht im original Quellcode alles nachvollzogen ob es dann auch wie gewünscht funktionieren wird, aber ich denke schon.

Also nochmal, ich habe den Code selber nicht getestet, also Verwendung auf eigenes Risiko!

Bye, H2
"...klingt komisch, ist aber so!"

3

16.11.2008, 18:15

also ich habe es nun so:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#!/usr/bin/python

"""mp3md5: MP3 checksums stored in ID3v2

mp3md5 calculates MD5 checksums for all MP3's on the command line
(either individual files, or directories which are recursively
processed).

Checksums are calculated by skipping the ID3v2 tag at the start of the
file and any ID3v1 tag at the end (does not however know about APEv2
tags).  The checksum is stored in an ID3v2 UFID (Unique File ID) frame
with owner 'md5' (the ID3v2 tag is created if necessary).

Usage: mp3md5.py [options] [files or directories]

-h/--help
  Output this message and exit.

-l/--license
  Output license terms for mp3md5 and exit.

-n/--nocheck
  Do not check existing checksums (so no CONFIRMED or CHANGED lines
  will be output). Causes --update to be ignored.

-r/--remove
  Remove checksums, outputting REMOVED lines (outputs NOCHECKSUM for
  files already without them).  Ignores --nocheck and --update.

-u/--update
  Instead of printing changes, update the checksum aand output UPDATED
  lines.

Depends on the eyeD3 module (http://eyeD3.nicfit.net)

Copyright 2007 G raham P oulter
"""

__copyright__ = "2007 G raham P oulter"
__author__ = "G raham P oulter"
__license__ = """This program is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your option)
any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>."""

import eyeD3
from getopt import getopt
import md5
import os
import struct
import sys

pretend = False # Whether to pretend to write tags
nocheck = False # Whether to not check existing sums
remove = False  # Whether to remove checksums
update = False  # Whether to update changed checksums
    
def log(head, body, *args):
    """Print a message to standard output"""
    print head + " "*(12-len(head)) + (body % args)

def openTag(fpath):
    """Attempt to open ID3 tag, creating a new one if not present"""
    if not eyeD3.tag.isMp3File(fpath):
        raise ValueError("NOT AN MP3: %s" % fpath)
    try:
        audioFile = eyeD3.tag.Mp3AudioFile(fpath, eyeD3.ID3_V2)
    except eyeD3.tag.InvalidAudioFormatException, ex:
        raise ValueError("ERROR IN MP3: %s" % fpath)
    tag = audioFile.getTag()
    if tag is None:
        tag = eyeD3.Tag(fpath)
        tag.header.setVersion(eyeD3.ID3_V2_3)
        if not pretent:
            tag.update()
    return tag

### WARNING: REMEMBER TO UPDATE THE COPY IN MD5DIR
def calculateUID(filepath):
    """Calculate MD5 for an MP3 excluding ID3v1 and ID3v2 tags if
    present. See www.id3.org for tag format specifications."""
    f = open(filepath, "rb")
    # Detect ID3v1 tag if present
    finish = os.stat(filepath).st_size;
    f.seek(-128, 2)
    if f.read(3) == "TAG":
        finish -= 128
    # ID3 at the start marks ID3v2 tag (0-2)
    f.seek(0)
    start = f.tell()
    if f.read(3) == "ID3":
        # Bytes w major/minor version (3-4)
        version = f.read(2)
        # Flags byte (5)
        flags = struct.unpack("B", f.read(1))[0]
        # Flat bit 4 means footer is present (10 bytes)
        footer = flags & (1<<4)
        # Size of tag body synchsafe integer (6-9)
        bs = struct.unpack("BBBB", f.read(4))
        bodysize = (bs[0]<<21) + (bs[1]<<14) + (bs[2]<<7) + bs[3]
        # Seek to end of ID3v2 tag
        f.seek(bodysize, 1)
        if footer:
            f.seek(10, 1)
        # Start of rest of the file
        start = f.tell()
    # Calculate MD5 using stuff between tags
    f.seek(start)
    h = md5.new()
    h.update(f.read(finish-start))
    f.close()
    return h.hexdigest()

def readUID(fpath):
    """Read MD5 UID from ID3v2 tag of fpath."""
    tag = openTag(fpath)
    for x in tag.getUniqueFileIDs():
        if x.owner_id == "md5":
            return x.id
    return None

def removeUID(fpath):
    """Remove MD5 UID from ID3v2 tag of fpath"""
    tag = openTag(fpath)
    todel = None
    for i, x in enumerate(tag.frames):
        if isinstance(x, eyeD3.frames.UniqueFileIDFrame) \
               and x.owner_id == "md5":
            todel = i
            break
    if todel is not None:
        del tag.frames[i]
        if not pretend:
            tag.update(eyeD3.ID3_V2_3)
        return True
    else:
        return False

def writeUID(fpath, uid):
    """Write the MD5 UID in the ID3v2 tag of fpath."""
    tag = openTag(fpath)
    present = False
    for x in tag.getUniqueFileIDs():
        if x.owner_id == "md5":
            present = True
            x.id = uid
            break
    if not present:
        tag.addUniqueFileID("md5", uid)
    if not pretend:
        tag.update(eyeD3.ID3_V2_3)

def mungeUID(fpath):
    "Update the MD5 UID on the tag"""
    if remove:
        if removeUID(fpath):
            log("REMOVED", fpath)
        else:
            log("NOCHECKSUM", fpath)
    else:
      cur_uid = readUID(fpath)
      new_uid = calculateUID(fpath)
	if pretend:
	    log($new_uid, fpath)
	if cur_uid is None:
	    writeUID(fpath, new_uid)
	    log("ADDED", fpath)
	elif not nocheck:
	    if cur_uid == new_uid:
		log("CONFIRMED", fpath)
	    elif update:
		  writeUID(fpath, new_uid)
		  log("UPDATED", fpath)
	    else:
		  log("BROKEN", fpath)

if __name__ == "__main__":
    optlist, args = getopt(sys.argv[1:], "hlnru", ["help","license","nocheck","remove","update","pretend"])
    for key, value in optlist:
        if key in ("-h","--help"):
            print __doc__
            sys.exit(0)
        elif key in ("-l","--license"):
            print license
            sys.exit(0)
        elif key in ("-n","--nocheck"):
            nocheck = True
        elif key in ("-r", "--remove"):
            remove = True
        elif key in ("-u", "--update"):
            update = True
	elif key in ("-p", "--pretend"):
             pretend = True
    for start in args:
        if os.path.isfile(start):
            if start.endswith(".mp3"):
                mungeUID(start)
        elif os.path.isdir(start):
            for root, dirs, files in os.walk(start):
                dirs.sort()
                files.sort()
                for fname in files:
                    if fname.endswith(".mp3"):
                        mungeUID(os.path.join(root,fname))
        else:
            log("WARNING", "%s does not exist", start)



leider bekomme ich jedoch einen fehler ...:

Quellcode

1
2
3
4
5
$ python mp3md5.py mytestfile.mp3
  File "mp3md5.py", line 170
    if pretend:
    ^
IndentationError: unexpected indent
Intel core 2 duo E6600
XFX Geforce 7950 gt
4096 MB DDR2
Intel DP35DP

4

16.11.2008, 18:36

Ok, der Quellcode war wohl nicht ganz richtig formatiert (spielt das bei Python eine Rolle*?*) und bei mir hatte sich von Perl her intuitiv ein "$" vor der Variablen eingeschlichen ;)
So sollte es nun funktionieren:

EDIT: fehlerhaften Code wegen Übersichtlichkeit im Thread entfernt. H2

P.S.: Kann jemand mal sagen, wie diese Änderung bezüglich der Lizenz zu behandel ist? Ich will ja hier nichts unrechtes tun ;)

Gruß, H2
"...klingt komisch, ist aber so!"

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »H2« (16.11.2008, 19:52)


5

16.11.2008, 19:16

Vielen Dank!

ich teste gleich mal ..

EDIT: Leider wieder nicht:

Quellcode

1
2
3
4
5
$ python mp3md5.py mytestfile.mp3
  File "mp3md5.py", line 76
    raise ValueError("NOT AN MP3: %s" % fpath)
        ^
IndentationError: expected an indented block



steht ja eh da :

Zitat

__license__ = """This program is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your option)
any later version.


also schon ..
Intel core 2 duo E6600
XFX Geforce 7950 gt
4096 MB DDR2
Intel DP35DP

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »schade« (16.11.2008, 19:29)


6

16.11.2008, 19:51

Oh, irgendwie ist wohl die Einrückung im Code-Block durcheinandergeraten, ich habe es jetzt nochmal durchgehend gleich formatiert und hoffe es funktioniert jetzt. Wenn es nicht klappt, dann vergleiche mal mit dem Quellcode, den du im ersten Post hattest, die Formatierung ist entscheidend für die Schleifen- und If-Blöcke.

EDIT: Ach, warum so kompliziert mit dem Code-Block, wenn es doch als Anhang simpel geht. Einfach die Dateiendung in .py umändern (wegen Dateiformat-Upload-Sperre).

Gruß, H2
»H2« hat folgende Datei angehängt:
  • mp3md5.txt (6,09 kB - 1 mal heruntergeladen - zuletzt: 16.11.2008, 20:15)
"...klingt komisch, ist aber so!"

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »H2« (16.11.2008, 20:05)


7

16.11.2008, 20:14

Super! .. Vielen Dank


aber nur fast:

Quellcode

1
2
3
python mp3md5.py --pretend mytestfile.mp3
b4f2d5d1a1eb2253869724ee652ccea4mytestfile.mp3
CONFIRMED   mytestfile.mp3


entweder ein Leerzeichen zwischen md5 oder den namen nach der md5 garnicht mit ausgeben.


nochmals vielen dank!
Intel core 2 duo E6600
XFX Geforce 7950 gt
4096 MB DDR2
Intel DP35DP

8

16.11.2008, 21:02

Das ist relativ einfach zu machen. Der Aufruf für die Ausgabe ist in Zeile 174 (der Datei die ich hochgeladen habe) und die Formatierung der Ausgabe steht in Zeile 71.
Eine alternative zur Ursprünglichen Formatierung wäre zb

Quellcode

1
print head + " "*(80-len(head)-len(body)) + body

d.h. zuerst steht der erste Parameter des log-Aufrufs, dann kommen soviele Leerezeichen, dass bei einer Konsole in Standardgröße, der zweite Parameter am Ende der Zeile steht und zuletzt der Text des zweiten Parameters. Aber das kannst du dir drehen wie du möchtest. Nur nicht vergessen, dass es auch noch andere Aufrufe der log-Funktion gibt, ich habe jetzt in der geposteten Zeile den Parameter args unterschlagen, dessen Intention mir aber auch nicht ganz klar ist.
Simpelste Lösung wäre

Quellcode

1
print head + " " + (body % args)


Aber das kannst du dir wohl so machen, wie du es gerne hättest ;)
"...klingt komisch, ist aber so!"

9

17.11.2008, 10:25

grosses DANKE an H2



Was genau von der Formatierung ist nun der filename selbst?


Ein wenig muss ich mir das skript noch ansehen, weil es ein problem mit dateien gibt die noch kein ID3 Tag haben.
Intel core 2 duo E6600
XFX Geforce 7950 gt
4096 MB DDR2
Intel DP35DP

10

17.11.2008, 12:53

Kein Problem.
Was genau von der Formatierung ist nun der filename selbst?

Ein wenig muss ich mir das skript noch ansehen, weil es ein problem mit dateien gibt die noch kein ID3 Tag haben.
Die Checksumme und der Dateiname wird mit dem Aufruf "log(new_uid, fpath)" ausgegeben. "new_uid" enthält die berechnete Checksumme und "fpath" den Dateinamen.
Somit steht beim Aufruf der Funktion "log" dann der Inhalt von "new_uid" in "head" und in "body" der Inhalt von "fpath".
"print head + " " + (body % args)" gibt dann also zuerst die Verkettung des Inhalts von "head" mit einem Leerzeichen und dann den Inhalt von "body" modulo dem Wert von "args" aus. Diesen letzten Schritt mit dem Modulo stammt vom Originalautor, und ich kann Ihn nicht nachvollziehen, aber ich habe ihn einfach mal übernommen, weil "args" bei den meisten Aufrufen sowieso nicht definiert ist und somit das Modulo keinen Effekt hat.

Wegen dem fehlenden Tag in einigen Dateien müsste die Funktion "openTag" eigentlich einen neuen Tag selbstständig anlegen....
"...klingt komisch, ist aber so!"

11

18.11.2008, 14:06

Gute Arbeit! :thumbsup:

(PS: Einrückungen und Formatierungen sind in Python zwingend! Ist Teil der Syntax; es gibt keine Blöcke-Commandos; alles auf einer Einrückung ist ein Block ...)
http://www.dyle.org
IM-Account (Jabber!) sind auf meiner HP ...
There is no place like /home

http://www.gentooforum.de
http://www.gentoofreunde.org

<div>how to annoy a web developer?</span>