[xiph-commits] r13245 - websites/validator.xspf.org
sping at svn.xiph.org
sping at svn.xiph.org
Wed Jul 11 06:10:13 PDT 2007
Author: sping
Date: 2007-07-11 06:10:11 -0700 (Wed, 11 Jul 2007)
New Revision: 13245
Added:
websites/validator.xspf.org/.htaccess
websites/validator.xspf.org/check.py
websites/validator.xspf.org/xspflogo-1.5.gif
Log:
Initial checkin with code of 2007-02-18
Added: websites/validator.xspf.org/.htaccess
===================================================================
--- websites/validator.xspf.org/.htaccess (rev 0)
+++ websites/validator.xspf.org/.htaccess 2007-07-11 13:10:11 UTC (rev 13245)
@@ -0,0 +1,6 @@
+# Use mod_rewrite to enable execution of python script
+<IfModule mod_rewrite.c>
+ RewriteEngine On
+ RewriteBase /
+ RewriteRule ^$ check.py [L]
+</IfModule>
Property changes on: websites/validator.xspf.org/.htaccess
___________________________________________________________________
Name: svn:executable
+ *
Added: websites/validator.xspf.org/check.py
===================================================================
--- websites/validator.xspf.org/check.py (rev 0)
+++ websites/validator.xspf.org/check.py 2007-07-11 13:10:11 UTC (rev 13245)
@@ -0,0 +1,1105 @@
+#!/usr/bin/python
+# ------------------------------------------------------------------------------
+# Written by Sebastian Pipping <webmaster at hartwork.org>
+# Licensed under GNU General Public License http://www.gnu.org/copyleft/gpl.html
+#
+# NOTE: Python 2.4 or later requiered for current line attribute of Expat parser
+#
+# 2007-02-18
+# * Added: License header for source code release
+# 2007-01-20
+# * Changed: URI checking removed until proper parser available
+# 2007-01-09
+# * Fixed: No highlighting on XML error bug fixed
+# 2006-10-09
+# * Changed: "Valid" now on the top
+# 2006-10-04
+# ------------------------------------------------------------------------------
+
+import cgi
+############## import cgitb; cgitb.enable()
+import urllib2
+import sys
+import xml.parsers.expat
+import re
+
+print "Content-Type: text/html" # HTML is following
+print # blank line, end of headers
+
+
+
+# Get the basename of this script
+# co = sys._getframe().f_code
+# SELFBASE = co.co_filename
+
+
+
+print """
+<html>
+ <head>
+ <title>XSPF Validator</title>
+ <style>
+ body {
+ background-color:rgb(230,230,230);
+ margin:0px;
+ }
+
+ body, td, h1, h2, h3, h4 {
+ font-family:"Verdana";
+ }
+
+ a {
+ color:rgb(130,130,130);
+ text-decoration:none;
+ }
+
+ td.vert {
+ background-color:rgb(180,180,180);
+ font-size:1pt;
+ width:2px;
+ padding:0px;
+ }
+ td.number {
+ text-align:right;
+ padding-top:3px;
+ padding-bottom:3px;
+ padding-right:8px;
+ }
+ a.number {
+ color:#000000;
+ text-decoration:underline;
+ }
+ td.error {
+ padding-left:8px;
+ }
+ td.horz {
+ background-color:rgb(180,180,180);
+ font-size:1px;
+ height:2px;
+ padding:0px;
+ line-height:1px;
+ }
+ span.invalid {
+ text-transform:uppercase;
+ padding-left:2px;
+ color:red;
+ font-weight:bold;
+ }
+ span.valid {
+ text-transform:uppercase;
+ padding-left:2px;
+ color:green;
+ font-weight:bold;
+ }
+
+ td.lineNumber {
+ background-color:rgb(222,222,222);
+ font-family:"Courier new";
+ text-align:right;
+ vertical-align:top;
+ padding-top:3px;
+ padding-bottom:3px;
+ padding-left:8px;
+ padding-right:4px;
+ }
+ td.lineVert {
+ background-color:rgb(0,0,0);
+ font-size:1pt;
+ width:1px;
+ padding:0px;
+ }
+ td.lineOdd {
+ font-family:"Courier new";
+ background-color:rgb(236,236,236);
+ padding-top:3px;
+ padding-bottom:3px;
+ padding-left:4px;
+ padding-right:8px;
+ }
+ td.lineEven {
+ font-family:"Courier new";
+ background-color:rgb(250,250,250);
+ padding-top:3px;
+ padding-bottom:3px;
+ padding-left:4px;
+ padding-right:8px;
+ }
+ td.lineBad {
+ font-family:"Courier new";
+ background-color:rgb(255,188,188);
+ padding-top:3px;
+ padding-bottom:3px;
+ padding-left:4px;
+ padding-right:8px;
+ }
+
+ a.blackLink {
+ color:#000000;
+ text-decoration:none;
+ }
+ a.anchor {
+ color:#000000;
+ text-decoration:none;
+ }
+ </style>
+ </head>
+ <body>
+ <!-- CENTERING -->
+ <table width="100%" height="100%">
+ <tr>
+ <td align="center" valign="center" style="padding:10px">
+ <!-- BORDER -->"""
+
+
+intro = ""
+
+input = ""
+form = cgi.FieldStorage()
+if form.has_key("pasted") and form.has_key("submitPasted"):
+ input = form.getlist("pasted")[0]
+ if input != "":
+ intro = "Validating pasted text<br><br>"
+
+elif form.has_key("uploaded") and form.has_key("submitUploaded"):
+ uploaded = form["uploaded"]
+ if uploaded.file:
+ input = uploaded.file.read()
+
+ if input != "":
+ intro = "Validating uploaded file<br><b><i>" + uploaded.filename + "</i></b><br><br>"
+
+elif form.has_key("url"): ### and form.has_key("submitUrl")
+ url = form.getlist("url")[0]
+
+ try:
+ file = urllib2.urlopen(url)
+ input = file.read()
+ except ValueError:
+ intro = """<b style="color:red">Invalid URL.</b><br><br>"""
+
+ except urllib2.HTTPError:
+ # 404 or similar
+ intro = """<b style="color:red">Could not download from URL.</b><br><br>"""
+
+ if input != "":
+ intro = "Validating data from URL<br><b><i><a href=\"" + url + "\" target=\"_blank\" class=\"blackLink\">" + url + "</a></i></b><br><br>"
+
+
+
+lineHeads = [0]
+if input != "":
+ index = -1
+ while 1:
+ index = input.find("\x0A", index + 1)
+ if index == -1:
+ break
+ lineHeads.append(index + 1)
+
+
+
+
+if input == "":
+ # Formular 600
+ print """
+ <table cellpadding="0" cellspacing="0" width="600" style="border:1px solid rgb(180,180,180); background-color:white">"""
+else:
+ # Results 800
+ print """
+ <table cellpadding="0" cellspacing="0" width="750" style="border:1px solid rgb(180,180,180); background-color:white">"""
+
+print """
+ <tr>"""
+
+
+
+if input == "":
+ # Formular centered
+ print """
+ <td align="center" style="padding-top:60px; padding-bottom:60px">
+ <form action="" accept-charset="UTF-8" enctype="multipart/form-data" method="post">
+ <!-- CONTENT -->
+ <table>"""
+
+else:
+ # Results full width
+ print """
+ <td style="padding-top:60px; padding-bottom:50px">
+ <!-- CONTENT -->
+ <table width="100%">"""
+
+
+
+print """
+ <tr>
+ <td style="padding-bottom: 20px;" align="center"><img src="xspflogo-1.5.gif" width="297" height="83" border="0" /></td>
+ </tr>"""
+
+
+
+if input == "":
+ if intro != "":
+ print """
+ <tr>
+ <td width="100%" align="center">"""
+ print intro
+ print """
+ </tr>"""
+
+ # Formular
+ print """
+ <tr>
+ <td style="padding-bottom:16px">
+ Validate a Spiff playlist from ...
+ </td>
+ </tr>
+ <tr>
+ <td>
+ URL<br>
+ <input name="url" size="60" style="width: 350px;" type="text">
+ </td>
+ </tr>
+ <tr>
+ <td style="padding-bottom:20px">
+ <input name="submitUrl" value="Submit" type="submit">
+ </td>
+ </tr>
+ <tr>
+ <td>
+ Uploaded file<br>
+ <input name="uploaded" maxlength="100000" accept="text/*" size="41" type="file">
+ </td>
+ </tr>
+ <tr>
+ <td style="padding-bottom:20px">
+ <input name="submitUploaded" value="Submit" type="submit">
+ </td>
+ </tr>
+ <tr>
+ <td>
+ Pasted text<br>
+ <textarea cols="60" rows="5" name="pasted" size="60" style="width: 350px;"></textarea>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <input name="submitPasted" value="Submit" type="submit">
+ </td>
+ </tr>"""
+
+else:
+ print """
+ <tr>
+ <td style="padding-left:60px; padding-right:60px">"""
+ print intro
+
+ stack = []
+ valid = True
+ version = -1
+ accum = ""
+
+ TAG_UNKNOWN = 0
+ TAG_PLAYLIST = 1
+ TAG_PLAYLIST_TITLE = 2
+ TAG_PLAYLIST_CREATOR = 3
+ TAG_PLAYLIST_ANNOTATION = 4
+ TAG_PLAYLIST_INFO = 5
+ TAG_PLAYLIST_LOCATION = 6
+ TAG_PLAYLIST_IDENTIFIER = 7
+ TAG_PLAYLIST_IMAGE = 8
+ TAG_PLAYLIST_DATE = 9
+ TAG_PLAYLIST_LICENSE = 10
+ TAG_PLAYLIST_ATTRIBUTION = 11
+ TAG_PLAYLIST_ATTRIBUTION_LOCATION = 12
+ TAG_PLAYLIST_ATTRIBUTION_IDENTIFIER = 13
+ TAG_PLAYLIST_LINK = 14
+ TAG_PLAYLIST_META = 15
+ TAG_PLAYLIST_EXTENSION = 16
+ TAG_PLAYLIST_TRACKLIST = 17
+ TAG_PLAYLIST_TRACKLIST_TRACK = 18
+ TAG_PLAYLIST_TRACKLIST_TRACK_LOCATION = 19
+ TAG_PLAYLIST_TRACKLIST_TRACK_IDENTIFIER = 20
+ TAG_PLAYLIST_TRACKLIST_TRACK_TITLE = 21
+ TAG_PLAYLIST_TRACKLIST_TRACK_CREATOR = 22
+ TAG_PLAYLIST_TRACKLIST_TRACK_ANNOTATION = 23
+ TAG_PLAYLIST_TRACKLIST_TRACK_INFO = 24
+ TAG_PLAYLIST_TRACKLIST_TRACK_IMAGE = 25
+ TAG_PLAYLIST_TRACKLIST_TRACK_ALBUM = 26
+ TAG_PLAYLIST_TRACKLIST_TRACK_TRACKNUM = 27
+ TAG_PLAYLIST_TRACKLIST_TRACK_DURATION = 28
+ TAG_PLAYLIST_TRACKLIST_TRACK_LINK = 29
+ TAG_PLAYLIST_TRACKLIST_TRACK_META = 30
+ TAG_PLAYLIST_TRACKLIST_TRACK_EXTENSION = 31
+
+ skipAbove = -1
+
+ firstPlaylistAnnotation = True
+ firstPlaylistAttribution = True
+ firstPlaylistCreator = True
+ firstPlaylistDate = True
+ firstPlaylistIdentifier = True
+ firstPlaylistImage = True
+ firstPlaylistInfo = True
+ firstPlaylistLicense = True
+ firstPlaylistLocation = True
+ firstPlaylistTitle = True
+ firstPlaylistTrackList = True
+ firstTrackTitle = True
+ firstTrackCreator = True
+ firstTrackAnnotation = True
+ firstTrackInfo = True
+ firstTrackImage = True
+ firstTrackAlbum = True
+ firstTrackTrackNum = True
+ firstTrackDuration = True
+ firstTrack = True
+
+ uriRegex = re.compile("^[a-zA-Z0-9.+-]+:")
+ dateRegex = re.compile("^(-?\\d\\d\\d\\d)-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])T([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(\\.\\d+)?([+-](((0[0-9]|1[0-3]):[0-5][0-9])|14:00)|Z)?$")
+
+ checker = xml.parsers.expat.ParserCreate()
+
+
+
+#############################################################################################
+
+
+errorTable = ""
+
+
+def startErrorTable():
+ if globals()["errorTable"] == "":
+ globals()["errorTable"] += """
+ <h3>Error protocol:</h3>
+ <table cellspacing="0">
+ <tr><td class="number">Line</td><td class="number">Col</td><td class="vert"> </td><td class="error">Error</td></tr>
+ <tr height="1"><td class="horz"> </td><td class="horz"> </td><td class="horz"> </td><td class="horz"> </td></tr>"""
+
+
+
+# line is one-based
+def addError(line, col, error):
+ globals()["errorTable"] += "<tr><td class=\"number\"><a href=\"#bad_" + str(line) + "\" class=\"number\">" + str(line) + "</a></td><td class=\"number\">" + str(col) + "</td><td class=\"vert\"> </td><td class=\"error\">" + error + "</td></tr>"
+
+
+
+def stopErrorTable():
+ if globals()["errorTable"] != "":
+ globals()["errorTable"] += """
+ </table>
+ <br>
+ <br>"""
+
+ else:
+ globals()["errorTable"] += """
+ <br>"""
+
+
+
+sourceTable = ""
+lastLine = -1
+
+
+
+def startSourceTable():
+ if globals()["sourceTable"] == "":
+ globals()["sourceTable"] = """
+ <h3>Processed input:</h3>
+ <table cellspacing="0">"""
+
+
+
+def stopSourceTable():
+ startSourceTable()
+
+ # Add missing lines
+ LAST = globals()["lastLine"]
+ ALL = len(globals()["lineHeads"])
+ if LAST < ALL - 1:
+ moreSourceLinesIncluding(ALL - 1, False)
+ globals()["sourceTable"] += """
+ </table>
+ <br>
+ <br>"""
+
+
+# lineNumber is zero-based
+def addSourceLine(lineNumber, badFlag):
+ globals()["sourceTable"] += """
+ <tr>
+ <td class="lineNumber">""" + str(lineNumber + 1) + """</td>
+ <td class="lineVert"> </td>"""
+ if badFlag:
+ globals()["sourceTable"] += """
+ <td class="lineBad"><a name=\"bad_""" + str(lineNumber + 1) + """\" class=\"anchor\">"""
+ elif lineNumber % 2:
+ globals()["sourceTable"] += """
+ <td class="lineEven">"""
+ else:
+ globals()["sourceTable"] += """
+ <td class="lineOdd">"""
+
+ # Last line or normal?
+ if lineNumber == len(globals()["lineHeads"]) - 1:
+ line = input[lineHeads[lineNumber]:]
+ else:
+ line = input[lineHeads[lineNumber]:lineHeads[lineNumber + 1] - 1]
+
+ MAX_CHARS_PER_LINE = 100
+ line2 = line[0:MAX_CHARS_PER_LINE]
+ for i in range(MAX_CHARS_PER_LINE, len(line), MAX_CHARS_PER_LINE):
+ line2 += "\n" + line[i:i + MAX_CHARS_PER_LINE]
+ globals()["sourceTable"] += line2.replace("<", "<").replace(">", ">").replace("\t", " ").replace(" ", " ").replace("\n", "<br>")
+ if badFlag:
+ globals()["sourceTable"] += """</a>"""
+ globals()["sourceTable"] += """</td>
+ </tr>"""
+
+
+# lineNumber is zero-based
+def moreSourceLinesIncluding(lineNumber, badFlag):
+ LAST = globals()["lastLine"]
+ if lineNumber <= LAST:
+ return
+
+ for i in range(LAST + 1, lineNumber):
+ addSourceLine(i, False)
+ addSourceLine(lineNumber, badFlag)
+ globals()["lastLine"] = lineNumber
+
+
+
+def fail(text):
+ globals()["valid"] = False
+
+ startErrorTable()
+ addError(checker.CurrentLineNumber, checker.CurrentColumnNumber + 1, text)
+
+ startSourceTable()
+ moreSourceLinesIncluding(checker.CurrentLineNumber - 1, True)
+
+
+
+def handlePlaylistAttribs(atts):
+ versionFound = False
+ xmlnsFound = False
+
+ keys = atts.keys()
+ for i in range(len(atts)):
+ name = keys[i]
+ if name == "version":
+ dummyVersion = atts.values()[i]
+ if dummyVersion == "0":
+ globals()["version"] = 0
+ elif dummyVersion == "1":
+ globals()["version"] = 1
+ else:
+ fail("Version must be <i>0</i> or <i>1</i>, not '" + dummyVersion + "'.")
+ globals()["version"] = 1
+ versionFound = True
+ elif name == "xmlns":
+ namespace = atts.values()[i]
+ if namespace != "http://xspf.org/ns/0/":
+ fail("Namespace must be 'http://xspf.org/ns/0/', not '" + namespace + "'.")
+ xmlnsFound = True
+
+ if not versionFound:
+ fail("Attribute <i>version</i> missing.")
+ if not xmlnsFound:
+ fail("Attribute <i>xmlns</i> missing.")
+
+
+
+def handleNoAttribs(atts):
+ keys = atts.keys()
+ for i in range(len(atts)):
+ fail("Attribute '" + atts.values()[i] + "' not allowed.")
+
+
+
+def handleExtensionAttribs(atts):
+ size = len(atts)
+ if size == 0:
+ fail("Attribute <i>application</i> missing.")
+ else:
+ for i in range(size):
+ name = atts.keys()[i]
+ if name != "application":
+ fail("Attribute '" + name + "' not allowed.")
+ elif not isUri(atts.values()[i]):
+ fail("Attribute <i>application</i> is not a URI.")
+
+
+
+def handleMetaLinkAttribs(atts):
+ size = len(atts)
+ if size == 0:
+ fail("Attribute <i>rel</i> missing.")
+ else:
+ for i in range(size):
+ name = atts.keys()[i]
+ if name != "rel":
+ fail("Attribute '" + name + "' not allowed.")
+ elif not isUri(atts.values()[i]):
+ fail("Attribute <i>rel</i> is not a URI.")
+
+
+
+def handleStartOne(name, atts):
+ if name != "playlist":
+ # fail("Element '" + name + "' not allowed.")
+ fail("Root element must be <i>playlist</i>, not '" + name + "'.")
+ else:
+ handlePlaylistAttribs(atts)
+ globals()["stack"].append(TAG_PLAYLIST)
+
+
+
+def handleStartTwo(name, atts):
+ if name == "annotation":
+ if not globals()["firstPlaylistAnnotation"]:
+ fail("Only one <i>annotation</i> allowed for <i>playlist</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstPlaylistAnnotation"] = False
+ globals()["stack"].append(TAG_PLAYLIST_ANNOTATION)
+
+ elif name == "attribution":
+ if not globals()["firstPlaylistAttribution"]:
+ fail("Only one <i>attribution</i> allowed for <i>playlist</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstPlaylistAttribution"] = False
+ globals()["stack"].append(TAG_PLAYLIST_ATTRIBUTION)
+
+ elif name == "creator":
+ if not globals()["firstPlaylistCreator"]:
+ fail("Only one <i>creator</i> allowed for <i>playlist</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstPlaylistCreator"] = False
+ globals()["stack"].append(TAG_PLAYLIST_CREATOR)
+
+ elif name == "date":
+ if not globals()["firstPlaylistDate"]:
+ fail("Only one <i>date</i> allowed for <i>playlist</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstPlaylistDate"] = False
+ globals()["stack"].append(TAG_PLAYLIST_DATE)
+
+ elif name == "extension":
+ if globals()["version"] == 0:
+ fail("Element <i>" + name + "</i> not allowed in XSPF-0.")
+ else:
+ handleExtensionAttribs(atts)
+ globals()["stack"].append(TAG_PLAYLIST_EXTENSION)
+ # Skip extension body
+ globals()["skipAbove"] = 2
+
+ elif name == "identifier":
+ if not globals()["firstPlaylistIdentifier"]:
+ fail("Only one <i>identifier</i> allowed for <i>playlist</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstPlaylistIdentifier"] = False
+ globals()["stack"].append(TAG_PLAYLIST_IDENTIFIER)
+
+ elif name == "image":
+ if not globals()["firstPlaylistImage"]:
+ fail("Only one <i>image</i> allowed for <i>playlist</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstPlaylistImage"] = False
+ globals()["stack"].append(TAG_PLAYLIST_IMAGE)
+
+ elif name == "info":
+ if not globals()["firstPlaylistInfo"]:
+ fail("Only one <i>info</i> allowed for <i>playlist</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstPlaylistInfo"] = False
+ globals()["stack"].append(TAG_PLAYLIST_INFO)
+
+ elif name == "license":
+ if not globals()["firstPlaylistLicense"]:
+ fail("Only one <i>license</i> allowed for <i>playlist</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstPlaylistLicense"] = False
+ globals()["stack"].append(TAG_PLAYLIST_LICENSE)
+
+ elif name == "link":
+ handleMetaLinkAttribs(atts)
+ globals()["stack"].append(TAG_PLAYLIST_LINK)
+
+ elif name == "location":
+ if not globals()["firstPlaylistLocation"]:
+ fail("Only one <i>location</i> allowed for <i>playlist</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstPlaylistLocation"] = False
+ globals()["stack"].append(TAG_PLAYLIST_LOCATION)
+
+ elif name == "meta":
+ handleMetaLinkAttribs(atts)
+ globals()["stack"].append(TAG_PLAYLIST_META)
+
+ elif name == "title":
+ if not globals()["firstPlaylistTitle"]:
+ fail("Only one <i>title</i> allowed for <i>playlist</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstPlaylistTitle"] = False
+ globals()["stack"].append(TAG_PLAYLIST_TITLE)
+
+ elif name == "trackList":
+ globals()["firstPlaylistTrackList"]
+ if not globals()["firstPlaylistTrackList"]:
+ fail("Only one <i>trackList</i> allowed for <i>playlist</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstPlaylistTrackList"] = False
+ globals()["stack"].append(TAG_PLAYLIST_TRACKLIST)
+
+ else:
+ fail("Element <i>" + name + "</i> not allowed.")
+ globals()["stack"].append(TAG_UNKNOWN)
+ # Skip body of forbidden element
+# globals()["skipAbove"]
+ globals()["skipAbove"] = 2
+
+
+
+def handleStartThree(name, atts):
+ stackTop = globals()["stack"][len(globals()["stack"]) - 1]
+ if stackTop == TAG_PLAYLIST_ATTRIBUTION:
+ if name == "identifier":
+ handleNoAttribs(atts)
+ globals()["stack"].append(TAG_PLAYLIST_ATTRIBUTION_IDENTIFIER)
+
+ elif name == "location":
+ handleNoAttribs(atts)
+ globals()["stack"].append(TAG_PLAYLIST_ATTRIBUTION_IDENTIFIER)
+
+ else:
+ fail("Element <i>" + name + "</i> not allowed.")
+ globals()["stack"].append(TAG_UNKNOWN)
+ # Skip body of forbidden element
+# globals()["skipAbove"]
+ globals()["skipAbove"] = 3
+
+ elif stackTop == TAG_PLAYLIST_TRACKLIST:
+ if name == "track":
+ handleNoAttribs(atts)
+ globals()["stack"].append(TAG_PLAYLIST_TRACKLIST_TRACK)
+
+ else:
+ fail("Element <i>" + name + "</i> not allowed.")
+ globals()["stack"].append(TAG_UNKNOWN)
+ # Skip body of forbidden element
+ globals()["skipAbove"] = 3
+
+ globals()["firstTrack"] = False
+
+ else:
+ fail("Element <i>" + name + "</i> not allowed.")
+ globals()["stack"].append(TAG_UNKNOWN)
+ # Skip body of forbidden element
+# globals()["skipAbove"]
+ globals()["skipAbove"] = 3
+
+
+
+def handleStartFour(name, atts):
+ if name == "album":
+ if not globals()["firstTrackAlbum"]:
+ fail("Only one <i>album</i> allowed for <i>track</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstTrackAlbum"] = False
+ globals()["stack"].append(TAG_PLAYLIST_TRACKLIST_TRACK_ALBUM)
+
+ elif name == "annotation":
+ if not globals()["firstTrackAnnotation"]:
+ fail("Only one <i>annotation</i> allowed for <i>track</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstTrackAnnotation"] = False
+ globals()["stack"].append(TAG_PLAYLIST_TRACKLIST_TRACK_ANNOTATION)
+
+ elif name == "creator":
+ if not globals()["firstTrackCreator"]:
+ fail("Only one <i>creator</i> allowed for <i>track</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstTrackCreator"] = False
+ globals()["stack"].append(TAG_PLAYLIST_TRACKLIST_TRACK_CREATOR)
+
+ elif name == "duration":
+ if not globals()["firstTrackDuration"]:
+ fail("Only one <i>duration</i> allowed for <i>track</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstTrackDuration"] = False
+ globals()["stack"].append(TAG_PLAYLIST_TRACKLIST_TRACK_DURATION)
+
+ elif name == "extension":
+ if globals()["version"] == 0:
+ fail("Element <i>" + name + "</i> not allowed in XSPF-0.")
+ else:
+ handleExtensionAttribs(atts)
+ globals()["stack"].append(TAG_PLAYLIST_TRACKLIST_TRACK_EXTENSION)
+ # Skip extension body
+ globals()["skipAbove"] = 4
+
+ elif name == "identifier":
+ handleNoAttribs(atts)
+ globals()["stack"].append(TAG_PLAYLIST_TRACKLIST_TRACK_IDENTIFIER)
+
+ elif name == "image":
+ if not globals()["firstTrackImage"]:
+ fail("Only one <i>image</i> allowed for <i>track</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstTrackImage"] = False
+ globals()["stack"].append(TAG_PLAYLIST_TRACKLIST_TRACK_IMAGE)
+
+ elif name == "info":
+ if not globals()["firstTrackInfo"]:
+ fail("Only one <i>info</i> allowed for <i>track</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstTrackInfo"] = False
+ globals()["stack"].append(TAG_PLAYLIST_TRACKLIST_TRACK_INFO)
+
+ elif name == "link":
+ handleMetaLinkAttribs(atts)
+ globals()["stack"].append(TAG_PLAYLIST_TRACKLIST_TRACK_LINK)
+
+ elif name == "location":
+ handleNoAttribs(atts)
+ globals()["stack"].append(TAG_PLAYLIST_TRACKLIST_TRACK_LOCATION)
+
+ elif name == "meta":
+ handleMetaLinkAttribs(atts)
+ globals()["stack"].append(TAG_PLAYLIST_TRACKLIST_TRACK_META)
+
+ elif name == "trackNum":
+ if not globals()["firstTrackTrackNum"]:
+ fail("Only one <i>trackNum</i> allowed for <i>track</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstTrackTrackNum"] = False
+ globals()["stack"].append(TAG_PLAYLIST_TRACKLIST_TRACK_TRACKNUM)
+
+ elif name == "title":
+ if not globals()["firstTrackTitle"]:
+ fail("Only one <i>title</i> allowed for <i>track</i>.")
+ else:
+ handleNoAttribs(atts)
+ globals()["firstTrackTitle"] = False
+ globals()["stack"].append(TAG_PLAYLIST_TRACKLIST_TRACK_TITLE)
+
+ else:
+ fail("Element <i>" + name + "</i> not allowed.")
+ globals()["stack"].append(TAG_UNKNOWN)
+ # Skip body of forbidden element
+ globals()["skipAbove"] = 4
+
+
+
+def handleStart(name, atts):
+ newLevel = len(globals()["stack"]) + 1
+ if (globals()["skipAbove"] != -1) and (newLevel > globals()["skipAbove"]):
+ globals()["stack"].append(TAG_UNKNOWN)
+ return
+
+ if newLevel == 1:
+ handleStartOne(name, atts)
+ elif newLevel == 2:
+ handleStartTwo(name, atts)
+ elif newLevel == 3:
+ handleStartThree(name, atts)
+ elif newLevel == 4:
+ handleStartFour(name, atts)
+ else:
+ fail("Element <i>" + name + "</i> not allowed.")
+ globals()["stack"].append(TAG_UNKNOWN)
+ # Skip body of forbidden element
+ globals()["skipAbove"] = 4
+
+
+
+def handleEndOne(name):
+ if globals()["firstPlaylistTrackList"]:
+ fail("Element <i>trackList</i> missing.")
+
+
+
+def handleEndTwo(name):
+ stackTop = globals()["stack"][len(globals()["stack"]) - 1]
+ if stackTop == TAG_PLAYLIST_DATE:
+ if not isDateTime(globals()["accum"]):
+ fail("Content of <i>date</i> is not a dateTime.")
+
+ elif stackTop == TAG_PLAYLIST_IDENTIFIER:
+ if not isUri(globals()["accum"]):
+ fail("Content of <i>identifier</i> is not a URI.")
+
+ elif stackTop == TAG_PLAYLIST_IMAGE:
+ if not isUri(globals()["accum"]):
+ fail("Content of <i>image</i> is not a URI.")
+
+ elif stackTop == TAG_PLAYLIST_INFO:
+ if not isUri(globals()["accum"]):
+ fail("Content of <i>info</i> is not a URI.")
+
+ elif stackTop == TAG_PLAYLIST_LICENSE:
+ if not isUri(globals()["accum"]):
+ fail("Content of <i>license</i> is not a URI.")
+
+ elif stackTop == TAG_PLAYLIST_LINK:
+ if not isUri(globals()["accum"]):
+ fail("Content of <i>link</i> is not a URI.")
+
+ elif stackTop == TAG_PLAYLIST_LOCATION:
+ if not isUri(globals()["accum"]):
+ fail("Content of <i>location</i> is not a URI.")
+
+ elif stackTop == TAG_PLAYLIST_TRACKLIST:
+ if (globals()["version"] == 0) and (globals()["firstTrack"]):
+ fail("Element <i>track</i> missing. This is not allowed in XSPF-0.")
+
+ globals()["accum"] = ""
+
+
+
+def handleEndThree(name):
+ stackTop = globals()["stack"][len(globals()["stack"]) - 1]
+ if stackTop == TAG_PLAYLIST_ATTRIBUTION_IDENTIFIER:
+ if not isUri(globals()["accum"]):
+ fail("Content of <i>identifier</i> is not a URI.")
+
+ elif stackTop == TAG_PLAYLIST_ATTRIBUTION_LOCATION:
+ if not isUri(globals()["accum"]):
+ fail("Content of <i>location</i> is not a URI.")
+
+ elif stackTop == TAG_PLAYLIST_TRACKLIST_TRACK:
+ globals()["firstTrackTitle"] = True
+ globals()["firstTrackCreator"] = True
+ globals()["firstTrackAnnotation"] = True
+ globals()["firstTrackInfo"] = True
+ globals()["firstTrackImage"] = True
+ globals()["firstTrackAlbum"] = True
+ globals()["firstTrackTrackNum"] = True
+ globals()["firstTrackDuration"] = True
+
+ globals()["accum"] = ""
+
+
+
+def handleEndFour(name):
+ stackTop = globals()["stack"][len(globals()["stack"]) - 1]
+ if stackTop == TAG_PLAYLIST_TRACKLIST_TRACK_DURATION:
+ if not globals()["accum"].isdigit():
+ fail("Content of <i>duration</i> is not an unsigned integer.")
+
+ elif stackTop == TAG_PLAYLIST_TRACKLIST_TRACK_IDENTIFIER:
+ if not isUri(globals()["accum"]):
+ fail("Content of <i>identifier</i> is not a URI.")
+
+ elif stackTop == TAG_PLAYLIST_TRACKLIST_TRACK_IMAGE:
+ if not isUri(globals()["accum"]):
+ fail("Content of <i>image</i> is not a URI.")
+
+ elif stackTop == TAG_PLAYLIST_TRACKLIST_TRACK_INFO:
+ if not isUri(globals()["accum"]):
+ fail("Content of <i>info</i> is not a URI.")
+
+ elif stackTop == TAG_PLAYLIST_TRACKLIST_TRACK_LINK:
+ if not isUri(globals()["accum"]):
+ fail("Content of <i>link</i> is not a URI.")
+
+ elif stackTop == TAG_PLAYLIST_TRACKLIST_TRACK_LOCATION:
+ if not isUri(globals()["accum"]):
+ fail("Content of <i>location</i> is not a URI.")
+
+ elif stackTop == TAG_PLAYLIST_TRACKLIST_TRACK_TRACKNUM:
+ if not globals()["accum"].isdigit() or (globals()["accum"] == "0"):
+ fail("Content of <i>trackNum</i> is not an unsigned integer greater zero.")
+
+ globals()["accum"] = ""
+
+
+
+def handleEnd(name):
+ level = len(globals()["stack"])
+ if globals()["skipAbove"] == level:
+ globals()["skipAbove"] = -1
+ elif globals()["skipAbove"] > level:
+ globals()["stack"].pop()
+ return
+
+ if level == 1:
+ handleEndOne(name)
+ elif level == 2:
+ handleEndTwo(name)
+ elif level == 3:
+ handleEndThree(name)
+ elif level == 4:
+ handleEndFour(name)
+
+ globals()["stack"].pop()
+
+
+
+def handleCharacters(s):
+ level = len(globals()["stack"])
+ if (globals()["skipAbove"] != -1) and (level > globals()["skipAbove"]):
+ return
+
+ stackTop = stack[len(stack) - 1]
+ if level == 1:
+ if stackTop == TAG_PLAYLIST:
+ if s.strip() != "":
+ fail("No character data allowed")
+
+ elif level == 2:
+ if stackTop in [TAG_PLAYLIST_TRACKLIST, TAG_PLAYLIST_ATTRIBUTION]:
+ if s.strip() != "":
+ fail("No character data allowed")
+
+ else:
+ globals()["accum"] += s
+
+ elif level == 3:
+ if (stackTop == TAG_PLAYLIST_TRACKLIST_TRACK):
+ if s.strip() != "":
+ fail("No character data allowed")
+
+ else:
+ globals()["accum"] += s
+
+ elif level == 4:
+ globals()["accum"] += s
+
+ else:
+ globals()["accum"] += s
+
+
+
+def isUri(text):
+ return True # 2007-01-20, TODO, OLD: globals()["uriRegex"].match(text)
+
+
+
+def isDateTime(text):
+ match = globals()["dateRegex"].match(text)
+ if not match:
+ return False
+
+ # Year- and month-specific day check
+ year = int(match.group(1))
+ month = int(match.group(2))
+ day = int(match.group(3))
+ if month == 2:
+ if day in [30, 31]:
+ return False
+
+ elif day == 29:
+ if (((year % 400) != 0) and (((year % 4) != 0) or ((year % 100) == 0))):
+ # Not a leap year
+ return False;
+
+ elif month in [4, 6, 9, 11]:
+ if day > 30:
+ return False
+
+ return True
+
+
+
+#############################################################################################
+
+
+
+if input != "":
+ # Results
+ checker.StartElementHandler = handleStart
+ checker.EndElementHandler = handleEnd
+ checker.CharacterDataHandler = handleCharacters
+
+ try:
+ checker.Parse(input, 1)
+ except xml.parsers.expat.ExpatError:
+ errorLineOneBased = checker.ErrorLineNumber
+ startErrorTable()
+ addError(errorLineOneBased, checker.ErrorColumnNumber, "Invalid XML")
+ startSourceTable()
+ moreSourceLinesIncluding(errorLineOneBased - 1, True)
+ valid = False
+
+
+ print """
+ <h3>Result:</h3>"""
+
+ if valid:
+ print """
+ <span class="valid">Valid</span>"""
+ else:
+ print """
+ <span class="invalid">Invalid</span>"""
+
+
+
+ print """
+ <br>
+ <br>"""
+
+
+ stopErrorTable()
+ print errorTable
+
+ stopSourceTable()
+ print sourceTable
+
+
+
+ print """
+ </td>
+ </tr>
+ </table>"""
+
+else:
+ # Formular
+ print """
+ </table>
+ </form>"""
+
+
+
+print """
+ </td>
+ </tr>
+ <tr>
+ <td align="center" style="padding-left:6px; padding-right:6px">
+ <table cellpadding="0" cellspacing="0" width="100%" height="1" style="background-color:rgb(180,180,180)">
+ <tr>
+ <td style="font-size:1px; line-height:1px"> </td>
+ </tr>
+ </table>
+ </td>
+ <tr>
+ <tr>
+ <td align="center" style="font-size:9pt; padding-top:2px; padding-bottom:5px">
+ <a href="http://www.hartwork.org/" target="_blank">Hartwork Project</a>
+ <a href="http://libspiff.sourceforge.net/" target="_blank">libSpiff</a>
+ <a href="http://www.xspf.org/specs/" target="_blank">XSPF Spec</a>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </body>
+</html>"""
Property changes on: websites/validator.xspf.org/check.py
___________________________________________________________________
Name: svn:executable
+ *
Added: websites/validator.xspf.org/xspflogo-1.5.gif
===================================================================
(Binary files differ)
Property changes on: websites/validator.xspf.org/xspflogo-1.5.gif
___________________________________________________________________
Name: svn:executable
+ *
Name: svn:mime-type
+ application/octet-stream
More information about the commits
mailing list