diff --git a/CHANGED b/CHANGED index 492b3db64..f937ea526 100644 --- a/CHANGED +++ b/CHANGED @@ -1,5 +1,6 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - feature: 98_DLNARenderer: Support players which require DIDL-Lite - bugfix: 73_km200: Bugfix for double and triple downloads - feature: 73_km200: DbLog_splitFn implemented - bugfix: 74_AMAD: fix Issues 475717 diff --git a/FHEM/98_DLNARenderer.pm b/FHEM/98_DLNARenderer.pm index 8a8f1fbea..bcbbced6f 100644 --- a/FHEM/98_DLNARenderer.pm +++ b/FHEM/98_DLNARenderer.pm @@ -1,7 +1,13 @@ ############################################################################ -# 2016-07-18, v2.0.0, dominik.karall@gmail.com +# Author: dominik.karall@gmail.com # $Id$ # +# v2.0.1 - 20160725 +# - FEATURE: support DIDL-Lite in channel_X attribute (thx@Weissbrotgrill) +# - FEATURE: automatically generate DIDL-Lite based on URI (thx@Weissbrotgrill) +# - CHANGE: update CommandRef perl library requirements +# - BUGFIX: fix ignoreUDNs crash when device gets removed +# # v2.0.0 - 20160718 # - CHANGE: first official release within fhem repository # - BUGFIX: support device events without / at the end of the xmlns (thx@MichaelT) @@ -117,6 +123,7 @@ use HTML::Entities; use XML::Simple; use Data::Dumper; use Data::UUID; +use LWP::UserAgent; #get UPnP::ControlPoint loaded properly my $gPath = ''; @@ -164,7 +171,7 @@ sub DLNARenderer_Define($$) { if(@param < 3) { #main $hash->{UDN} = 0; - my $VERSION = "v2.0.0"; + my $VERSION = "v2.0.1"; $hash->{VERSION} = $VERSION; Log3 $hash, 3, "DLNARenderer: DLNA Renderer $VERSION"; DLNARenderer_setupControlpoint($hash); @@ -310,7 +317,7 @@ sub DLNARenderer_speak { my $ttsLang = AttrVal($hash->{NAME}, "ttsLanguage", "en"); return "DLNARenderer: Maximum text length is 100 characters." if(length($ttsText) > 100); - DLNARenderer_stream($hash, "http://translate.google.com/translate_tts?tl=$ttsLang&client=tw-ob&q=$ttsText"); + DLNARenderer_stream($hash, "http://translate.google.com/translate_tts?tl=$ttsLang&client=tw-ob&q=$ttsText", ""); } sub DLNARenderer_channel { @@ -319,13 +326,30 @@ sub DLNARenderer_channel { if($stream eq "") { return "DLNARenderer: Set channel_XX attribute first."; } - DLNARenderer_stream($hash, $stream); + my $meta = ""; + if (substr($stream,0,10) eq "{"item"}{"res"}{"content"}; + }; + + if($@) { + Log3 $hash, 2, "DLNARenderer: Incorrect DIDL-Lite format, $@"; + } + } + DLNARenderer_stream($hash, $stream, $meta); readingsSingleUpdate($hash, "channel", $channelNr, 1); } sub DLNARenderer_stream { - my ($hash, $stream) = @_; - DLNARenderer_upnpSetAVTransportURI($hash, $stream); + my ($hash, $stream, $meta) = @_; + if (!defined($meta)) { + DLNARenderer_generateDidlLiteAndPlay($hash, $stream); + return undef; + } + + DLNARenderer_upnpSetAVTransportURI($hash, $stream, $meta); DLNARenderer_play($hash); readingsSingleUpdate($hash, "stream", $stream, 1); } @@ -767,8 +791,9 @@ sub DLNARenderer_upnpPause { } sub DLNARenderer_upnpSetAVTransportURI { - my ($hash, $stream) = @_; - return DLNARenderer_upnpCallAVTransport($hash, "SetAVTransportURI", 0, $stream, ""); + my ($hash, $stream, $meta) = @_; + if (!defined($meta)) { $meta = ""; } + return DLNARenderer_upnpCallAVTransport($hash, "SetAVTransportURI", 0, $stream, $meta); } sub DLNARenderer_upnpStop { @@ -1332,6 +1357,7 @@ sub DLNARenderer_addedDevice { sub DLNARenderer_removedDevice($$) { my ($hash, $device) = @_; my $deviceHash = DLNARenderer_getHashByUDN($hash, $device->UDN()); + return undef if(!defined($deviceHash)); readingsSingleUpdate($deviceHash, "presence", "offline", 1); readingsSingleUpdate($deviceHash, "state", "offline", 1); @@ -1397,6 +1423,83 @@ sub DLNARenderer_getAllDLNARenderersWithCaskeid($) { ############################### ###### UTILITY FUNCTIONS ###### ############################### +sub DLNARenderer_generateDidlLiteAndPlay { + my ($hash, $stream) = @_; + BlockingCall('DLNARenderer_generateDidlLiteBlocking', $hash->{NAME}."|".$stream, 'DLNARenderer_generateDidlLiteBlockingFinished'); + return undef; +} + +sub DLNARenderer_generateDidlLiteBlockingFinished { + my ($string) = @_; + + return unless (defined($string)); + + my ($name, $stream, $meta) = split("\\|",$string); + my $hash = $defs{$name}; + + DLNARenderer_upnpSetAVTransportURI($hash, $stream, $meta); + DLNARenderer_play($hash); + readingsSingleUpdate($hash, "stream", $stream, 1); +} + +sub DLNARenderer_generateDidlLiteBlocking { + my ($string) = @_; + my ($name, $stream) = split("\\|", $string); + my $hash = $main::defs{$name}; + my $ret = $name."|".$stream; + + if(index($stream, "http:") != 0) { + return $ret; + } + + my $ua = new LWP::UserAgent(agent => 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1'); + $ua->max_size(0); + my $resp = $ua->get($stream); + + my $didl_header = ''; + my $didl_footer = ''; + + $stream = encode_entities($stream); + + my $size = ""; + my $protocolInfo = ""; + my $album = $stream; + my $title = $stream; + my $meta = ""; + + if (defined($resp->header('content-length'))) { + $size = ' size="'.$resp->header('content-length').'"'; + } + + my @header = split /;/, $resp->header('content-type'); + my $contenttype = $header[0]; + + if (defined($resp->header('contentfeatures.dlna.org'))) { + $protocolInfo = "http-get:*:".$contenttype.":".$resp->header('contentfeatures.dlna.org'); + } else { + $protocolInfo = "http-get:*:".$contenttype.":DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01700000000000000000000000000000"; + } + + if (defined($resp->header('icy-name'))) { + $album = encode_entities($resp->header('icy-name')); + } + + if (defined($resp->header('icy-genre'))) { + $title = encode_entities($resp->header('icy-genre')); + } + + if (substr($contenttype,0,5) eq "audio" or $contenttype eq "application/ogg") { + $meta = $didl_header.'object.item.audioItem.musicTrack'.$title.''.$album.''.$stream.''.$didl_footer; + } elsif (substr($contenttype,0,5) eq "video") { + $meta = $didl_header.'object.item.videoItem'.$title.''.$album.''.$stream.''.$didl_footer; + } else { + $meta = ""; + } + $ret .= "|".$meta; + + return $ret; +} + sub DLNARenderer_newChash($$$) { my ($hash,$socket,$chash) = @_; @@ -1459,7 +1562,7 @@ sub DLNARenderer_addSocketsToMainloop { DLNARenderer automatically discovers all your MediaRenderer devices in your local network and allows you to fully control them.
It also supports multiroom audio for Caskeid and Bluetooth Caskeid speakers (e.g. MUNET).

Note: The followig libraries are required for this module: -
+ + + Attributes +