Having trouble getting deep linking to work in my Roku channel.
I've done a lot of reading on the Roku SDK (particularly their Deep Linking Doc) and a variety of forums. However, I'm just not making any headway.
Currently, my channel uses the Simple Grid with Details template, which serves video content via RSS files.
Here's the beginning of my main.brs file:
sub Main(args as Dynamic) as Void
showSGScreen(args)
if (args.mediaType <> invalid) and (args.contentID <> invalid)
if (args.mediaType = "movie" or args.mediaType = "episode" or args.mediaType = "short-form" or args.mediaType = "series" or args.mediaType = "special")
'play content directly
else
'deep linking issue such as a contentID not matching any content in the partner's catalog
'display an appropriate error message for the user
end if
else
'launch channel normally
end if
end sub
Sub RunUserInterface()
screen = CreateObject("roSGScreen")
scene = screen.CreateScene("HomeScene")
port = CreateObject("roMessagePort")
screen.SetMessagePort(port)
screen.Show()
oneRow = GetApiArray1()
twoRow = GetApiArray2()
threeRow = GetApiArray3()
fourRow = GetApiArray4()
list = [
{
TITLE : "Watch Our Services Live"
ContentList : oneRow
}
{
TITLE : "Without Ceasing: A Series On Prayer"
ContentList : twoRow
}
{
TITLE : "The Least Of These: Hope, Help, Heal"
ContentList : threeRow
}
{
TITLE : "The Bible: Learning To Live It"
ContentList : fourRow
}
]
scene.gridContent = ParseXMLContent(list)
while true
msg = wait(0, port)
print "------------------"
print "msg = "; msg
end while
if screen <> invalid then
screen.Close()
screen = invalid
end if
End Sub
Function ParseXMLContent(list As Object)
RowItems = createObject("RoSGNode","ContentNode")
for each rowAA in list
'for index = 0 to 1
row = createObject("RoSGNode","ContentNode")
row.Title = rowAA.Title
for each itemAA in rowAA.ContentList
item = createObject("RoSGNode","ContentNode")
' We don't use item.setFields(itemAA) as doesn't cast
streamFormat to proper value
for each key in itemAA
item[key] = itemAA[key]
end for
row.appendChild(item)
end for
RowItems.appendChild(row)
end for
return RowItems
End Function
Function GetApiArray1()
url = CreateObject("roUrlTransfer")
url.SetUrl("http://media.genepensiero.com/roku/rss/livestream.rss")
rsp = url.GetToString()
responseXML = ParseXML(rsp)
responseXML = responseXML.GetChildElements()
responseArray = responseXML.GetChildElements()
result = []
for each xmlItem in responseArray
if xmlItem.getName() = "item"
itemAA = xmlItem.GetChildElements()
if itemAA <> invalid
item = {}
for each xmlItem in itemAA
item[xmlItem.getName()] = xmlItem.getText()
if xmlItem.getName() = "media:content"
item.stream = {url : xmlItem.url}
item.url = xmlItem.getAttributes().url
item.streamFormat = "hls"
mediaContent = xmlItem.GetChildElements()
for each mediaContentItem in mediaContent
if mediaContentItem.getName() =
"media:thumbnail"
item.HDPosterUrl =
mediaContentItem.getattributes().url
item.hdBackgroundImageUrl =
mediaContentItem.getattributes().url
end if
end for
end if
end for
result.push(item)
end if
end if
end for
return result
End Function
Then an example of my RSS feed is:
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
<channel>
<title>Bright Light Church</title>
<link />
<description>Live and Archived Bible Studies</description>
<language>en-us</language>
<pubDate>Wed, 26 Apr 2017 12:00:00 PT</pubDate>
<category>TV & Film</category>
<image>
<title>The Bible</title>
<url>http://media.genepensiero.com/roku/withoutceasing.jpg</url>
<width>-1</width>
<height>-1</height>
</image>
<item>
<title>Prayer Pressure</title>
<link>http://media.calvaryhanford.com/psalms/prayerpressure.mp4</link>
<description>David brings big requests to God and receives a big
revelation.</description>
<pubDate>Sun, 05 Mar 2017 10:15:00 PT</pubDate>
<guid isPermaLink="false">ch001</guid>
<media:content channels="2" type="special" isDefault="true" id="ch001" url="http://media.calvaryhanford.com/psalms/prayerpressure.mp4">
<media:description>David brings big requests to God and receives a big
revelation.</media:description>
<media:keywords>church, bible study, expository sermon</media:keywords>
<media:thumbnail url="http://media.genepensiero.com/roku/withoutceasing.jpg" />
<media:title>Prayer Pressure</media:title>
</media:content>
</item>
When I use the Deep Link Tester it just opens my channel to the home screen, it doesn't launch the specific video.
Any insight would be appreciated.
You don't seem to be doing anything with the contentID and mediaType, the app will not deeplink to content automatically (or magically). You'll have to match the received contentId to an item in your API, and navigate to or play that item (depending on the mediaType), all from within your app.
Related
I have the Following XML which is a result from a certain WEB-Service.
<?xml version="1.0" encoding="UTF-8"?>
-<ArrayOfString xmlns="http://tempuri.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>16/May/2016 - 20/May/2016</string>
<string>20/May/2016 - 23/May/2016</string>
<string>23/May/2016 - 27/May/2016</string>
<string>27/May/2016 - 30/May/2016</string>
</ArrayOfString>
I have the Following VBA Code to read the Above XML
strRet = PostWebservice(strUrlEBenefit, strSoapAction, strXml)
intPos1 = InStr(strRet, "<string>") + 8
intPos2 = InStr(strRet, "</string>")
If intPos1 > 11 And intPos2 > 0 Then
xmlresult = Mid(strRet, intPos1, intPos2 - intPos1)
End If
as a result I'm Getting "16/May/2016 - 20/May/2016" in xmlresult.
What I want to do is getting all the date values Between all the [string] tags.
Can you please guide me how can I achieve the result? I understand I need to read it into array but I don't know how and didn't saw any useful tutorials for me(beginner in VBA and XML) to ref.
Read the xml-result which was returned from the web service into xml-document and use e.g. SelectSingleNode to select the node ArrayOfString. This node has namaspaces so you need to use namespaces in the xpath as well. Then just read all the child node texts e.g. into a collection here declared as result. HTH
Note: Add reference to Microsoft XML, v6.0 dll
Sub GetDateValues()
Dim xmlDocument As MSXML2.DOMDocument60
Dim xmlNamespaces As String
Dim arrayOfStringNode As IXMLDOMNode
Dim result As Collection
Dim xmlNode As IXMLDOMNode
Dim strRet As String
strRet = PostWebservice(strUrlEBenefit, strSoapAction, strXml)
Set xmlDocument = New DOMDocument60
If Not xmlDocument.LoadXML(strRet) Then
Err.Raise xmlDocument.parseError.ErrorCode, , xmlDocument.parseError.reason
End If
xmlNamespaces = "xmlns:myns='http://tempuri.org/'"
xmlDocument.setProperty "SelectionNamespaces", xmlNamespaces
Set arrayOfStringNode = xmlDocument.SelectSingleNode("/myns:ArrayOfString")
Set result = New Collection
For Each xmlNode In arrayOfStringNode.ChildNodes
result.Add xmlNode.Text
Next xmlNode
End Sub
I'm extrapolating using the answer from: 'dee', but reading in to an array with x number of columns. The reason for dNode below is because webservices often return auxiliary metadata at the same level as the actual value you want in your array. So you have to use SelectSingleNode at that point.
xmlDoc.SetProperty "SelectionNamespaces", xmlNamespaces
Set xmlNodes = xmlDoc.SelectNodes("/myns:.../...")
rws = xmlNodes.length
cols = var '# of columns from the request sent or extrapolate this code to count children of a single node at the level necessary
ReDim xay(1 To rws, 1 To cols)
For Each xNode In xmlNodes
r = r + 1
For c = 1 To UBound(xay, 2)
Set dNode = xNode.ChildNodes(c - 1)
Set dNode = dNode.SelectSingleNode("myns:THE_VALUE")
xay(r, c) = dNode.Text
Next c
Next xNode
I am using the youtube api to direct upload videos like this:
YouTubeRequestSettings setting = new YouTubeRequestSettings("devpa", key, "user", "pass");
YouTubeRequest req = new YouTubeRequest(setting);
Video ytv = new Video();
ytv.Title = "test video1";
ytv.Tags.Add(new MediaCategory("Autos", YouTubeNameTable.CategorySchema));
ytv.Keywords = "test, dev";
ytv.Description = "this is a test video";
ytv.YouTubeEntry.Private = true;
ytv.YouTubeEntry.MediaSource = new MediaFileSource(Server.MapPath("PATH"), "video/mp4");
Video createdVideo = req.Upload(ytv);
But every time i get this error:
Cannot close stream until all bytes are written
Even though *i am uploading small videos with different extinctions (flv,mp4 ..etc) *, what is the problem?
Thanks
You need to set timeout. Like ytv.TimeOut = 100000000;
Actually set the instance of
YouTubeRequestSettings.Timeout = [A LARGER NUMBER]
That's the setting you'll want to use.
Using RubyMotion (for the first time!), I want to use Twitter's search API to retrieve some recent tweets for some users so have put together the class below.
The value of tweets is always an empty array. I suspect that BW::HTTP.get(url) spawns its own thread which is causing the issue.
Really, I just want twitter_search_results to return response.body.to_str but I am not sure how to do this.
How do I use RubyMotion (or BubbleWrap) to put an array of Tweet objects into my UIViewController?
class TweetsController
def initialize
#twitter_accounts = %w(dhh google)
#tweets = []
end
def tweets
twitter_search_results
puts #tweets.count
#tweets
end
def create_tweets(response)
BW::JSON.parse(response)["results"].each do |result|
#tweets << Tweet.new(result)
end
end
def twitter_search_results
query = #twitter_accounts.map{ |account| "from:#{account}" }.join(" OR ")
url = "http://search.twitter.com/search.json?q=#{query}"
BW::HTTP.get(url) do |response|
create_tweets(response.body.to_str)
end
end
end
class TwitterViewController < UIViewController
def viewDidLoad
super
self.view.backgroundColor = UIColor.blueColor
#table = UITableView.alloc.initWithFrame(self.view.bounds)
self.view.addSubview #table
#table.dataSource = self
#tweets_controller = TweetsController.new
end
def initWithNibName(name, bundle: bundle)
super
self.tabBarItem = UITabBarItem.alloc.initWithTitle(
"Twitter",
image: UIImage.imageNamed('twitter.png'),
tag: 1)
self
end
def tableView(tableView, numberOfRowsInSection: section)
#tweets_controller.tweets.length
end
def tableView(tableView, cellForRowAtIndexPath: indexPath)
#reuse_id = "Tweet"
cell = UITableViewCell.alloc.initWithStyle(UITableViewCellStyleDefault, reuseIdentifier:#reuse_id)
cell.textLabel.text = #tweets_controller.tweets[indexPath.row].text
return cell
end
end
class Tweet
attr_reader :created_at, :from_user, :text
def initialize(tweet_result)
#created_at = tweet_result["created_at"]
#from_user = tweet_result["from_user"]
#text = tweet_result["text"]
end
end
Full controller code below. I've also put the project on GitHub
class TweetsController
def initialize
#twitter_accounts = %w(dhh google)
#tweets = []
create_tweets
end
def tweets
#tweets
end
def create_tweets
json_data = twitter_search_results.dataUsingEncoding(NSUTF8StringEncoding)
e = Pointer.new(:object)
dict = NSJSONSerialization.JSONObjectWithData(json_data, options:0, error: e)
dict["results"].each do |result|
p result.class
p result
#tweets << Tweet.new(result)
end
end
def twitter_search_results
query = #twitter_accounts.map{ |account| "from:#{account}" }.join(" OR ")
url_string = "http://search.twitter.com/search.json?q=#{query}"
url_string_escaped = url_string.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
url = NSURL.URLWithString(url_string_escaped)
request = NSURLRequest.requestWithURL(url)
response = nil
error = nil
data = NSURLConnection.sendSynchronousRequest(request, returningResponse: response, error: error)
raise "BOOM!" unless (data.length > 0 && error.nil?)
json = NSString.alloc.initWithData(data, encoding: NSUTF8StringEncoding)
end
end
the issue here is asynchronicity. you're almost there, I think, but the create_tweets method is not called before puts #tweets. In this case, I would recommend using a notification, because I think they are good ;-)
TweetsReady = 'TweetsReady' # constants are nice
NSNotificationCenter.defaultCenter.postNotificationName(TweetsReady, object:#tweets)
In your controller, register for this notification in `viewWillAppear` and unregister in `viewWillDisappear`
NSNotificationCenter.defaultCenter.addObserver(self, selector: 'tweets_ready:', name: TweetsReady, object:nil) # object:nil means 'register for all events, not just ones associated with 'object'
# ...
NSNotificationCenter.defaultCenter.removeObserver(self, name:TweetsReady, object:nil)
and you tweets_ready method should implement your UI changes.
def tweets_ready(notification)
#table.reloadData
end
This site was working properly before I started encountering an error after entering some code. The code was not on the home page but none of the site pages will load now. I restarted the site in IIS and that did not help.
Here is the code that I entered:
'Prepare to parse XML
Set objXML = Server.CreateObject(Microsoft.XMLDOM)
'Set Asynchoronous = false
objXML.async = False
'Load the XML file.
'User Server.MapPath method is the XML is located in your site.
'Else you can use the absolute path.
objXML.Load (Server.MapPath(Products.xml))
'If there is any errors pasring the file the notify
If objXML.parseError.errorCode = 0 Then
'Response.Write(objXML.parseError.reason)
Else objXML.parseError.errorCode <> 0 Then
'Get ALL the Elements by the tag name product
Set products = objXML.getElementsByTagName(product)
Select Case iItemID
Case 1
aParameters = Array(products.item(0).childNodes(0).text, products.item(i).childNodes(2).text, products.item(i).childNodes(2).text)
Case 2
aParameters = Array(products.item(1).childNodes(0).text, products.item(i).childNodes(2).text, products.item(i).childNodes(2).text)
End Select
' Return array containing product info.
GetItemParameters = aParameters
End If
Running IIS in Windows 7 using classic ASP. Editing with Notepad++.
Here is the XML file:
<configuration>
<products>
<product>
<image>
<![CDATA[ /Images/Atlas Gloves.jpg ]]>
</image>
<name>
<![CDATA[ Atlas Nitrile Touch Gloves ]]>
</name>
<description>
<![CDATA[ Atlas Nitrile Touch is available in 6 vibrant colors, and is America’s #1 glove for the Lawn and Garden market. Atlas gloves have a breathable nylon back and are machine washable. Like a “second skin,” these gloves are the most comfortable! Atlas Nitrile gloves are the #1 gardening gloves. Atlas Nitrile gloves act like a "second skin" between the user and their work, offering full dexterity and grip. Atlas Nitrile Gloves are perfect for gardening, but their uses expand to so many places – the woodshop, the workshop, the workplace. ]]>
</description>
<size>
<![CDATA[ Small, Medium ]]>
</size>
<color>
<![CDATA[ Purple, Pink, Green, Orange ]]>
</color>
</product>
</products>
</configuration>
Lets start by getting the code in order:
First we'll create a little helper function which given a parent XML element and an XPath (can be simply a tagName of a child element) will return the text value of an element. In this case I have deliberately choosen to return null if the element isn't found but you could leave the return value empty if you prefer:
Function GetElemText(parentElem, path)
Dim elem: Set elem = parentElem.selectSingleNode(path)
If Not elem Is Nothing Then
GetElemText = elem.text
Else
GetElemText = null
End If
End Function
Now we'll create a little VBScript class which has a field for each of the product elements. This class has a LoadFromXml method which given an product xml element will extract the field values.
Class Product
Public Image
Public Name
Public Description
Public Size
Public Color
Public Sub LoadFromXml(prodElem)
Image = GetElemText(prodElem, "image")
Name = GetElemText(prodElem, "name")
Description = GetElemText(prodElem, "description")
Size = GetElemText(prodElem, "size")
Color = GetElemText(prodElem, "color")
End Sub
End Class
Finally we create a GetProduct function that given the index of a product will load return a Product class instance loaded with the appropriate product details.
Function GetProduct(productIndex)
Dim objXML: Set objXML = Server.CreateObject("MSXML2.DOMDocument.3.0")
objXML.async = False
objXML.setProperty "SelectionLanguage", "XPath"
objXML.Load Server.MapPath("Products.xml") ''# Assumes Products xml in same folder as this script
Dim elem: Set elem = objXML.documentElement.selectSingleNode("products/product[" & productIndex & "]")
If Not elem Is Nothing Then
Set GetProduct = new Product
GetProduct.LoadFromXml elem
Else
Set GetProduct = Nothing
End If
End Function
Note the use named elements eliminates the need for "magic numbers" the values of which you would either have to remember or place in constants and are very fragile. Also the use of XPath as the selection language and a more specific ProgID. All in all much more robust and in this case also working.
If your products xml remains fairly static over the life time of the application consider this variation of:
Function GetProduct(productIndex)
Dim objXML
If IsEmpty(Application.Contents("Products")) Then
Set objXML = Server.CreateObject("MSXML2.FreeThreadedDOMDocument.3.0")
objXML.async = False
objXML.setProperty "SelectionLanguage", "XPath"
Set Application.Contents("Products") = objXML
Else
Set objXML = Application.Contents("Products")
End If
objXML.Load Server.MapPath("Products.xml") ''# Assumes Products xml in same folder as this script
Dim elem: Set elem = objXML.documentElement.selectSingleNode("products/product[" & productIndex & "]")
If Not elem Is Nothing Then
Set GetProduct = new Product
GetProduct.LoadFromXml elem
Else
Set GetProduct = Nothing
End If
End Function
This loads the XML DOM into the application store saving the cost of reloading every time a product is needed.
One other change I would recommend, the reliance of know the ordinal position of a product element in order to retrieve it is quite fragile. Consider adding an id="1" attribute to the product element. It can then be retrieved with:
Dim elem: Set elem = objXML.documentElement.selectSingleNode("products/product[#id=""" & productIndex & """]")
Note: This question can also be found on the WiX mailing list.
I need to be able to check for the existence of an IIS7 website based on the website's description. If the website does not exist I need to cancel the installation. If the website exists I want to continue the installation. I also need to be able to save the site id of the website so that I may use it during an uninstall.
For debugging purposes I have hard coded the website's description. I do not see any indication that a check for the website is being made within the MSI log file. This is the code I am using:
<iis:WebSite Id="IISWEBSITE" Description="Default Web Site" SiteId="*">
<iis:WebAddress Id="IisWebAddress" Port="1"/>
</iis:WebSite>
<Condition Message="Website [IISWEBSITE] not found.">
<![CDATA[IISWEBSITE]]>
</Condition>
Using ORCA I can see that IIsWebAddress and IIsWebSite tables are added to the MSI. The values are:
IIsWebsite
WEB: IISWEBSITE
Description: Default Web Site
KeyAddress: IisWebAddress
Id: -1
IIsWebAddress
Address: IisWebAddress
Web_: IISWEBSITE
Port: 1
Secure: 0
With the above code, the installation is halted with the error message "Website not found". It appears that IISWEBSITE is never getting set. Though, I know that "Default Web Site" exists. I know that I must be missing something, but what?
How can I perform a simple check for the existence of a website in IIS 7?
I too had same problem.
I wrote a custom action to check the version of IIS from registry.
On the basis of registry value create virtual directory
I wrote a custom action in Javascript to do this. If you are assuming IIS7, then you can use the appcmd.exe tool, and just invoke it from within Javascript to get the list of sites. In theory, it's pretty simple to do. But in practice, there's a bunch of hoops you need to jump through.
Here's what I came up with:
function RunAppCmd(command, deleteOutput) {
var shell = new ActiveXObject("WScript.Shell"),
fso = new ActiveXObject("Scripting.FileSystemObject"),
tmpdir = fso.GetSpecialFolder(SpecialFolders.TemporaryFolder),
tmpFileName = fso.BuildPath(tmpdir, fso.GetTempName()),
windir = fso.GetSpecialFolder(SpecialFolders.WindowsFolder),
appcmd = fso.BuildPath(windir,"system32\\inetsrv\\appcmd.exe") + " " + command,
rc;
deleteOutput = deleteOutput || false;
LogMessage("shell.Run("+appcmd+")");
// use cmd.exe to redirect the output
rc = shell.Run("%comspec% /c " + appcmd + "> " + tmpFileName, WindowStyle.Hidden, true);
LogMessage("shell.Run rc = " + rc);
if (deleteOutput) {
fso.DeleteFile(tmpFileName);
}
return {
rc : rc,
outputfile : (deleteOutput) ? null : tmpFileName
};
}
// GetWebSites_Appcmd()
//
// Gets website info using Appcmd.exe, only on IIS7+ .
//
// The return value is an array of JS objects, one per site.
//
function GetWebSites_Appcmd() {
var r, fso, textStream, sites, oneLine, record,
ParseOneLine = function(oneLine) {
// split the string: capture quoted strings, or a string surrounded
// by parens, or lastly, tokens separated by spaces,
var tokens = oneLine.match(/"[^"]+"|\(.+\)|[^ ]+/g),
// split the 3rd string: it is a set of properties separated by colons
props = tokens[2].slice(1,-1),
t2 = props.match(/\w+:.+?(?=,\w+:|$)/g),
bindingsString = t2[1],
ix1 = bindingsString.indexOf(':'),
t3 = bindingsString.substring(ix1+1).split(','),
L1 = t3.length,
bindings = {}, i, split, obj, p2;
for (i=0; i<L1; i++) {
split = t3[i].split('/');
obj = {};
if (split[0] == "net.tcp") {
p2 = split[1].split(':');
obj.port = p2[0];
}
else if (split[0] == "net.pipe") {
p2 = split[1].split(':');
obj.other = p2[0];
}
else if (split[0] == "http") {
p2 = split[1].split(':');
obj.ip = p2[0];
if (p2[1]) {
obj.port = p2[1];
}
obj.hostname = "";
}
else {
p2 = split[1].split(':');
obj.hostname = p2[0];
if (p2[1]) {
obj.port = p2[1];
}
}
bindings[split[0]] = obj;
}
// return the object describing the website
return {
id : t2[0].split(':')[1],
name : "W3SVC/" + t2[0].split(':')[1],
description : tokens[1].slice(1,-1),
bindings : bindings,
state : t2[2].split(':')[1] // started or not
};
};
LogMessage("GetWebSites_Appcmd() ENTER");
r = RunAppCmd("list sites");
if (r.rc !== 0) {
// 0x80004005 == E_FAIL
throw new Exception("ApplicationException", "exec appcmd.exe returned nonzero rc ("+r.rc+")", 0x80004005);
}
fso = new ActiveXObject("Scripting.FileSystemObject");
textStream = fso.OpenTextFile(r.outputfile, OpenMode.ForReading);
sites = [];
// Read from the file and parse the results.
while (!textStream.AtEndOfStream) {
oneLine = textStream.ReadLine();
record = ParseOneLine(oneLine);
LogMessage(" site: " + record.name);
sites.push(record);
}
textStream.Close();
fso.DeleteFile(r.outputfile);
LogMessage("GetWebSites_Appcmd() EXIT");
return sites;
}