mirror of
https://github.com/DigitalDevices/octonet.git
synced 2023-10-10 11:36:52 +00:00
added octoserve
This commit is contained in:
90
octoserve/var/dms/ConnectionManager.lua
Normal file
90
octoserve/var/dms/ConnectionManager.lua
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
ConnectionManager = {}
|
||||
|
||||
function ConnectionManager:Description()
|
||||
t = ""
|
||||
local f = io.open("ConnectionManager.xml","r")
|
||||
if not f then os.exit() end
|
||||
while true do
|
||||
local line = f:read()
|
||||
if not line then break end
|
||||
t = t .. line .. "\r\n"
|
||||
end
|
||||
f:close()
|
||||
return t
|
||||
end
|
||||
|
||||
local dlnaprofile = 'DLNA.ORG_PN=MPEG_TS;DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000'
|
||||
local dlnaschema = ' xmlns:dlna="urn:schemas-dlna-org:metadata-1-0"'
|
||||
|
||||
if DisableDLNA then
|
||||
dlnaprofile = '*'
|
||||
dlnaschema = ''
|
||||
end
|
||||
|
||||
local Schema = 'xmlns:u="urn:schemas-upnp-org:service:ConnectionManager:1"'
|
||||
|
||||
|
||||
local ProtocolInfo = 'rtsp-rtp-udp:*:video/mp2t:'..dlnaprofile
|
||||
.. ','..'rtsp-rtp-udp:*:audio/mp2t:'..dlnaprofile
|
||||
-- ','..'http-get:*:video/x-ms-wmv:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000' -- TEST
|
||||
|
||||
|
||||
function ConnectionManager:Invoke(client,Attributes,Request)
|
||||
local Action = string.match(Attributes["SOAPACTION"],".+%#([%a%d%-_]+)")
|
||||
local Host = tostring(Attributes["host"])
|
||||
|
||||
print(Host,"ConnectionManager",Action)
|
||||
|
||||
if Action == "GetProtocolInfo" then
|
||||
UPnP:SendResponse(client,UPnP:CreateResponse(Schema,Action,{{ n = "Source", v = ProtocolInfo }, { n = "Sink", v = "" }}))
|
||||
elseif Action == "GetCurrentConnectionIDs" then
|
||||
UPnP:SendResponse(client,UPnP:CreateResponse(Schema,Action,{{ n = "ConnectionIDs", v = "0" }}))
|
||||
elseif Action == "GetCurrentConnectionInfo" then
|
||||
UPnP:SendSoapError(client,706)
|
||||
else
|
||||
UPnP:SendSoapError(client,401)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
function ConnectionManager:Subscribe(client,callback,timeout)
|
||||
local r = "HTTP/1.1 200 OK\r\n"
|
||||
.. 'Content-Type: text/xml; charset="utf-8"\r\n'
|
||||
.. "Server: "..UPnP.Server.."\r\n"
|
||||
.. "SID: uuid:50c95801-e839-4b96-b7ae-779d989e1399\r\n"
|
||||
.. "Timeout: Second-1800\r\n"
|
||||
.. "Content-Length: 0\r\n"
|
||||
.. "Connection: close\r\n"
|
||||
.. "EXT:\r\n"
|
||||
.. "\r\n"
|
||||
client:send(r)
|
||||
|
||||
local ipaddr,port = client:getpeername()
|
||||
local Args = { { n = "SourceProtocolInfo", v = ProtocolInfo }, { n = "SourceProtocolInfo", v = "" }, { n = "CurrentConnectionIDs", v = "0" } }
|
||||
UPnP:SendEvent(callback,"50c95801-e839-4b96-b7ae-779d989e1399",0,Args)
|
||||
end
|
||||
|
||||
function ConnectionManager:Renew(client,sid,timeout)
|
||||
local r = "HTTP/1.1 200 OK\r\n"
|
||||
.. 'Content-Type: text/xml; charset="utf-8"\r\n'
|
||||
.. "Server: "..UPnP.Server.."\r\n"
|
||||
.. "SID: uuid:50c95801-e839-4b96-b7ae-779d989e1399\r\n"
|
||||
.. "Timeout: Second-1800\r\n"
|
||||
.. "Content-Length: 0\r\n"
|
||||
.. "Connection: close\r\n"
|
||||
.. "EXT:\r\n"
|
||||
.. "\r\n"
|
||||
client:send(r)
|
||||
end
|
||||
|
||||
function ConnectionManager:Unsubscribe(client,sid)
|
||||
local r = "HTTP/1.1 200 OK\r\n"
|
||||
.. "Server: "..UPnP.Server.."\r\n"
|
||||
.. "Connection: close\r\n"
|
||||
.. "\r\n"
|
||||
client:send(r)
|
||||
end
|
||||
|
||||
return ConnectionManager
|
134
octoserve/var/dms/ConnectionManager.xml
Normal file
134
octoserve/var/dms/ConnectionManager.xml
Normal file
@@ -0,0 +1,134 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<scpd xmlns="urn:schemas-upnp-org:service-1-0">
|
||||
<specVersion>
|
||||
<major>1</major>
|
||||
<minor>0</minor>
|
||||
</specVersion>
|
||||
<actionList>
|
||||
<action>
|
||||
<name>GetProtocolInfo</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>Source</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>SourceProtocolInfo</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>Sink</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>SinkProtocolInfo</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
<action>
|
||||
<name>GetCurrentConnectionIDs</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>ConnectionIDs</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>CurrentConnectionIDs</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
<action>
|
||||
<name>GetCurrentConnectionInfo</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>ConnectionID</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_ConnectionID</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>RcsID</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_RcsID</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>AVTransportID</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_AVTransportID</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>ProtocolInfo</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_ProtocolInfo</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>PeerConnectionManager</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_ConnectionManager</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>PeerConnectionID</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_ConnectionID</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>Direction</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Direction</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>Status</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_ConnectionStatus</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
</actionList>
|
||||
|
||||
<serviceStateTable>
|
||||
<stateVariable sendEvents="yes">
|
||||
<name>SourceProtocolInfo</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="yes">
|
||||
<name>SinkProtocolInfo</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="yes">
|
||||
<name>CurrentConnectionIDs</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_ConnectionStatus</name>
|
||||
<dataType>string</dataType>
|
||||
<allowedValueList>
|
||||
<allowedValue>OK</allowedValue>
|
||||
<allowedValue>ContentFormatMismatch</allowedValue>
|
||||
<allowedValue>InsufficientBandwidth</allowedValue>
|
||||
<allowedValue>UnreliableChannel</allowedValue>
|
||||
<allowedValue>Unknown</allowedValue>
|
||||
</allowedValueList>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_ConnectionManager</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_Direction</name>
|
||||
<dataType>string</dataType>
|
||||
<allowedValueList>
|
||||
<allowedValue>Input</allowedValue>
|
||||
<allowedValue>Output</allowedValue>
|
||||
</allowedValueList>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_ProtocolInfo</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_ConnectionID</name>
|
||||
<dataType>i4</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_AVTransportID</name>
|
||||
<dataType>i4</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_RcsID</name>
|
||||
<dataType>i4</dataType>
|
||||
</stateVariable>
|
||||
</serviceStateTable>
|
||||
|
||||
</scpd>
|
422
octoserve/var/dms/ContentDirectory.lua
Normal file
422
octoserve/var/dms/ContentDirectory.lua
Normal file
@@ -0,0 +1,422 @@
|
||||
|
||||
|
||||
local ContentDirectory = {}
|
||||
|
||||
local db = require("DataBase")
|
||||
|
||||
-- local dlnaprofile = 'DLNA.ORG_PN=MPEG_TS;DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0D100000000000000000000000000000'
|
||||
local dlnaprofile = 'DLNA.ORG_PN=MPEG_TS;DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=8D100000000000000000000000000000'
|
||||
local dlnaschema = ' xmlns:dlna="urn:schemas-dlna-org:metadata-1-0"'
|
||||
|
||||
if DisableDLNA then
|
||||
dlnaprofile = '*'
|
||||
dlnaschema = ''
|
||||
end
|
||||
|
||||
local Schema = 'xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1"'
|
||||
|
||||
local DIDLStart = '' -- '<?xml version="1.0" encoding="utf-8"?>'
|
||||
..'<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/"'
|
||||
..' xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"'
|
||||
..' xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"'
|
||||
-- .. dlnaschema
|
||||
..'>'
|
||||
local DIDLEnd = '</DIDL-Lite>'
|
||||
|
||||
-- State variables
|
||||
local SystemUpdateID = 36
|
||||
-- ---------------
|
||||
----------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
local AllFolders = {}
|
||||
local RootFolders = {}
|
||||
local Folders = {}
|
||||
local AllItems = {}
|
||||
|
||||
local i,f,vi,ai
|
||||
|
||||
----- Hierarchical Folders
|
||||
|
||||
RootFolders[#RootFolders+1] = { title = "All", id = "64" }
|
||||
RootFolders[#RootFolders+1] = { title = "Audio", id = "1" }
|
||||
RootFolders[#RootFolders+1] = { title = "Images", id = "3" }
|
||||
RootFolders[#RootFolders+1] = { title = "Video", id = "2" }
|
||||
for _,f in ipairs(RootFolders) do
|
||||
f.VideoItems = {}
|
||||
f.AudioItems = {}
|
||||
f.ChildFolders = {}
|
||||
AllFolders[f.id] = f
|
||||
end
|
||||
AllFolders["2"].ChildFolders = Folders
|
||||
AllFolders["64"].ChildFolders = Folders
|
||||
|
||||
----
|
||||
|
||||
for _,f in ipairs(db.SourceList) do
|
||||
f.id = f.refid
|
||||
f.VideoItems = {}
|
||||
f.AudioItems = {}
|
||||
f.ChildFolders = {}
|
||||
table.insert(Folders,f)
|
||||
-- table.insert(RootFolders,f)
|
||||
AllFolders[f.id] = f
|
||||
end
|
||||
|
||||
for _,vi in ipairs(db.ChannelList) do
|
||||
f = AllFolders[vi.refid]
|
||||
vi.id = f.id.."$"..tostring(#f.VideoItems)
|
||||
vi.src = f.src
|
||||
vi.parentID = f.id
|
||||
vi.request = string.gsub(vi.request,'&','&amp;')
|
||||
vi.title = string.gsub(vi.title,'&','&amp;')
|
||||
vi.title = string.gsub(vi.title,'<','&lt;')
|
||||
vi.title = string.gsub(vi.title,'>','&gt;')
|
||||
table.insert(f.VideoItems,vi)
|
||||
AllItems[vi.id] = vi
|
||||
end
|
||||
|
||||
----- Add Stream Folder
|
||||
|
||||
f = {}
|
||||
f.id = "STRM"
|
||||
f.title = "Current Streams"
|
||||
f.VideoItems = {}
|
||||
f.AudioItems = {}
|
||||
f.ChildFolders = {}
|
||||
table.insert(Folders,f)
|
||||
AllFolders[f.id] = f
|
||||
for i = 1,4,1 do
|
||||
vi = { id = f.id.."$"..tostring(i-1), parentID = f.id, title = "Stream "..tostring(i), stream = tostring(i) }
|
||||
table.insert(f.VideoItems,vi)
|
||||
AllItems[vi.id] = vi
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
local function Folder(title,id,parentid,childCount)
|
||||
local F = '<container id="'..id..'" parentID="'..parentid..'"'
|
||||
-- ..' childCount="'..childCount..'"'
|
||||
..' restricted="1"'
|
||||
-- ..' searchable="1"'
|
||||
..'>'
|
||||
..'<dc:title>'..title..'</dc:title>'
|
||||
..'<upnp:class>object.container.storageFolder</upnp:class>'
|
||||
..'</container>'
|
||||
return F
|
||||
end
|
||||
|
||||
local function VideoItem(Host,Item,nCompat)
|
||||
local rtspreq = ''
|
||||
if Item.stream then
|
||||
rtspreq = 'stream='..Item.stream
|
||||
else
|
||||
-- Some clients don't like a long request url, or an url with '&' in it
|
||||
-- Fail them for now
|
||||
-- if nCompat then
|
||||
-- rtspreq = "stream_99"
|
||||
-- elseif Item.src then
|
||||
if Item.src then
|
||||
rtspreq = '?src='..Item.src..'&amp;'..Item.request
|
||||
else
|
||||
rtspreq = '?'..Item.request
|
||||
end
|
||||
end
|
||||
|
||||
local didl = '<item id="'..Item.id..'" parentID="'..Item.parentID
|
||||
didl = didl .. '" restricted="1">'
|
||||
..'<dc:title>'..Item.title..'</dc:title>'
|
||||
..'<upnp:class>object.item.videoItem.videoBroadcast</upnp:class>'
|
||||
if Item.channelNr then
|
||||
didl = didl ..'<upnp:channelNr>'..Item.channelNr..'</upnp:channelNr>'
|
||||
end
|
||||
didl = didl ..'<res'
|
||||
..' protocolInfo="rtsp-rtp-udp:*:video/mpeg:'..dlnaprofile..'">'
|
||||
..'rtsp://'..Host..':554/'..rtspreq
|
||||
..'</res>'
|
||||
didl = didl ..'</item>'
|
||||
return didl
|
||||
|
||||
end
|
||||
|
||||
|
||||
local function BrowseChildren(client,Host,Request,nCompat)
|
||||
local ObjectID = UPnP:GetRequestParam(Request,"ObjectID")
|
||||
local BrowseFlag = UPnP:GetRequestParam(Request,"BrowseFlag")
|
||||
local Filter = UPnP:GetRequestParam(Request,"Filter")
|
||||
local StartingIndex = tonumber(UPnP:GetRequestParam(Request,"StartingIndex"))
|
||||
local RequestedCount = tonumber(UPnP:GetRequestParam(Request,"RequestedCount"))
|
||||
print("BrowseChildren",ObjectID,Filter,StartingIndex,RequestedCount)
|
||||
|
||||
local didl = DIDLStart;
|
||||
local Error = 0
|
||||
local NumberReturned = 0
|
||||
local TotalMatches = 0
|
||||
local UpdateID = SystemUpdateID
|
||||
local f,vi,ai
|
||||
|
||||
if ObjectID == "0" then
|
||||
if nCompat then
|
||||
for _,f in ipairs(RootFolders) do
|
||||
didl = didl..Folder(f.title,f.id,ObjectID,tostring(#f.VideoItems + #f.AudioItems + #f.ChildFolders))
|
||||
NumberReturned = NumberReturned + 1
|
||||
TotalMatches = TotalMatches +1
|
||||
end
|
||||
else
|
||||
for _,f in ipairs(Folders) do
|
||||
didl = didl..Folder(f.title,f.id,ObjectID,tostring(#f.VideoItems + #f.AudioItems + #f.ChildFolders))
|
||||
NumberReturned = NumberReturned + 1
|
||||
TotalMatches = TotalMatches +1
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
local f = AllFolders[ObjectID]
|
||||
if f then
|
||||
|
||||
local Index = 0
|
||||
for i,cf in ipairs(f.ChildFolders) do
|
||||
if Index >= StartingIndex and (RequestedCount == 0 or NumberReturned < RequestedCount) then
|
||||
didl = didl..Folder(cf.title,cf.id,ObjectID,tostring(#cf.VideoItems + #cf.AudioItems + #cf.ChildFolders))
|
||||
NumberReturned = NumberReturned + 1
|
||||
end
|
||||
Index = Index + 1
|
||||
TotalMatches = TotalMatches +1
|
||||
end
|
||||
|
||||
for i,vi in ipairs(f.VideoItems) do
|
||||
if Index >= StartingIndex and (RequestedCount == 0 or NumberReturned < RequestedCount) then
|
||||
didl = didl..VideoItem(Host,vi,nCompat)
|
||||
NumberReturned = NumberReturned + 1
|
||||
end
|
||||
Index = Index + 1
|
||||
TotalMatches = TotalMatches +1
|
||||
end
|
||||
|
||||
else
|
||||
Error = 710
|
||||
end
|
||||
end
|
||||
|
||||
didl = didl..DIDLEnd
|
||||
print("Returned",StartingIndex,NumberReturned,TotalMatches,Error)
|
||||
|
||||
if Error == 0 then
|
||||
local Args = { { n = "Result", v = didl }, { n = "NumberReturned", v = tostring(NumberReturned)},
|
||||
{ n = "TotalMatches", v = tostring(TotalMatches)}, { n = "UpdateID", v = tostring(UpdateID) } }
|
||||
UPnP:SendResponse(client,UPnP:CreateResponse(Schema,"Browse",Args))
|
||||
else
|
||||
UPnP:SendSoapError(client,Error)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local function BrowseMetaData(client,Host,Request,nCompat)
|
||||
local ObjectID = UPnP:GetRequestParam(Request,"ObjectID")
|
||||
local didl = DIDLStart;
|
||||
local UpdateID = SystemUpdateID
|
||||
local Error = 0
|
||||
|
||||
if ObjectID == "0" then
|
||||
local ChildCount = #Folders
|
||||
if nCompat then ChildCount = #RootFolders end
|
||||
didl = didl .. '<container id="1" parentID = "-1" childCount="'..tostring(ChildCount)..'" restricted="true">>'
|
||||
.. '<:dc:title>OctopusNet</dc:title>'
|
||||
..'<upnp:class>object.container.storageFolder</upnp:class>'
|
||||
..'</container>'
|
||||
else
|
||||
local f = AllFolders[ObjectID]
|
||||
if f then
|
||||
didl = didl..Folder(f.title,f.id,ObjectID,tostring(#f.VideoItems + #f.AudioItems + #f.ChildFolders))
|
||||
else
|
||||
local item = AllItems[ObjectID]
|
||||
if item then
|
||||
didl = didl..VideoItem(Host,item,nCompat)
|
||||
else
|
||||
Error = 710
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
didl = didl..DIDLEnd
|
||||
|
||||
if Error == 0 then
|
||||
local Args = { { n = "Result", v = didl }, { n = "NumberReturned", v = "1"},
|
||||
{ n = "TotalMatches", v = "1"}, { n = "UpdateID", v = tostring(UpdateID) } }
|
||||
UPnP:SendResponse(client,UPnP:CreateResponse(Schema,"Browse",Args))
|
||||
else
|
||||
UPnP:SendSoapError(client,Error)
|
||||
end
|
||||
|
||||
didl = DIDLEnd;
|
||||
end
|
||||
|
||||
local function Search(client,Host,Request,nCompat)
|
||||
local ContainerID = UPnP:GetRequestParam(Request,"ContainerID")
|
||||
local SearchCriteria = UPnP:GetRequestParam(Request,"SearchCriteria")
|
||||
local Filter = UPnP:GetRequestParam(Request,"Filter")
|
||||
local StartingIndex = tonumber(UPnP:GetRequestParam(Request,"StartingIndex"))
|
||||
local RequestedCount = tonumber(UPnP:GetRequestParam(Request,"RequestedCount"))
|
||||
local SortCriteria = UPnP:GetRequestParam(Request,"SortCriteria")
|
||||
print(SearchCriteria,ContainerID,Filter,StartingIndex,RequestedCount,SortCriteria)
|
||||
|
||||
local didl = DIDLStart;
|
||||
local Error = 0
|
||||
local NumberReturned = 0
|
||||
local TotalMatches = 0
|
||||
local UpdateID = SystemUpdateID
|
||||
|
||||
if string.match(SearchCriteria,"videoItem") then
|
||||
|
||||
if ContainerID == "0" then
|
||||
if not nCompat or nCompat ~= "WMP" then
|
||||
local Index = 0
|
||||
for _,vi in pairs(AllItems) do
|
||||
if Index >= StartingIndex and (RequestedCount == 0 or NumberReturned < RequestedCount) then
|
||||
didl = didl..VideoItem(Host,vi,nCompat)
|
||||
NumberReturned = NumberReturned + 1
|
||||
end
|
||||
Index = Index + 1
|
||||
TotalMatches = TotalMatches + 1
|
||||
-- if nCompat and TotalMatches > 19 then break end
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
local f = AllFolders[ContainerID]
|
||||
if f then
|
||||
local src = f.src
|
||||
if src then src = "src="..src.."&" else src = "" end
|
||||
|
||||
local Index = 0
|
||||
for _,vi in ipairs(f.VideoItems) do
|
||||
if Index >= StartingIndex and (RequestedCount == 0 or NumberReturned < RequestedCount) then
|
||||
didl = didl..VideoItem(Host,vi,nCompat)
|
||||
NumberReturned = NumberReturned + 1
|
||||
end
|
||||
Index = Index + 1
|
||||
TotalMatches = TotalMatches +1
|
||||
end
|
||||
else
|
||||
Error = 710
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
didl = didl..DIDLEnd
|
||||
-- didl = TestDidl
|
||||
-- NumberReturned = 1
|
||||
-- TotalMatches = 1
|
||||
print("Returned",StartingIndex,NumberReturned,TotalMatches,Error)
|
||||
|
||||
|
||||
if Error == 0 then
|
||||
local Args = { { n = "Result", v = didl }, { n = "NumberReturned", v = tostring(NumberReturned)},
|
||||
{ n = "TotalMatches", v = tostring(TotalMatches)}, { n = "UpdateID", v = tostring(UpdateID) } }
|
||||
UPnP:SendResponse(client,UPnP:CreateResponse(Schema,"Search",Args))
|
||||
else
|
||||
UPnP:SendSoapError(client,Error)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local function SendResult(client,Action,VarName,Result)
|
||||
local Args = { { n = VarName, v = Result } }
|
||||
UPnP:SendResponse(client,UPnP:CreateResponse(Schema,Action,Args))
|
||||
end
|
||||
|
||||
function ContentDirectory:Invoke(client,Attributes,Request)
|
||||
local Action = string.match(Attributes["SOAPACTION"],".+%#([%a%d%-_]+)")
|
||||
local Host = tostring(Attributes["host"])
|
||||
|
||||
local Compability = nil
|
||||
if Attributes["USER-AGENT"] then
|
||||
if string.match(Attributes["USER-AGENT"],"Windows%-Media%-Player") then Compability = "WMP"
|
||||
elseif string.match(Attributes["USER-AGENT"],"IPI%/1%.0") then Compability = "IPI"
|
||||
end
|
||||
end
|
||||
print("Compability=",Compability)
|
||||
|
||||
print(Host,"ContentDirectory",Action)
|
||||
if Action == "Browse" then
|
||||
local BrowseFlag = tostring(UPnP:GetRequestParam(Request,"BrowseFlag"))
|
||||
if BrowseFlag == "BrowseDirectChildren" then
|
||||
BrowseChildren(client,Host,Request,Compability)
|
||||
elseif BrowseFlag == "BrowseMetadata" then
|
||||
BrowseMetaData(client,Host,Request,Compability)
|
||||
else
|
||||
UPnP:SendSoapError(client,710)
|
||||
end
|
||||
elseif Action == "Search" then
|
||||
Search(client,Host,Request,Compability)
|
||||
-- elseif Action == "X_GetRemoteSharingStatus" then
|
||||
-- SendResult(client,Action,"0")
|
||||
elseif Action == "GetSortCapabilities" then
|
||||
SendResult(client,Action,"SortCaps","dc:title,upnp:class,upnp:originalTrackNumber")
|
||||
elseif Action == "GetSearchCapabilities" then
|
||||
SendResult(client,Action,"SearchCaps","dc:title")
|
||||
elseif Action == "GetSystemUpdateID" then
|
||||
SendResult(client,Action,"Id","1")
|
||||
else
|
||||
UPnP:SendSoapError(client,401)
|
||||
end
|
||||
end
|
||||
|
||||
function ContentDirectory:Subscribe(client,callback,timeout)
|
||||
local r = "HTTP/1.1 200 OK\r\n"
|
||||
.. 'Content-Type: text/xml; charset="utf-8"\r\n'
|
||||
.. "Server: "..UPnP.Server.."\r\n"
|
||||
.. "SID: uuid:50c95800-e839-4b96-b7ae-779d989e1399\r\n"
|
||||
.. "Timeout: Second-1800\r\n"
|
||||
.. "Content-Length: 0\r\n"
|
||||
.. "Connection: close\r\n"
|
||||
.. "EXT:\r\n"
|
||||
.. "\r\n"
|
||||
client:send(r)
|
||||
|
||||
local ipaddr,port = client:getpeername()
|
||||
local Args = { { n = "TransferIDs", v = "" }, { n = "SystemUpdateID", v = tostring(SystemUpdateID) } }
|
||||
UPnP:SendEvent(callback,"50c95800-e839-4b96-b7ae-779d989e1399",0,Args)
|
||||
|
||||
end
|
||||
|
||||
function ContentDirectory:Renew(client,sid,timeout)
|
||||
local r = "HTTP/1.1 200 OK\r\n"
|
||||
.. 'Content-Type: text/xml; charset="utf-8"\r\n'
|
||||
.. "Server: "..UPnP.Server.."\r\n"
|
||||
.. "SID: uuid:50c95800-e839-4b96-b7ae-779d989e1399\r\n"
|
||||
.. "Timeout: Second-1800\r\n"
|
||||
.. "Content-Length: 0\r\n"
|
||||
.. "Connection: close\r\n"
|
||||
.. "EXT:\r\n"
|
||||
.. "\r\n"
|
||||
client:send(r)
|
||||
end
|
||||
|
||||
function ContentDirectory:Unsubscribe(client,sid)
|
||||
local r = "HTTP/1.1 200 OK\r\n"
|
||||
.. 'Content-Type: text/xml; charset="utf-8"\r\n'
|
||||
.. "Server: "..UPnP.Server.."\r\n"
|
||||
.. "Content-Length: 0\r\n"
|
||||
.. "Connection: close\r\n"
|
||||
.. "EXT:\r\n"
|
||||
.. "\r\n"
|
||||
client:send(r)
|
||||
end
|
||||
|
||||
|
||||
function ContentDirectory:Description()
|
||||
t = ""
|
||||
local f = io.open("ContentDirectory.xml","r")
|
||||
if not f then os.exit() end
|
||||
while true do
|
||||
local line = f:read()
|
||||
if not line then break end
|
||||
t = t .. line .. "\r\n"
|
||||
end
|
||||
f:close()
|
||||
return t
|
||||
end
|
||||
|
||||
return ContentDirectory
|
207
octoserve/var/dms/ContentDirectory.xml
Normal file
207
octoserve/var/dms/ContentDirectory.xml
Normal file
@@ -0,0 +1,207 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<scpd xmlns="urn:schemas-upnp-org:service-1-0">
|
||||
<specVersion>
|
||||
<major>1</major>
|
||||
<minor>0</minor>
|
||||
</specVersion>
|
||||
<actionList>
|
||||
<action>
|
||||
<name>GetSearchCapabilities</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>SearchCaps</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>SearchCapabilities</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
<action>
|
||||
<name>GetSortCapabilities</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>SortCaps</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>SortCapabilities</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
<action>
|
||||
<name>GetSystemUpdateID</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>Id</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>SystemUpdateID</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
<action>
|
||||
<name>Browse</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>ObjectID</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>BrowseFlag</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_BrowseFlag</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>Filter</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Filter</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>StartingIndex</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Index</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>RequestedCount</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>SortCriteria</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_SortCriteria</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>Result</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>NumberReturned</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>TotalMatches</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>UpdateID</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_UpdateID</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
<action>
|
||||
<name>Search</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>ContainerID</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>SearchCriteria</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_SearchCriteria</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>Filter</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Filter</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>StartingIndex</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Index</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>RequestedCount</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>SortCriteria</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_SortCriteria</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>Result</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>NumberReturned</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>TotalMatches</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>UpdateID</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_UpdateID</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
</actionList>
|
||||
<serviceStateTable>
|
||||
<stateVariable sendEvents="yes">
|
||||
<name>TransferIDs</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_ObjectID</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_Result</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_SearchCriteria</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_BrowseFlag</name>
|
||||
<dataType>string</dataType>
|
||||
<allowedValueList>
|
||||
<allowedValue>BrowseMetadata</allowedValue>
|
||||
<allowedValue>BrowseDirectChildren</allowedValue>
|
||||
</allowedValueList>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_Filter</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_SortCriteria</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_Index</name>
|
||||
<dataType>ui4</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_Count</name>
|
||||
<dataType>ui4</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_UpdateID</name>
|
||||
<dataType>ui4</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>SearchCapabilities</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>SortCapabilities</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="yes">
|
||||
<name>SystemUpdateID</name>
|
||||
<dataType>ui4</dataType>
|
||||
</stateVariable>
|
||||
</serviceStateTable>
|
||||
</scpd>
|
80
octoserve/var/dms/MediaReceiverRegistrar.lua
Normal file
80
octoserve/var/dms/MediaReceiverRegistrar.lua
Normal file
@@ -0,0 +1,80 @@
|
||||
|
||||
MediaReceiverRegistrar = {}
|
||||
|
||||
function MediaReceiverRegistrar:Description()
|
||||
t = ""
|
||||
local f = io.open("MediaReceiverRegistrar.xml","r")
|
||||
if not f then os.exit() end
|
||||
while true do
|
||||
local line = f:read()
|
||||
if not line then break end
|
||||
t = t .. line .. "\r\n"
|
||||
end
|
||||
f:close()
|
||||
return t
|
||||
end
|
||||
|
||||
local function SendResult(client,Action,Result)
|
||||
local Args = { { n = "Result", v = Result } }
|
||||
UPnP:SendResponse(client,UPnP:CreateResponse('xmlns:u="urn:microsoft-com:service:X_MS_MediaReceiverRegistrar:1"',Action,Args))
|
||||
end
|
||||
|
||||
|
||||
function MediaReceiverRegistrar:Invoke(client,Attributes,Request)
|
||||
local Action = string.match(Attributes["SOAPACTION"],".+%#(%a+)")
|
||||
print("MediaReceiverRegistrar",Action)
|
||||
if Action == "IsAuthorized" then
|
||||
SendResult(client,Action,"1")
|
||||
-- UPnP:SendSoapError(client,402)
|
||||
elseif Action == "IsValidated" then
|
||||
SendResult(client,Action,"1")
|
||||
else
|
||||
UPnP:SendSoapError(client,401)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
function MediaReceiverRegistrar:Subscribe(client,callback,timeout)
|
||||
local r = "HTTP/1.1 200 OK\r\n"
|
||||
.. 'Content-Type: text/xml; charset="utf-8"\r\n'
|
||||
.. "Server: "..UPnP.Server.."\r\n"
|
||||
.. "SID: uuid:50c95802-e839-4b96-b7ae-779d989e1399\r\n"
|
||||
.. "Timeout: Second-1800\r\n"
|
||||
.. "Content-Length: 0\r\n"
|
||||
.. "Connection: close\r\n"
|
||||
.. "EXT:\r\n"
|
||||
.. "\r\n"
|
||||
client:send(r)
|
||||
|
||||
local ipaddr,port = client:getpeername()
|
||||
UPnP:SendEvent(callback,"50c95802-e839-4b96-b7ae-779d989e1399",0,{} )
|
||||
end
|
||||
|
||||
function MediaReceiverRegistrar:Renew(client,sid,timeout)
|
||||
local r = "HTTP/1.1 200 OK\r\n"
|
||||
.. 'Content-Type: text/xml; charset="utf-8"\r\n'
|
||||
.. "Server: "..UPnP.Server.."\r\n"
|
||||
.. "SID: uuid:50c95802-e839-4b96-b7ae-779d989e1399\r\n"
|
||||
.. "Timeout: Second-1800\r\n"
|
||||
.. "Content-Length: 0\r\n"
|
||||
.. "Connection: close\r\n"
|
||||
.. "EXT:\r\n"
|
||||
.. "\r\n"
|
||||
client:send(r)
|
||||
end
|
||||
|
||||
function MediaReceiverRegistrar:Unsubscribe(client,sid)
|
||||
local r = "HTTP/1.1 200 OK\r\n"
|
||||
.. "Server: "..UPnP.Server.."\r\n"
|
||||
.. "Connection: close\r\n"
|
||||
.. "\r\n"
|
||||
client:send(r)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
return MediaReceiverRegistrar
|
73
octoserve/var/dms/MediaReceiverRegistrar.xml
Normal file
73
octoserve/var/dms/MediaReceiverRegistrar.xml
Normal file
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<scpd xmlns="urn:schemas-upnp-org:service-1-0">
|
||||
<specVersion>
|
||||
<major>1</major>
|
||||
<minor>0</minor>
|
||||
</specVersion>
|
||||
<actionList>
|
||||
<action>
|
||||
<name>IsAuthorized</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>DeviceID</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_DeviceID</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>Result</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
<action>
|
||||
<name>IsValidated</name>
|
||||
<argumentList>
|
||||
<argument>
|
||||
<name>DeviceID</name>
|
||||
<direction>in</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_DeviceID</relatedStateVariable>
|
||||
</argument>
|
||||
<argument>
|
||||
<name>Result</name>
|
||||
<direction>out</direction>
|
||||
<relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable>
|
||||
</argument>
|
||||
</argumentList>
|
||||
</action>
|
||||
</actionList>
|
||||
<serviceStateTable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_DeviceID</name>
|
||||
<dataType>string</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_RegistrationReqMsg</name>
|
||||
<dataType>bin.base64</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_RegistrationRespMsg</name>
|
||||
<dataType>bin.base64</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>A_ARG_TYPE_Result</name>
|
||||
<dataType>int</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>AuthorizationDeniedUpdateID</name>
|
||||
<dataType>ui4</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>AuthorizationGrantedUpdateID</name>
|
||||
<dataType>ui4</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>ValidationRevokedUpdateID</name>
|
||||
<dataType>ui4</dataType>
|
||||
</stateVariable>
|
||||
<stateVariable sendEvents="no">
|
||||
<name>ValidationSucceededUpdateID</name>
|
||||
<dataType>ui4</dataType>
|
||||
</stateVariable>
|
||||
</serviceStateTable>
|
||||
</scpd>
|
68
octoserve/var/dms/SendEvent.lua
Normal file
68
octoserve/var/dms/SendEvent.lua
Normal file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local socket = require("socket")
|
||||
local mime = require("mime")
|
||||
local url = require("socket.url")
|
||||
|
||||
print(#arg)
|
||||
if #arg ~= 4 then
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local path, uuid, seq, b64 = unpack(arg)
|
||||
|
||||
print(path,uuid,seq)
|
||||
print(b64)
|
||||
|
||||
local values = mime.unb64(b64)
|
||||
if values == "nil" then
|
||||
values =""
|
||||
end
|
||||
|
||||
local evnt = '<?xml version="1.0" encoding="utf-8"?>'.."\r\n"
|
||||
.. '<a:propertyset xmlns:a="urn:schemas-upnp-org:event-1-0/">'.."\r\n"
|
||||
.. "<a:property>\r\n"
|
||||
.. values
|
||||
.. "</a:property>\r\n"
|
||||
.. "</a:propertyset>\r\n"
|
||||
|
||||
-- socket.sleep(0.5)
|
||||
|
||||
for p in string.gmatch(path,"<(.+)>") do
|
||||
|
||||
local parsed_url = url.parse(p)
|
||||
print(parsed_url.host,parsed_url.port,parsed_url.path)
|
||||
|
||||
local ip = parsed_url.host
|
||||
local port = parsed_url.port
|
||||
local path = parsed_url.path
|
||||
|
||||
if port and port ~= "0" then
|
||||
print("send ",ip,port,path)
|
||||
|
||||
local r = "NOTIFY "..path.." HTTP/1.1\r\n"
|
||||
.. "Host: "..ip..":"..port.."\r\n"
|
||||
.. "Content-Type: text/xml; charset=\"utf-8\"\r\n"
|
||||
.. "Content-Length: "..string.format("%d",#evnt).."\r\n"
|
||||
.. "NT: upnp:event\r\n"
|
||||
.. "NTS: upnp:propchange\r\n"
|
||||
.. "SID: uuid:"..uuid.."\r\n"
|
||||
.. "SEQ: "..seq.."\r\n"
|
||||
.. "\r\n"
|
||||
.. evnt
|
||||
|
||||
tcp = socket.tcp()
|
||||
tcp:settimeout(2)
|
||||
|
||||
if tcp:connect(ip,port) then
|
||||
|
||||
tcp:send(r)
|
||||
tcp:receive("*l")
|
||||
tcp:close()
|
||||
|
||||
break
|
||||
end
|
||||
tcp:close()
|
||||
end
|
||||
|
||||
end
|
220
octoserve/var/dms/UPnP.lua
Normal file
220
octoserve/var/dms/UPnP.lua
Normal file
@@ -0,0 +1,220 @@
|
||||
|
||||
UPnP = {}
|
||||
|
||||
local DebugFlag = false
|
||||
local Server = "Linux/3.9 DLNADOC/1.50 UPnP/1.0 OctopusNet-DMS/1.0"
|
||||
|
||||
if DisableDLNA then
|
||||
Server = "Linux/3.9 UPnP/1.0 OctopusNet-DMS/1.0"
|
||||
end
|
||||
|
||||
function UPnP:SetDebug(f)
|
||||
DebugFlag = f
|
||||
end
|
||||
|
||||
UPnP.Server = Server
|
||||
|
||||
function UPnP:ParseHTTPHeader(header)
|
||||
local linenum = 0
|
||||
local method,path,proto
|
||||
local attributes = {}
|
||||
|
||||
for line in string.gmatch(header,"(.-)\r\n") do
|
||||
if not line then break end
|
||||
if DebugFlag then print(string.format("%2d:%s",linenum,line)) end
|
||||
if linenum == 0 then
|
||||
method,path,proto = string.match(line,"(.+)%s+(.+)%s+(.+)")
|
||||
else
|
||||
local n,v = string.match(line,"([%a%-]+)%s*%:%s*(.*)")
|
||||
if n and v then
|
||||
attributes[string.upper(n)] = v
|
||||
end
|
||||
end
|
||||
|
||||
linenum = linenum + 1
|
||||
end
|
||||
return method,path,proto,attributes
|
||||
end
|
||||
|
||||
|
||||
function UPnP:ReadHTTPHeader(client)
|
||||
|
||||
local linenum = 0
|
||||
local line, err = client:receive()
|
||||
|
||||
if err then
|
||||
if DebugFlag then print("HTTPHeader Error "..err) end
|
||||
return
|
||||
end
|
||||
|
||||
if DebugFlag then print(string.format("%2d:%s",linenum,line)) end
|
||||
local method,path,proto = string.match(line,"(.+)%s+(.+)%s+(.+)")
|
||||
local attributes = {}
|
||||
|
||||
while true do
|
||||
local line, err = client:receive()
|
||||
if err then
|
||||
if DebugFlag then print("HTTPHeader Error "..err.." HTTP Line: "..tostring(linenum)) end
|
||||
return
|
||||
end
|
||||
linenum = linenum + 1
|
||||
if DebugFlag then print(string.format("%2d:%s",linenum,line)) end
|
||||
if line == "" then break end
|
||||
|
||||
if linenum == 30 then
|
||||
if DebugFlag then print("HTTPHeader Error "..tostring(linenum)) end
|
||||
return
|
||||
end
|
||||
|
||||
local n,v = string.match(line,"([%a%-]+)%s*%:%s*(.*)")
|
||||
if n and v then
|
||||
attributes[string.upper(n)] = v
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Add some preparsed params, Note lower case key
|
||||
if attributes["HOST"] then
|
||||
local Host,Port = string.match(attributes["HOST"],"(.+)%:(%d+)")
|
||||
attributes["host"] = Host
|
||||
attributes["port"] = Port
|
||||
end
|
||||
|
||||
return method,path,proto,attributes
|
||||
end
|
||||
|
||||
function UPnP:ReadHTTPBody(client,clen)
|
||||
local Body = ""
|
||||
local linenum = 0
|
||||
|
||||
if DebugFlag and clen then print(string.format("---- Length = %d",clen)) end
|
||||
|
||||
Body, err = client:receive(clen)
|
||||
|
||||
if err then
|
||||
if DebugFlag then print("HTTPBody Error "..err) end
|
||||
return
|
||||
end
|
||||
-- while true do
|
||||
-- local line, err = client:receive()
|
||||
-- if err then
|
||||
-- if DebugFlag then print("Error "..err) end
|
||||
-- return
|
||||
-- end
|
||||
-- linenum = linenum + 1
|
||||
-- if DebugFlag then print(string.format("%4d:%s",linenum,line)) end
|
||||
-- if line == "" then break end
|
||||
|
||||
-- if linenum == 300 then
|
||||
-- if DebugFlag then print("Error "..linenum) end
|
||||
-- return
|
||||
-- end
|
||||
-- Body = Body .. line .. "\n"
|
||||
-- end
|
||||
return Body
|
||||
end
|
||||
|
||||
function UPnP:ParseInvocation(soap,Service)
|
||||
end
|
||||
|
||||
function UPnP:CreateResponse(Service,Action,Args)
|
||||
local n,v
|
||||
local soap = '<?xml version="1.0" encoding="utf-8"?>'..'\r\n'
|
||||
.. "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
||||
.. "<s:Body><u:"..Action.."Response "..Service..">"
|
||||
for _,a in ipairs(Args) do
|
||||
soap = soap .. "<"..a.n..">"..a.v.."</"..a.n..">"
|
||||
end
|
||||
soap = soap .. "</u:"..Action.."Response></s:Body></s:Envelope>"..'\r\n'
|
||||
return soap
|
||||
end
|
||||
|
||||
function UPnP:SendResponse(client,Content)
|
||||
local r = "HTTP/1.1 200 OK\r\n"
|
||||
.. "Content-Type: text/xml; charset=\"utf-8\"\r\n"
|
||||
.. "Connection: close\r\n"
|
||||
.. "Content-Length: "..string.format("%d",#Content).."\r\n"
|
||||
.. "Server: "..Server.."\r\n"
|
||||
.. "Date: "..os.date("!%a, %d %b %Y %H:%M:%S GMT").."\r\n"
|
||||
.. "EXT:\r\n"
|
||||
.. "\r\n"
|
||||
.. Content
|
||||
|
||||
client:send(r)
|
||||
end
|
||||
|
||||
function UPnP:GetRequestParam(Request,Param)
|
||||
return string.match(Request,"%<"..Param..".*%>%s*(.+)%s*%<%/"..Param.."%>")
|
||||
end
|
||||
|
||||
local SoapErrorDescription = {}
|
||||
SoapErrorDescription[402] = "Invalid Args"
|
||||
|
||||
function UPnP:SendSoapError(client,code)
|
||||
local soap = '<?xml version="1.0" encoding="utf-8"?>'
|
||||
.. "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
||||
.. "<s:Body>"
|
||||
.. "<s:Fault>"
|
||||
.. "<s:faultcode>s:Client</s:faultcode>"
|
||||
.. "<s:faultstring>UPnPError</s:faultstring>"
|
||||
.. "<s:detail>"
|
||||
.. '<UPnPError xmlns="urn:shemas-upnp-org:control-1-0">'
|
||||
.. '<errorCode>'..code..'</errorCode>'
|
||||
.. '<errorDescription>'..tostring(SoapErrorDescription[code])..'</errorDescription>'
|
||||
.. '</UPnPError>'
|
||||
.. "</s:detail>"
|
||||
.. "</s:fault>"
|
||||
.. "</s:Body>"
|
||||
.. "</s:Envelope>"
|
||||
|
||||
local r = "HTTP/1.1 500 Internal Server Error\r\n"
|
||||
.. "Content-Type: text/xml; charset=\"utf-8\"\r\n"
|
||||
.. "Connection: close\r\n"
|
||||
.. "Content-Length: "..string.format("%d",#soap).."\r\n"
|
||||
.. "Server: "..Server.."\r\n"
|
||||
.. "Date: "..os.date("!%a, %d %b %Y %H:%M:%S GMT").."\r\n"
|
||||
.. "EXT:\r\n"
|
||||
.. "\r\n"
|
||||
.. soap
|
||||
|
||||
client:send(r)
|
||||
end
|
||||
|
||||
function UPnP:SendHTTPError(client,code)
|
||||
local r = "HTTP/1.1 " ..code.."\r\n"
|
||||
.. "Content-Type: text/html\r\n"
|
||||
.. "Connection: Close\r\n"
|
||||
.. "Server: "..UPnP.Server.."\r\n"
|
||||
.. "Date: "..os.date("!%a, %d %b %Y %H:%M:%S GMT").."\r\n"
|
||||
.. "\r\n"
|
||||
.. "<html><head><title>Error "..code.."</title></head><body><h1>Error "..code.."</h1></body></html>\r\n"
|
||||
client:send(r)
|
||||
end
|
||||
|
||||
function UPnP:SendEvent(path,uuid,seq,values)
|
||||
local xml = ""
|
||||
for _,v in ipairs(values) do
|
||||
xml = xml .. "<"..v.n..">"..v.v.."</"..v.n..">\r\n"
|
||||
end
|
||||
if xml == "" then xml="nil" end
|
||||
|
||||
local b64 = mime.b64(xml)
|
||||
os.execute('lua SendEvent.lua "'..path..'" "'..uuid..'" "'..seq..'" "'..b64..'"&')
|
||||
end
|
||||
|
||||
function UPnP:SystemParameters(template)
|
||||
local ifconfig = io.popen("ifconfig eth0")
|
||||
assert(ifconfig)
|
||||
local eth0 = ifconfig:read("*a")
|
||||
ifconfig:close()
|
||||
|
||||
local myip = string.match(eth0,"inet addr%:(%d+%.%d+%.%d+%.%d+)")
|
||||
local hwaddr = string.match(eth0,"HWaddr (%x+%:%x+%:%x+%:%x+%:%x+%:%x+)")
|
||||
local guidend = string.gsub(hwaddr,"%:","")
|
||||
local sernbr = tonumber(string.sub(guidend,-6),16)
|
||||
local uuid = string.lower(string.gsub(template,"000000000000",guidend))
|
||||
|
||||
return uuid,sernbr,myip
|
||||
end
|
||||
|
||||
return UPnP
|
190
octoserve/var/dms/dms.lua
Executable file
190
octoserve/var/dms/dms.lua
Executable file
@@ -0,0 +1,190 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local ClientTimeout = 20
|
||||
local Backlog = 32
|
||||
DisableDLNA = true
|
||||
|
||||
local socket = require("socket")
|
||||
local mime = require("mime")
|
||||
|
||||
local upnp = require("UPnP")
|
||||
local cman = require("ConnectionManager")
|
||||
local cdir = require("ContentDirectory")
|
||||
local mreg = require("MediaReceiverRegistrar")
|
||||
|
||||
|
||||
|
||||
|
||||
function sendXMLFile(client,data)
|
||||
local r = "HTTP/1.1 200 OK\r\n"
|
||||
.. 'Content-Type: text/xml; charset="utf-8"\r\n'
|
||||
.. "Connection: close\r\n"
|
||||
.. "Content-Length: "..string.format("%d",#data).."\r\n"
|
||||
.. "Server: "..UPnP.Server.."\r\n"
|
||||
.. "Date: "..os.date("!%a, %d %b %Y %H:%M:%S GMT").."\r\n"
|
||||
.. "EXT:\r\n"
|
||||
.. "\r\n"
|
||||
.. data
|
||||
client:send(r)
|
||||
end
|
||||
|
||||
function sendImage(client,path)
|
||||
local p = string.gsub(path,"%.+",".")
|
||||
if string.sub(p,1,7) == "/icons/" then
|
||||
local f = io.open("."..p,"r")
|
||||
if f then
|
||||
local image = f:read(100000)
|
||||
f:close()
|
||||
local t = "jpeg"
|
||||
if p.sub(p,-3) == "png" then t = "png" end
|
||||
|
||||
local r = "HTTP/1.1 200 OK\r\n"
|
||||
.. "Content-Type: image/"..t.."\r\n"
|
||||
.. "Content-Length: "..string.format("%d",#image).."\r\n"
|
||||
.. "Connection: Close\r\n"
|
||||
.. "Server: "..UPnP.Server.."\r\n"
|
||||
.. "Date: "..os.date("!%a, %d %b %Y %H:%M:%S GMT").."\r\n"
|
||||
.. "\r\n"
|
||||
.. image
|
||||
client:send(r)
|
||||
else
|
||||
upnp:SendHTTPError(client,404)
|
||||
end
|
||||
else
|
||||
upnp:SendHTTPError(client,404)
|
||||
end
|
||||
end
|
||||
|
||||
function sendRedirect(client,host)
|
||||
local r = "HTTP/1.1 200 OK\r\n"
|
||||
.. "Content-Type: text/html\r\n"
|
||||
.. "Connection: Close\r\n"
|
||||
.. "Server: "..UPnP.Server.."\r\n"
|
||||
.. "Refresh: 0; url=http://"..host.."/\r\n"
|
||||
.. "\r\n"
|
||||
.. '<html><body><a href="http://'..host..'/">Click</a></body></html>\r\n'
|
||||
client:send(r)
|
||||
end
|
||||
|
||||
function LoadFile(fname)
|
||||
print(fname)
|
||||
t = ""
|
||||
local f = io.open(fname,"r")
|
||||
if not f then os.exit() end
|
||||
while true do
|
||||
local line = f:read()
|
||||
if not line then break end
|
||||
t = t .. line .. "\r\n"
|
||||
end
|
||||
f:close()
|
||||
return t
|
||||
end
|
||||
|
||||
upnp:SetDebug(true)
|
||||
|
||||
local port = 8080
|
||||
-- local RootLocation = "http://10.0.4.31:8080/dms.xml"
|
||||
|
||||
local uuid,sernbr,myip = upnp:SystemParameters("f0287290-e1e1-11e2-9a21-000000000000")
|
||||
|
||||
local Desc = LoadFile("dms.xml")
|
||||
Desc = string.gsub(Desc,"##(%a+)##",{ UUID = uuid, SERNBR = sernbr, HOST = myip })
|
||||
|
||||
if DisableDLNA then
|
||||
Desc = string.gsub(Desc,"(%<dlna:.+DOC%>)","")
|
||||
end
|
||||
|
||||
local server = socket.tcp()
|
||||
assert(server:setoption("reuseaddr", true))
|
||||
assert(server:bind("*", port))
|
||||
|
||||
local ip, port = server:getsockname()
|
||||
print("Listen: " .. ip .. ":" .. port)
|
||||
|
||||
assert(server:listen(Backlog))
|
||||
|
||||
while true do
|
||||
local client = server:accept()
|
||||
client:settimeout(ClientTimeout)
|
||||
|
||||
local method,path,proto,attributes = upnp:ReadHTTPHeader(client)
|
||||
if method then
|
||||
if method == "GET" then
|
||||
if path == "/dms.xml" then
|
||||
sendXMLFile(client,Desc)
|
||||
elseif path == "/cman.xml" then
|
||||
sendXMLFile(client,cman:Description())
|
||||
elseif path == "/cdir.xml" then
|
||||
sendXMLFile(client,cdir:Description())
|
||||
elseif path == "/mreg.xml" then
|
||||
sendXMLFile(client,mreg:Description())
|
||||
elseif path == "/" then
|
||||
sendRedirect(client,attributes["host"])
|
||||
else
|
||||
sendImage(client,path)
|
||||
end
|
||||
elseif method == "SUBSCRIBE" then
|
||||
if attributes["NT"] then
|
||||
if attributes["NT"] == "upnp:event" and attributes["CALLBACK"] ~= "" then
|
||||
if path == "/cde" then
|
||||
cdir:Subscribe(client,attributes["CALLBACK"],attributes["TIMEOUT"])
|
||||
elseif path == "/cme" then
|
||||
cman:Subscribe(client,attributes["CALLBACK"],attributes["TIMEOUT"])
|
||||
elseif path == "/mre" then
|
||||
mreg:Subscribe(client,attributes["CALLBACK"],attributes["TIMEOUT"])
|
||||
else
|
||||
upnp:SendSoapError(client,401)
|
||||
end
|
||||
else
|
||||
upnp:SendSoapError(client,412)
|
||||
end
|
||||
else
|
||||
if attributes["SID"] and attributes["SID"] ~= "" then
|
||||
if path == "/cde" then
|
||||
cdir:Renew(client,attributes["SID"],attributes["TIMEOUT"])
|
||||
elseif path == "/cme" then
|
||||
cman:Renew(client,attributes["SID"],attributes["TIMEOUT"])
|
||||
elseif path == "/mre" then
|
||||
mreg:Renew(client,attributes["SID"],attributes["TIMEOUT"])
|
||||
else
|
||||
upnp:SendSoapError(client,401)
|
||||
end
|
||||
else
|
||||
upnp:SendSoapError(client,412)
|
||||
end
|
||||
end
|
||||
elseif method == "UNSUBSCRIBE" then
|
||||
if attributes["SID"] and attributes["SID"] ~= "" then
|
||||
if path == "/cde" then
|
||||
cdir:Unsubscribe(client,attributes["SID"])
|
||||
elseif path == "/cme" then
|
||||
cman:Unsubscribe(client,attributes["SID"])
|
||||
elseif path == "/mre" then
|
||||
mreg:Unsubscribe(client,attributes["SID"])
|
||||
else
|
||||
upnp:SendSoapError(client,401)
|
||||
end
|
||||
else
|
||||
upnp:SendSoapError(client,412)
|
||||
end
|
||||
elseif method == "POST" then
|
||||
local soap = upnp:ReadHTTPBody(client,attributes["CONTENT-LENGTH"])
|
||||
|
||||
if path == "/cdc" then
|
||||
cdir:Invoke(client,attributes,soap)
|
||||
elseif path == "/cmc" then
|
||||
cman:Invoke(client,attributes,soap)
|
||||
elseif path == "/mrc" then
|
||||
mreg:Invoke(client,attributes,soap)
|
||||
else
|
||||
upnp:SendSoapError(client,404)
|
||||
end
|
||||
else
|
||||
upnp:SendHTTPError(client,"500")
|
||||
end
|
||||
end
|
||||
|
||||
client:close()
|
||||
collectgarbage()
|
||||
|
||||
end
|
74
octoserve/var/dms/dms.xml
Normal file
74
octoserve/var/dms/dms.xml
Normal file
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root xmlns="urn:schemas-upnp-org:device-1-0">
|
||||
<specVersion>
|
||||
<major>1</major>
|
||||
<minor>0</minor>
|
||||
</specVersion>
|
||||
<device>
|
||||
<deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>
|
||||
<friendlyName>OctopusNet DMS</friendlyName>
|
||||
<manufacturer>Digital Devices GmbH</manufacturer>
|
||||
<manufacturerURL>http://www.digitaldevices.de</manufacturerURL>
|
||||
<modelDescription>OctopusNet</modelDescription>
|
||||
<modelName>OctopusNet DMS</modelName>
|
||||
<modelNumber>1</modelNumber>
|
||||
<!-- <modelURL>http://www.digitaldevices.de</modelURL> -->
|
||||
<serialNumber>##SERNBR##</serialNumber>
|
||||
<UDN>uuid:##UUID##</UDN>
|
||||
<dlna:X_DLNADOC xmlns:dlna="urn:schemas-dlna-org:device-1-0">DMS-1.50</dlna:X_DLNADOC>
|
||||
<iconList>
|
||||
<icon>
|
||||
<mimetype>image/png</mimetype>
|
||||
<width>120</width>
|
||||
<height>120</height>
|
||||
<depth>24</depth>
|
||||
<url>icons/dd-120.png</url>
|
||||
</icon>
|
||||
<icon>
|
||||
<mimetype>image/jpg</mimetype>
|
||||
<width>120</width>
|
||||
<height>120</height>
|
||||
<depth>24</depth>
|
||||
<url>icons/dd-120.jpg</url>
|
||||
</icon>
|
||||
<icon>
|
||||
<mimetype>image/png</mimetype>
|
||||
<width>48</width>
|
||||
<height>48</height>
|
||||
<depth>24</depth>
|
||||
<url>icons/dd-48.png</url>
|
||||
</icon>
|
||||
<icon>
|
||||
<mimetype>image/jpg</mimetype>
|
||||
<width>48</width>
|
||||
<height>48</height>
|
||||
<depth>24</depth>
|
||||
<url>icons/dd-48.jpg</url>
|
||||
</icon>
|
||||
</iconList>
|
||||
<serviceList>
|
||||
<service>
|
||||
<serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType>
|
||||
<serviceId>urn:upnp-org:serviceId:ContentDirectory</serviceId>
|
||||
<SCPDURL>/cdir.xml</SCPDURL>
|
||||
<controlURL>/cdc</controlURL>
|
||||
<eventSubURL>/cde</eventSubURL>
|
||||
</service>
|
||||
<service>
|
||||
<serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>
|
||||
<serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId>
|
||||
<SCPDURL>/cman.xml</SCPDURL>
|
||||
<controlURL>/cmc</controlURL>
|
||||
<eventSubURL>/cme</eventSubURL>
|
||||
</service>
|
||||
<service>
|
||||
<serviceType>urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1</serviceType>
|
||||
<serviceId>urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar</serviceId>
|
||||
<SCPDURL>/mreg.xml</SCPDURL>
|
||||
<controlURL>/mrc</controlURL>
|
||||
<eventSubURL>/mre</eventSubURL>
|
||||
</service>
|
||||
</serviceList>
|
||||
<presentationURL>//##HOST##/</presentationURL>
|
||||
</device>
|
||||
</root>
|
BIN
octoserve/var/dms/icons/dd-120.jpg
Normal file
BIN
octoserve/var/dms/icons/dd-120.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.7 KiB |
BIN
octoserve/var/dms/icons/dd-120.png
Normal file
BIN
octoserve/var/dms/icons/dd-120.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.8 KiB |
BIN
octoserve/var/dms/icons/dd-48.jpg
Normal file
BIN
octoserve/var/dms/icons/dd-48.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
BIN
octoserve/var/dms/icons/dd-48.png
Normal file
BIN
octoserve/var/dms/icons/dd-48.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
5
octoserve/var/dms/start.sh
Normal file
5
octoserve/var/dms/start.sh
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
killall lua
|
||||
sleep 3
|
||||
lua dms.lua&
|
Reference in New Issue
Block a user