jellyfin / jellyfin-plugin-dlna

GNU General Public License v3.0
13 stars 11 forks source link

Error parsing `MediaStreamProtocol` in custom profile XML #52

Open gabrielsoldani opened 3 months ago

gabrielsoldani commented 3 months ago

After upgrading Jellyfin to 10.9.z and migrating to the DLNA plugin, I've copied my /config/dlna/user directory over to /plugins/configurations/dlna/user and I'm running into issues with one of my profiles (created using the Web interface in 10.8.z).

This is my profile:

<?xml version="1.0" encoding="utf-8"?>
<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Name>The Frame 2023</Name>
  <Identification>
    <FriendlyName />
    <ModelNumber />
    <SerialNumber />
    <ModelName />
    <ModelDescription />
    <ModelUrl>samsung.com</ModelUrl>
    <Manufacturer />
    <ManufacturerUrl />
    <Headers>
      <HttpHeaderInfo name="User-Agent" value="SEC_" match="Substring" />
    </Headers>
  </Identification>
  <FriendlyName />
  <Manufacturer>Jellyfin</Manufacturer>
  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
  <ModelName>Jellyfin Server</ModelName>
  <ModelNumber>01</ModelNumber>
  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
  <SerialNumber />
  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
  <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit>
  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
  <UserId />
  <AlbumArtPn>JPEG_SM</AlbumArtPn>
  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
  <MaxIconWidth>48</MaxIconWidth>
  <MaxIconHeight>48</MaxIconHeight>
  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
  <MaxStaticBitrate>140000000</MaxStaticBitrate>
  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
  <MaxStaticMusicBitrate>8000000</MaxStaticMusicBitrate>
  <SonyAggregationFlags />
  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
  <XmlRootAttributes>
    <XmlAttribute name="xmlns:sec" value="http://www.sec.co.kr/" />
  </XmlRootAttributes>
  <DirectPlayProfiles>
    <DirectPlayProfile container="avi,matroska,mkv,asf,mp4,3gp,mov,flv,mpeg,vob,mpegts" audioCodec="ac3,pcm,aac,aac_he,wmav2,wmapro,eac3,mp3,ac4,opus,pcm_alaw,pcm_mulaw" videoCodec="h264,hevc,mjpeg,mpeg4,wmv3,mpeg2video,mpeg1video,msmpeg4v1,msmpeg4v2,msmpeg4v3,wmv1,wmv2,h263,vp6,av1" type="Video" />
    <DirectPlayProfile container="webm" audioCodec="vorbis" videoCodec="vp9,vp8,av1" type="Video" />
    <DirectPlayProfile container="rmvb" audioCodec="cook" videoCodec="rv30,rv40" type="Video" />
    <DirectPlayProfile container="mp3,flac" type="Audio" />
    <DirectPlayProfile container="jpeg" type="Photo" />
  </DirectPlayProfiles>
  <TranscodingProfiles>
    <TranscodingProfile container="mp3" type="Audio" videoCodec="" audioCodec="mp3" protocol="">
      <Conditions />
    </TranscodingProfile>
    <TranscodingProfile container="ts" type="Video" videoCodec="hevc" audioCodec="ac3" protocol="Http">
      <Conditions />
    </TranscodingProfile>
    <TranscodingProfile container="jpeg" type="Photo" videoCodec="" audioCodec="" protocol="">
      <Conditions />
    </TranscodingProfile>
  </TranscodingProfiles>
  <ContainerProfiles>
    <ContainerProfile type="Photo" container="">
      <Conditions>
        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
      </Conditions>
    </ContainerProfile>
  </ContainerProfiles>
  <CodecProfiles />
  <ResponseProfiles>
    <ResponseProfile container="avi" type="Video" mimeType="video/x-msvideo">
      <Conditions />
    </ResponseProfile>
    <ResponseProfile container="mkv" type="Video" mimeType="video/x-mkv">
      <Conditions />
    </ResponseProfile>
    <ResponseProfile container="flac" type="Audio" mimeType="audio/x-flac">
      <Conditions />
    </ResponseProfile>
    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
      <Conditions />
    </ResponseProfile>
  </ResponseProfiles>
  <SubtitleProfiles>
    <SubtitleProfile format="srt" method="Embed" />
    <SubtitleProfile format="srt" method="External" didlMode="CaptionInfoEx" />
  </SubtitleProfiles>
