karlheyes / icecast-kh

KH branch of icecast
GNU General Public License v2.0
298 stars 106 forks source link

XML Parsing Error #317

Closed miluxmilux closed 4 years ago

miluxmilux commented 4 years ago

Hi there, I am in the process of switching from icecast to icecast-kh. So far everything is working well. I just redirected a stream containing an ö in the title and I get an XML Parsing Error: not well-formed when I visit the icecast page in the browser. Is this some known bug? I can't seem to see a difference between my old normal icecast (which was working fine with the ö) and the -kh version. I compiled from source with libxslt1-dev (1.1.32), libxml2-dev (2.9.4) and icecast-kh-icecast-2.4.0-kh14. Any idea why this is happening? ö

karlheyes commented 4 years ago

Not much to go on. The first issue may be a charset related thing. When using non-ascii characters then utf8 is best used but if it is something else then it needs to be made clear so that an internal conversion to utf8 can be done. This is because a shared medium like a web page or the directory needs to have that consistency. For metadata updates it was consider best to use latin1 but there was never any clear spec for that sort of thing, mainly because of a limited range for facility. UTF8 is assumed now as it is common enough but you can specify charset tags in the xml or as a parameter to the metadata updating.

The latter display issue may be a fallout from that, depending on the character sets involved but the xsl may be a factor, hard to tell from the sample, it's obviously not the one shipped by me, but not necessarily incorrect.

karl.

miluxmilux commented 4 years ago

Hey karl, thanks for your fast response. Sorry that I didn't provide more info. Since I just swapped between icecast and icecast-kh, I first wanted to know if you were aware of something that could cause this in your version. As this is not the case, I'll check further. I'm just not sure where the problem could be. All the .xsl and .xslt contain the encoding="utf-8" attribute somewhere at the beginning, no changes were made there.

This is my status.xsl, a mix between your version and the icecast one I think. This is the file causing the problem.

<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0">
<xsl:output omit-xml-declaration="no" method="xml" 
      doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" 
      doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" 
      indent="yes" encoding="UTF-8" />
<xsl:template match = "/icestats">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Icecast Streaming Server</title>
    <link rel="stylesheet" type="text/css" href="style.css" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
