| 1 | -- Copyright (C) 2011-2015 Anton Burdinuk
|
|---|
| 2 | -- clark15b@gmail.com
|
|---|
| 3 | -- https://tsdemuxer.googlecode.com/svn/trunk/xupnpd
|
|---|
| 4 |
|
|---|
| 5 | services={}
|
|---|
| 6 | services.cds={}
|
|---|
| 7 | services.cms={}
|
|---|
| 8 | services.msr={}
|
|---|
| 9 |
|
|---|
| 10 | function playlist_item_type(pls)
|
|---|
| 11 | local mtype=mime[pls.type]
|
|---|
| 12 | local extras=nil
|
|---|
| 13 |
|
|---|
| 14 | if cfg.dlna_extras==false then
|
|---|
| 15 | extras='*'
|
|---|
| 16 | else
|
|---|
| 17 | if pls.dlna_extras then extras=dlna_org_extras[pls.dlna_extras] end
|
|---|
| 18 | if not extras then extras=mtype[5] end
|
|---|
| 19 | if pls.path and extras~='*' then extras=string.gsub(extras,'DLNA.ORG_OP=%d%d','DLNA.ORG_OP=01') end
|
|---|
| 20 | end
|
|---|
| 21 |
|
|---|
| 22 | return mtype,extras
|
|---|
| 23 | end
|
|---|
| 24 |
|
|---|
| 25 | function playlist_get_url(pls)
|
|---|
| 26 | local url=pls.url
|
|---|
| 27 | if pls.path then
|
|---|
| 28 | url=string.format('%s/stream/%s.%s',www_location,pls.objid,pls.type)
|
|---|
| 29 | elseif pls.plugin then
|
|---|
| 30 | url=string.format('%s/proxy/%s.%s',www_location,pls.objid,pls.type)
|
|---|
| 31 | elseif cfg.proxy>0 then
|
|---|
| 32 | if cfg.proxy>1 or mtype[1]==2 then
|
|---|
| 33 | url=string.format('%s/proxy/%s.%s',www_location,pls.objid,pls.type)
|
|---|
| 34 | end
|
|---|
| 35 | end
|
|---|
| 36 | return url
|
|---|
| 37 | end
|
|---|
| 38 |
|
|---|
| 39 | function get_duration(n)
|
|---|
| 40 | local seconds=math.fmod(n,60) n=(n-seconds)/60
|
|---|
| 41 | local minutes=math.fmod(n,60) n=(n-minutes)/60
|
|---|
| 42 |
|
|---|
| 43 | return string.format(' duration="%.2d:%.2d:%.2d.000"',n,minutes,seconds)
|
|---|
| 44 | end
|
|---|
| 45 |
|
|---|
| 46 | function playlist_item_to_xml(id,parent_id,pls)
|
|---|
| 47 | local logo=''
|
|---|
| 48 | local sec_extras=''
|
|---|
| 49 | local objid=pls.objid
|
|---|
| 50 |
|
|---|
| 51 | if pls.logo then
|
|---|
| 52 | local l
|
|---|
| 53 |
|
|---|
| 54 | if cfg.upnp_albumart<2 then
|
|---|
| 55 | l=pls.logo
|
|---|
| 56 | else
|
|---|
| 57 | l=string.format('%s/logo/%s.jpeg',www_location,objid)
|
|---|
| 58 | end
|
|---|
| 59 |
|
|---|
| 60 | if cfg.upnp_albumart==0 or cfg.upnp_albumart==2 then
|
|---|
| 61 | logo=string.format('<upnp:albumArtURI dlna:profileID=\"JPEG_TN\">%s</upnp:albumArtURI>',l)
|
|---|
| 62 | else
|
|---|
| 63 | logo=string.format('<res protocolInfo="http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN">%s</res>',l)
|
|---|
| 64 | end
|
|---|
| 65 | end
|
|---|
| 66 |
|
|---|
| 67 | if cfg.sec_extras then
|
|---|
| 68 | if pls.path then
|
|---|
| 69 | sec_extras=string.format('<sec:CaptionInfoEx sec:type="srt">%s/sub/%s.srt</sec:CaptionInfoEx>',www_location,objid)
|
|---|
| 70 | end
|
|---|
| 71 |
|
|---|
| 72 | if pls.bookmark then
|
|---|
| 73 | sec_extras=sec_extras..string.format('<sec:dcmInfo>BM=%s</sec:dcmInfo>',pls.bookmark)
|
|---|
| 74 | end
|
|---|
| 75 | end
|
|---|
| 76 |
|
|---|
| 77 | if pls.elements then
|
|---|
| 78 | return string.format(
|
|---|
| 79 | '<container id=\"%s\" childCount=\"%i\" parentID=\"%s\" restricted=\"true\"><dc:title>%s</dc:title><upnp:class>%s</upnp:class></container>',
|
|---|
| 80 | id,pls.size or 0,parent_id,util.xmlencode(pls.name),cfg.upnp_container)
|
|---|
| 81 | else
|
|---|
| 82 | local mtype,extras=playlist_item_type(pls)
|
|---|
| 83 |
|
|---|
| 84 | local artist=''
|
|---|
| 85 | local url=pls.url or ''
|
|---|
| 86 |
|
|---|
| 87 | if cfg.upnp_artist==true and pls.parent then
|
|---|
| 88 | if mtype[1]==1 then
|
|---|
| 89 | artist=string.format('<upnp:actor>%s</upnp:actor>',util.xmlencode(pls.parent.name))
|
|---|
| 90 | elseif mtype[1]==2 then
|
|---|
| 91 | artist=string.format('<upnp:artist>%s</upnp:artist>',util.xmlencode(pls.parent.name))
|
|---|
| 92 | end
|
|---|
| 93 | end
|
|---|
| 94 |
|
|---|
| 95 | url=playlist_get_url(pls)
|
|---|
| 96 |
|
|---|
| 97 | local duration=''
|
|---|
| 98 |
|
|---|
| 99 | if(pls.duration) then duration=get_duration(tonumber(pls.duration)) end
|
|---|
| 100 |
|
|---|
| 101 | return string.format(
|
|---|
| 102 | '<item id=\"%s" parentID=\"%s\" restricted=\"true\"><dc:title>%s</dc:title><upnp:class>%s</upnp:class>%s%s<res size=\"%s\" protocolInfo=\"%s%s\"%s>%s</res>%s</item>',
|
|---|
| 103 | id,parent_id,util.xmlencode(pls.name),mtype[2],artist,logo,pls.length or 0,mtype[4],extras,duration,util.xmlencode(url),sec_extras)
|
|---|
| 104 |
|
|---|
| 105 | end
|
|---|
| 106 | end
|
|---|
| 107 |
|
|---|
| 108 | function get_playlist_item_parent(s)
|
|---|
| 109 | if s=='0' then return '-1' end
|
|---|
| 110 |
|
|---|
| 111 | local t={}
|
|---|
| 112 |
|
|---|
| 113 | for i in string.gmatch(s,'(%w+)_') do table.insert(t,i) end
|
|---|
| 114 |
|
|---|
| 115 | return table.concat(t,'_')
|
|---|
| 116 | end
|
|---|
| 117 |
|
|---|
| 118 |
|
|---|
| 119 | function xml_serialize(r)
|
|---|
| 120 | local t={}
|
|---|
| 121 |
|
|---|
| 122 | for i,j in pairs(r) do
|
|---|
| 123 | table.insert(t,'<') table.insert(t,j[1]) table.insert(t,'>')
|
|---|
| 124 | table.insert(t,j[2])
|
|---|
| 125 | table.insert(t,'</') table.insert(t,j[1]) table.insert(t,'>')
|
|---|
| 126 | end
|
|---|
| 127 |
|
|---|
| 128 | return table.concat(t)
|
|---|
| 129 | end
|
|---|
| 130 |
|
|---|
| 131 | function acl_validate(acl,ip)
|
|---|
| 132 | for i in string.gmatch(acl,'([^;]+)') do
|
|---|
| 133 | if ip==i then return true end
|
|---|
| 134 | end
|
|---|
| 135 |
|
|---|
| 136 | return false
|
|---|
| 137 | end
|
|---|
| 138 |
|
|---|
| 139 |
|
|---|
| 140 | services.cds.schema='urn:schemas-upnp-org:service:ContentDirectory:1'
|
|---|
| 141 |
|
|---|
| 142 | function services.cds.GetSystemUpdateID()
|
|---|
| 143 | return {{'Id',update_id}}
|
|---|
| 144 | end
|
|---|
| 145 |
|
|---|
| 146 | function services.cds.GetSortCapabilities()
|
|---|
| 147 | return {{'SortCaps','dc:title'}}
|
|---|
| 148 | end
|
|---|
| 149 |
|
|---|
| 150 | function services.cds.GetSearchCapabilities()
|
|---|
| 151 | return {{'SearchCaps','upnp:class'}}
|
|---|
| 152 | end
|
|---|
| 153 |
|
|---|
| 154 | -- wget -O - "http://127.0.0.1:4044/soap/cds?action=X_GetFeatureList"
|
|---|
| 155 | function services.cds.X_GetFeatureList()
|
|---|
| 156 | return {{'FeatureList',util.xmlencode(cfg.upnp_feature_list)}}
|
|---|
| 157 | end
|
|---|
| 158 |
|
|---|
| 159 | -- wget -O - "http://127.0.0.1:4044/soap/cds?action=X_SetBookmark&ObjectID=0_2_1&PosSecond=1234"
|
|---|
| 160 | function services.cds.X_SetBookmark(args)
|
|---|
| 161 | core.sendevent('bookmark',args.ObjectID or '',args.PosSecond or '')
|
|---|
| 162 | return {}
|
|---|
| 163 | end
|
|---|
| 164 |
|
|---|
| 165 | -- wget -O - "http://127.0.0.1:4044/soap/cds?action=Browse&ObjectID=0&StartingIndex=0&RequestedCount=100"
|
|---|
| 166 | function services.cds.Browse(args,ip)
|
|---|
| 167 | local items={}
|
|---|
| 168 | local count=0
|
|---|
| 169 | local total=0
|
|---|
| 170 |
|
|---|
| 171 | table.insert(items,'<DIDL-Lite xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0\">')
|
|---|
| 172 |
|
|---|
| 173 | local objid=args.ObjectID or args.ContainerID
|
|---|
| 174 |
|
|---|
| 175 | local pls=find_playlist_object(objid)
|
|---|
| 176 |
|
|---|
| 177 | if pls then
|
|---|
| 178 |
|
|---|
| 179 | if args.BrowseFlag=='BrowseMetadata' then
|
|---|
| 180 | table.insert(items,playlist_item_to_xml(objid,get_playlist_item_parent(objid),pls))
|
|---|
| 181 | count=1
|
|---|
| 182 | total=1
|
|---|
| 183 | else
|
|---|
| 184 | local from=tonumber(args.StartingIndex)
|
|---|
| 185 | local to=from+tonumber(args.RequestedCount)
|
|---|
| 186 |
|
|---|
| 187 | if to==from then to=from+pls.size end
|
|---|
| 188 |
|
|---|
| 189 | if pls.elements then
|
|---|
| 190 | for i,j in ipairs(pls.elements) do
|
|---|
| 191 | if i>from and i<=to then
|
|---|
| 192 | if not j.acl or acl_validate(j.acl,ip) then
|
|---|
| 193 | table.insert(items,playlist_item_to_xml(string.format('%s_%s',objid,i),objid,j))
|
|---|
| 194 | count=count+1
|
|---|
| 195 | end
|
|---|
| 196 | end
|
|---|
| 197 | end
|
|---|
| 198 | total=pls.size
|
|---|
| 199 | end
|
|---|
| 200 | end
|
|---|
| 201 |
|
|---|
| 202 | end
|
|---|
| 203 |
|
|---|
| 204 | table.insert(items,'</DIDL-Lite>')
|
|---|
| 205 |
|
|---|
| 206 | return {{'Result',util.xmlencode(table.concat(items))}, {'NumberReturned',count}, {'TotalMatches',total}, {'UpdateID',update_id}}
|
|---|
| 207 |
|
|---|
| 208 | end
|
|---|
| 209 |
|
|---|
| 210 | -- wget -O - "http://127.0.0.1:4044/soap/cds?action=Search&ContainerID=0&StartingIndex=0&RequestedCount=100&SearchCriteria=*"
|
|---|
| 211 | function services.cds.Search(args,ip)
|
|---|
| 212 | local items={}
|
|---|
| 213 | local count=0
|
|---|
| 214 | local total=0
|
|---|
| 215 |
|
|---|
| 216 | local from=tonumber(args.StartingIndex)
|
|---|
| 217 | local to=from+tonumber(args.RequestedCount)
|
|---|
| 218 | local what=util.upnp_search_object_type(args.SearchCriteria)
|
|---|
| 219 |
|
|---|
| 220 | if to==from then to=from+10000 end
|
|---|
| 221 |
|
|---|
| 222 | table.insert(items,'<DIDL-Lite xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0\">')
|
|---|
| 223 |
|
|---|
| 224 | local pls=find_playlist_object(args.ContainerID)
|
|---|
| 225 |
|
|---|
| 226 | if pls then
|
|---|
| 227 | function __search(id,parent_id,p)
|
|---|
| 228 | if p.elements then
|
|---|
| 229 | if not p.virtual and (not p.acl or acl_validate(p.acl,ip)) then
|
|---|
| 230 | for i,j in pairs(p.elements) do
|
|---|
| 231 | __search(string.format('%s_%s',id,i),id,j)
|
|---|
| 232 | end
|
|---|
| 233 | end
|
|---|
| 234 | else
|
|---|
| 235 | if what==0 or mime[p.type][1]==what then
|
|---|
| 236 | total=total+1
|
|---|
| 237 |
|
|---|
| 238 | if total>from and total<=to then
|
|---|
| 239 | table.insert(items,playlist_item_to_xml(id,parent_id,p))
|
|---|
| 240 | count=count+1
|
|---|
| 241 | end
|
|---|
| 242 | end
|
|---|
| 243 | end
|
|---|
| 244 | end
|
|---|
| 245 |
|
|---|
| 246 | __search(args.ContainerID,get_playlist_item_parent(args.ContainerID),pls)
|
|---|
| 247 | end
|
|---|
| 248 |
|
|---|
| 249 | table.insert(items,'</DIDL-Lite>')
|
|---|
| 250 |
|
|---|
| 251 | return {{'Result',util.xmlencode(table.concat(items))}, {'NumberReturned',count}, {'TotalMatches',total}, {'UpdateID',update_id}}
|
|---|
| 252 |
|
|---|
| 253 | end
|
|---|
| 254 |
|
|---|
| 255 |
|
|---|
| 256 | services.cms.schema='urn:schemas-upnp-org:service:ConnectionManager:1'
|
|---|
| 257 |
|
|---|
| 258 | function services.cms.GetCurrentConnectionInfo(args)
|
|---|
| 259 | return {{'ConnectionID',0}, {'RcsID',-1}, {'AVTransportID',-1}, {'ProtocolInfo',''},
|
|---|
| 260 | {'PeerConnectionManager',''}, {'PeerConnectionID',-1}, {'Direction','Output'}, {'Status','OK'}}
|
|---|
| 261 | end
|
|---|
| 262 |
|
|---|
| 263 | function services.cms.GetProtocolInfo()
|
|---|
| 264 | local protocols={}
|
|---|
| 265 |
|
|---|
| 266 | for i,j in pairs(upnp_proto) do table.insert(protocols,j..'*') end
|
|---|
| 267 |
|
|---|
| 268 | return {{'Sink',''}, {'Source',table.concat(protocols,',')}}
|
|---|
| 269 | end
|
|---|
| 270 |
|
|---|
| 271 | function services.cms.GetCurrentConnectionIDs()
|
|---|
| 272 | return {{'ConnectionIDs',''}}
|
|---|
| 273 | end
|
|---|
| 274 |
|
|---|
| 275 |
|
|---|
| 276 | services.msr.schema='urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1'
|
|---|
| 277 |
|
|---|
| 278 | function services.msr.IsAuthorized(args)
|
|---|
| 279 | return {{'Result',1}}
|
|---|
| 280 | end
|
|---|
| 281 |
|
|---|
| 282 | function services.msr.RegisterDevice(args)
|
|---|
| 283 | return nil
|
|---|
| 284 | end
|
|---|
| 285 |
|
|---|
| 286 | function services.msr.IsValidated(args)
|
|---|
| 287 | return {{'Result',1}}
|
|---|
| 288 | end
|
|---|