</Profile>

First, I ran into this issue:

[2024-05-26 21:45:23.804 +00:00] [ERR] [49] Jellyfin.Plugin.Dlna.DlnaManager: Error parsing profile file: "/config/plugins/configurations/dlna/user/The Frame 2023.xml"
System.InvalidOperationException: There is an error in XML document (53, 88).
 ---> System.InvalidOperationException: Instance validation error: 'Http' is not a valid value for MediaStreamProtocol.
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderDlnaDeviceProfile.Read5_MediaStreamProtocol(String s)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderDlnaDeviceProfile.Read11_TranscodingProfile(Boolean isNullable, Boolean checkType)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderDlnaDeviceProfile.Read23_DlnaDeviceProfile(Boolean isNullable, Boolean checkType)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderDlnaDeviceProfile.Read24_Profile()
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader)
   at Emby.Server.Implementations.Serialization.MyXmlSerializer.DeserializeFromStream(Type type, Stream stream)
   at Emby.Server.Implementations.Serialization.MyXmlSerializer.DeserializeFromFile(Type type, String file)
   at Jellyfin.Plugin.Dlna.DlnaManager.ParseProfileFile(String path, DeviceProfileType type)

In the TranscodingProfiles section of the profile, there are problematic TranscodingProfile entries with protocol attribute set to an empty string. The 10.8.z interface for creating profiles generated profiles with such entries when the protocol input field is empty (rather than omitting the protocol XML attribute), so these should be accepted for backwards compatibility.

As a workaround, I tried editing the profile to omit the protocol attribute (and other empty options):

...
  <TranscodingProfiles>
    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3">
      <Conditions />
    </TranscodingProfile>
    <TranscodingProfile container="ts" type="Video" videoCodec="hevc" audioCodec="ac3" protocol="Http">
      <Conditions />
    </TranscodingProfile>
    <TranscodingProfile container="jpeg" type="Photo">
      <Conditions />
    </TranscodingProfile>
  </TranscodingProfiles>
...

But this triggered a different error:

[2024-05-26 21:45:23.804 +00:00] [ERR] [49] Jellyfin.Plugin.Dlna.DlnaManager: Error parsing profile file: "/config/plugins/configurations/dlna/user/The Frame 2023.xml"
System.InvalidOperationException: There is an error in XML document (53, 88).
 ---> System.InvalidOperationException: Instance validation error: 'Http' is not a valid value for MediaStreamProtocol.
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderDlnaDeviceProfile.Read5_MediaStreamProtocol(String s)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderDlnaDeviceProfile.Read11_TranscodingProfile(Boolean isNullable, Boolean checkType)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderDlnaDeviceProfile.Read23_DlnaDeviceProfile(Boolean isNullable, Boolean checkType)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderDlnaDeviceProfile.Read24_Profile()
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader)
   at Emby.Server.Implementations.Serialization.MyXmlSerializer.DeserializeFromStream(Type type, Stream stream)
   at Emby.Server.Implementations.Serialization.MyXmlSerializer.DeserializeFromFile(Type type, String file)
   at Jellyfin.Plugin.Dlna.DlnaManager.ParseProfileFile(String path, DeviceProfileType type)

It seems like protocol is expecting a lower-case "http" instead of the capitalized "Http". The MediaServerProtocol enum (defined here) has lower-case members since jellyfin/jellyfin#11126, but before that, it had capitalized members when it was introduced in jellyfin/jellyfin#10153.

As a second workaround, I've edited the profile to change protocol="Http" to protocol="http" and it worked.

Since these profiles were generated by stable versions of Jellyfin, they probably should be supported for backwards compatibility.

disgustipated commented 2 months ago

You might want to manually edit your custom profiles for now. I've been digging through the code to see if im capable of adding profile editing functionality back in and it looks like a significant undertaking for myself, might be do-able though since some of the controls still exist in the plugin code.

spali commented 2 months ago

had a similar error with the protocol, but my file contained and empty protocol="" attribute which wasn't valid either. Removing the entire attribute manually helped.