</head>
<body>
    <h1 id="header">Icecast2 Status</h1>
    <!--index header menu -->
    <div id="menu">
        <ul>
            <li><a href="admin/stats.xsl">Administration</a></li>
            <li><a href="status.xsl">Server Status</a></li>
            <li><a href="server_version.xsl">Version</a></li>
        </ul>
    </div>
    <!--end index header menu -->
    <xsl:text disable-output-escaping="yes">
    &lt;!-- WARNING:
     DO NOT ATTEMPT TO PARSE ICECAST HTML OUTPUT!
     The web interface may change completely between releases.
     If you have a need for automatic processing of server data,
     please read the appropriate documentation. Latest docs:
     http://icecast.org/docs/icecast-latest/icecast2_stats.html
    -->
    </xsl:text>
    <!--mount point stats-->
    <xsl:for-each select="source">
        <xsl:choose>
            <xsl:when test="listeners">
            <div class="roundbox">
                <div class="mounthead">
                    <h3 class="mount">Mount Point <xsl:value-of select="@mount" /></h3>
                    <div class="right">
                        <xsl:choose>
                            <xsl:when test="authenticator">
                                <a class="auth" href="/auth.xsl">Login</a>
                            </xsl:when>
                            <xsl:otherwise>
                                <ul class="mountlist">
                                    <li><a class="play" href="{@mount}.m3u">M3U</a></li>
                                    <li><a class="play" href="{@mount}.xspf">XSPF</a></li>
                                    <!-- <li><a class="play" href="{@mount}.vclt">VCLT</a></li> -->
                                </ul>
                            </xsl:otherwise>
                        </xsl:choose>
                    </div>
                </div>
                <div class="mountcont">
                    <xsl:if test="server_type and ((server_type = 'application/ogg') or (server_type = 'audio/ogg') or (server_type = 'audio/mpeg'))">
                        <div class="audioplayer">
                            <audio controls="controls" preload="none">
                                <source src="{@mount}" type="{server_type}" />
                            </audio>
                        </div>
                    </xsl:if>
                    <table class="yellowkeys">
                        <tbody>
                            <xsl:if test="server_name">
                                <tr>
                                    <td>Stream Name:</td>
                                    <td><xsl:value-of select="server_name" /></td>
                                </tr>
                            </xsl:if>
                            <xsl:if test="server_description">
                                <tr>
                                    <td>Stream Description:</td>
                                    <td><xsl:value-of select="server_description" /></td>
                                </tr>
                            </xsl:if>
                            <xsl:if test="server_type">
                                <tr>
                                    <td>Content Type:</td>
                                    <td><xsl:value-of select="server_type" /></td>
                                </tr>
                            </xsl:if>
                            <xsl:if test="stream_start">
                                <tr>
                                    <td>Stream started:</td>
                                    <td class="streamstats"><xsl:value-of select="stream_start" /></td>
                                </tr>
                            </xsl:if>
                            <xsl:if test="bitrate">
                                <tr>
                                    <td>Bitrate:</td>
                                    <td class="streamstats"><xsl:value-of select="bitrate" /></td>
                                </tr>
                            </xsl:if>
                            <xsl:if test="quality">
                                <tr>
                                    <td>Quality:</td>
                                    <td class="streamstats"><xsl:value-of select="quality" /></td>
                                </tr>
                            </xsl:if>
                            <xsl:if test="video_quality">
                                <tr>
                                    <td>Video Quality:</td>
                                    <td class="streamstats"><xsl:value-of select="video_quality" /></td>
                                </tr>
                            </xsl:if>
                            <xsl:if test="frame_size">
                                <tr>
                                    <td>Framesize:</td>
                                    <td class="streamstats"><xsl:value-of select="frame_size" /></td>
                                </tr>
                            </xsl:if>
                            <xsl:if test="frame_rate">
                                <tr>
                                    <td>Framerate:</td>
                                    <td class="streamstats"><xsl:value-of select="frame_rate" /></td>
                                </tr>
                            </xsl:if>
                            <xsl:if test="listeners">
                                <tr>
                                    <td>Listeners (current):</td>
                                    <td class="streamstats"><xsl:value-of select="listeners" /></td>
                                </tr>
                            </xsl:if>
                            <xsl:if test="listener_peak">
                                <tr>
                                    <td>Listeners (peak):</td>
                                    <td class="streamstats"><xsl:value-of select="listener_peak" /></td>
                                </tr>
                            </xsl:if>
                            <xsl:if test="genre">
                                <tr>
                                    <td>Genre:</td>
                                    <td class="streamstats"><xsl:value-of select="genre" /></td>
                                </tr>
                            </xsl:if>
                            <xsl:if test="server_url">
                                <tr>
                                    <td>Stream URL:</td>
                                    <td class="streamstats">
                                        <a href="{server_url}"><xsl:value-of select="server_url" /></a>
                                    </td>
                                </tr>
                            </xsl:if>
                            <tr>
                                <td>Currently playing:</td>
                                <td class="streamstats">
                                <xsl:if test="artist">
                                    <xsl:value-of select="artist" /> - 
                                </xsl:if>
                                    <xsl:value-of select="title" />
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
            </xsl:when>
            <xsl:otherwise>
                <h3><xsl:value-of select="@mount" /> - Not Connected</h3>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:for-each>
    <div id="footer">
             Support Icecast development at <a target="_blank" href="http://www.icecast.org">www.icecast.org</a>
    </div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

milux

karlheyes commented 4 years ago

Check to see what is being submitted to the icecast by the source, see if that is UTF8. Any translation from an xsl comes from internal stats which are expected to be stored as UTF8. If there is metadata coming in as non UTF8 and icecast is not aware of what it is then the translation of characters (which could be multibyte) will be odd and the xsl will be wrong. It may be just that you need charset latin1 in the mount block (You can actually use configure to default to a different charset if need be). Using that xsl provided, my normal streaming works with it but that is utf8

karl

miluxmilux commented 4 years ago

It seems that the source is indeed sending ISO-8859-1 and not UTF-8. If I change the charset in the status.xsl, it works as expected... When searching in the "normal" icecast repo, I found this:

Icecast 2.3.2
-----------------------------------------------------------------------------
- Character set support.
Most non-Ogg streams (eg MP3) send metadata as Latin1 but it could be in other
character sets.  As icecast uses UTF-8, we need to convert to UTF-8 so that web
pages and stream directories render correctly,
  . Allow a per-mount <charset> setting.  For when source clients do not
  indicate which character set is in use.
  . a charset= parameter to the metadata request.
  . Default for non-Ogg content is now Latin-1 (aka ISO-8859-1),  Ogg content
  still use UTF-8.

I know this is old, but I was just wondering what is the reason that this changed. If there's no other solution, I'll try to see if we can make the source send the charset= parameter.

Cheers, milux

karlheyes commented 4 years ago

The traditional way is to have the charset in the mount block, but obviously that applies to all meta arriving on that mountpoint, the charset= parameter allows for a specific update. UTF8 is a universal standard like ASCII but covers a lot more, latin1 does not, but at the time a whole series of sources/servers were made, ASCII was the only concern. If you need to make icecast assume latin1 as a default (or any other for that matter) then use ./configure --help to see the setting for changing that. Some are using portals which build the xml files automatically so do not have the option to add tags, at least that can make those cases work.

karl.

miluxmilux commented 4 years ago

The ICY_CHARSET env variable seems to be exactly what I'm looking. Thanks for your time and the detailed answer!