Huddled Masses
Using REST API’s from PowerShell with the Dream SDK
Awhile back I wrote a Get-Web script, which I later converted to a compiled Get-Web cmdlet and module (pssnapin) ... adding support for POST forms. At the time, it seemed like GET and POST with some string variables pretty much covered what I’d ever want to do with web pages, however I found a few places where I needed to POST a file attachment, and then a couple of full-on REST APIs, which use not just GET and POST, but PUT and DELETE as well.
While I was playing with scripting the REST API for DekiWiki … I experimented with the MindTouch Dream SDK … and as a result, I’ve refactored the part of that module that actually does the HTTP work out into a little PowerShell 1.0 compatible script library. Of course, it depends on several dll’s (mindtouch.core.dll, mindtouch.dream.dll, and SgmlReaderDll.dll and log4net.dll) from the Dream SDK, which you can download from SourceForge.
Example Uses## Retrieve Google search results:
function Get-Google {
Invoke-Http GET http://www.google.com/search @{q=$args} |
Receive-Http Xml "//h3[@class='r']/a" | Select href, InnerText
}
## Download a file from a web page
function Get-WebFile($url,$cred) {
Invoke-Http GET $url -auth $cred | Receive-Http File
}
## Upload a PowerShell script to PasteBin
function Send-Paste {
PARAM($PastebinURI="http://posh.jaykul.com/p/",[IO.FileInfo]$file)
PROCESS {
if($_){[IO.FileInfo]$file=$_}
if($file.Exists) {
$ofs="`n"
$result = Invoke-Http POST $PastebinURI @{
format="posh" # PowerShell
expiry="d" # (d)ay or (m)onth or (f)orever
poster=$([Security.Principal.WindowsIdentity]::GetCurrent().Name.Split("\")[-1])
code2="$((gc $file) -replace "http://","http``://")" # To get past the spam filter.
paste="Send"
} -Type FORM_URLENCODED -Wait
$xml = $result.AsDocument().ToXml()
write-output $xml.SelectSingleNode("//*[@class='highlight']/*").href
} else { throw "File Not Found" }
}}
#########################################################
## Example, to download my PasswordRequired.dll:
$cred = new-object System.Management.Automation.PSCredential `
"test", $(ConvertTo-SecureString "password")
Get-WebFile http://huddledmasses.org/downloads/PasswordRequired.dll $cred
## Example, to upload a script called TestScript.ps1 to the Pastebin we use on IRC:
ls TestScript.ps1 | Send-Paste "http://posh.jaykul.com/p/"
There are basically two main functions: Invoke-Http and Receive-Http. There are also a few extra functions which are mostly just for use by these two functions, and then Set-HttpDefaultUrl and Set-HttpCredential which you can use to simplify your scripts. I intend to rewrite these as cmdlets in C# and contribute them to the PowerShell Community Extensions along with some of my other networking cmdlets (Get-DNS), so if you have any suggestions, I’d love to hear them!
Invoke-HttpInvokes HTTP verbs (like GET, POST, PUT, and DELETE) against a URL with optional parameters (eg: name = value), input, etc … and returns the output as a MindTouch.Dream.Result or (optionally) as a MindTouch.Dream.DreamMessage …
SyntaxInvoke-Http [-Verb] <String> [[-Path] <String>] [[-With] <Hashtable>] [[-Content] <Object>] [[-Type] <String>] [[-Authenticate] <String>] [-WaitForResponse]
Parameters VerbThe HTTP verb you want to invoke. Defaults to GET, but can be anything your HTTP server supports.
PathThe URL you want to invoke against. Can be a partial URL, if you first call Set-HttpDefaultURL first. You can also specify this as an array or a hashtable. This is particularly useful if you’re specifying a partial URL, and your URL is built based on logic in your code. If you pass an array, the values in the array are joined with forward-slashes. If you pass a hashtable, the keys and values are appended joined with forward-slashes, but the values are double encoded (by UrlEncode), ie: $key/$(UrlEncode(UrlEncode($value))
WithThe parameters to be passed with the verb call, as a hashtable. (these are particularly used for POST, etc). These are passed in the query as named parameters, with the values encoded.
ContentAnything you want to pass as content. In actuality, we really only handle three things: XML Documents, file paths (the file will be used as content), and everything else is passed as simple text. If you need other data types, the “DreamMessage” that the scripts depend on can handle all sorts of data types, so you’ll just need to add it around line 50 of the script.
TypeThis is the type of content. We can automatically deduce this for XML or File objects, but if you want to force a type, you should use this parameter. For instance, to fake a web form submission, you need to specify the type as FORM or FORM_URLENCODED but you can also specify the type as any of: ATOM, BINARY, BMP, CSS, DREAM_EXCEPTION, FORM_URLENCODED, GIF, HTML, HTTP, JPEG, JS, JSON, MULTIPART_MIXED, NOTHING (mimetype -/-), ANY (mimetype */*), PDF, PHP, PNG, RDF, RELAXNG, SVG, ANY_TEXT (mimetype text/*), TEXT (mimetype text/plain, with the ISO-8859-1 encoding), TEXT_UTF8 (mimetype text/plain, with UTF8 encoding), TEXT_XML, TIFF, VCAL, VERSIT, XHTML, XML, XRDS.
AuthenticateYou can pass either a boolean (in which case the HTTPRest scripts will use a default $global:HttpRestCredential which it will prompt for if you haven’t set it), or an actual PSCredential or NetworkCredential object. In any case, this causes the invoke to contain network credentials. Note that sometimes you may need to pre-authenticate before making calls to a web-service.
WaitForResponseA switch parameter that causes the query to wait until the response completes downloading, and return a DreamResponse instead of returning an incomplete Result ... this is useful when you know the response is a simple XML file and you need to parse it to determine whether your call has succeeded. If you need to actually get the data out, or if you just need to know the method was invoked successfully, you shouldn’t pass this parameter.
Receive-HttpReceives the response or result from a Dream.Result or DreamMessage. as XML, XDoc, text, raw bytes, or even to file. This function is really only useful to receive the output of Invoke-Http when you need the response content.
SyntaxReceive-Http [[-Output] <string:[Xml|File|Text|Bytes]>] [[-Path] <string>] [[-InputObject] <MindTouch.Dream.Result<MindTouch.Dream.DreamMessage>>]
Receive-Http [[-Output] <string:[Xml|File|Text|Bytes]>] [[-Path] <string>] [[-InputObject] <MindTouch.Dream.DreamMessage>]
Parameters OutputThe TYPE of output you want:
- Xml will return an XmlDocument — or XmlNode(s) if you pass a Path.
- File will write to file — currently, you must pass a path, I didn’t make it guess it automatically.
- Text will output text — in the case where the actual output is XML, this returns the InnerText.
- Bytes returns an array of bytes…
- XDoc returns a MindTouch.Dream.XDoc (not the .Net native class), which has a boatload of extra web-related methods and features you might find useful such as accepting XPath operators as indexers.
A string containing the path for file output or XPath selector for XML, XDoc, and Text output
InputObjectThe input must be either a Result<DreamMessage> or a DreamMessage itself. Values passed on the pipeline are accepted into this parameter.
Set-HttpDefaultUrlThis takes the passed URI and sets it as the base (default) URL for the Invoke-Http function (if you set this, any path passed to Invoke-Http is treated as relative to this path).
Set-HttpCredentialThis takes a PSCredential or NetworkCredential and stores it as a PSCredential for use by the Invoke-Http function. Particularly useful when you want to script a bunch of authenticated methods without being re-prompted. Note that if you don’t pass a credential, you’ll be prompted for one the usual way, so calling just Set-HttpCredential is sufficient when working on the command-line.
MiscellaneousThere are other functions in here, a few should be considered “internal” functions (when this is a module, they would not be exported: Get-DreamMessage, Get-DreamPlug, Get-HttpCredential) and a few of which came out of my utilities functions (like Encode-Twice, and Join-Url, Get-FileName, Get-UtcTime, and Get-CredentialBetter)...
CodeProject Report Accepted — Defense Scheduled!
Well, my project report has been accepted and I’m defending my Master’s on December 2nd, 2008 at 4:30pm. You’re all invited to come and see how slick my Self-Organizing Maps Recommender is, from database to PowerShell cmdlets and all.
Report AbstractThis project comprises designing and implementing a hybrid recommender system for web–pages which uses data from a social tagging system to recommend interesting items to users. For this implementation, the tagging data comes from del.icio.us, the oldest and largest public social bookmarking or tagging system for web pages. The system clusters items using a pair of self–organizing maps (SOM) networks and allows users to see the system’s evaluation of their region of interest, or set their own regions.
The focus of this project was the web-scraper for gathering tagged URLs from del.icio.us, and the recommender system. The SOM networks are built using the GHSOM implementation, and several recommenders were built to compare results: one using a single map for URLs and users and the other using separate maps to compare the relative quality of the recommendations.
Update to PoshNet and Get-DNS
This is just a short note about Get-DNS (my dig for PowerShell cmdlet). Someone emailed me to ask how they could specify the dns servers, and the answer was that you couldn’t. So then I looked a bit deeper, and realized that the code I copied from CodeProject already had support for that, so I whipped up a new version which you can download here (source here under GPL).
The only real difference is the addition of a -DnsServers parameter which accepts one or more dns server ip addresses. NOTE: they must be IP Addresses, not domain names (sorry, I was lazy, Maybe if someone I’ll make sure that gets tweaked eventually). You may include the port (eg: 71.6.202.220:53) but you don’t have to unless it’s non-standard (ie: not port 53). You may specify multiple servers if you need a fallback … and as always, if you leave it off, the cmdlet will use your default DNS servers.
Have fun, and uhm, feel free to tip the waiter.
PowerShell and Hashtable oddities
Hashtables are IEnumerable, but they don’t behave that way in PowerShell … this seems to cause all sorts of odd behavior and such, so I thought I’d write up all the examples I can think of in one place. That means this post is going to be a little bit rambling, so please bear with me.
PowerShell enumerates IEnumerableadd-type @"
<code lang="csharp">
using System.Collections;
public class enumer : IEnumerable {
public IEnumerator GetEnumerator() {
for(int i = 0; i < 10; i++) {
yield return i;
}
}
}
"@
$e -is [Collections.IEnumerable] # is true
$e = new-object enumer
$e # will output 0..9 to the console
$e.GetEnumerator() # will output the same
$e | measure-object # will show a count of 10.
$table = @{test="This is a test";exam="this is an exam";defense="defend your thesis"}
$table -is [Collections.IEnumerable] # is true too!
$table # will output three DictionaryEntry items
$table.GetEnumerator() # will output the same thing
$table.Count # will output 3
$table | measure-object # will output 1! WHAT!?
It turns out that in the case of Hashtables, PowerShell does NOT enumerate them into the pipeline. Instead, it passes the entire Hashtable object. Of course, nobody realizes this … because the ouput cmdlets unwrap them (what?!).
But there are bugs caused by special treatmentThe first bug is in Add-Member, which doesn’t work on Hashtables until you’ve already used the Hashtable.
$table = @{test="This is a test";exam="this is an exam";defense="defend your thesis"}
Add-Member -in $table NoteProperty Quiz "Surprise, hope you're ready!"
$table.Quiz # It's not there! There is NO OUTPUT
Add-Member -in $table NoteProperty Quiz "Surprise, hope you're ready!"
$table.Quiz # This time it works ...
NOT ONLY does Add-Member not work the first time, it’s not just a matter of calling it twice: you just have to try to access something in the hashtable before you can use Add-Member on it:
$table = @{test="This is a test";exam="this is an exam";defense="defend your thesis"}
Add-Member -in $table NoteProperty Puzzle "How on earth does this work?"
Add-Member -in $table NoteProperty Quiz "Surprise, hope you're ready!"
$table.Quiz # It's not there! There is NO OUTPUT
Add-Member -in $table NoteProperty Quiz "Surprise, hope you're ready!"
Add-Member -in $table NoteProperty Puzzle "How on earth does this work?"
$table | gm -type NoteProperty | %{ $_.Name } # Output: Puzzle, Quiz
Here’s another buggy manifestation, in the Formatting cmdlets. This time, the Format-* cmdlets unroll the hashtable … to make it look like it’s being enumerated the way it should be.
$table = @{test="This is a test";exam="this is an exam";defense="defend your thesis"}
## Prime it, and then Add-Member won't work
$table.PrimeTheHashtableSoWeCanAddMember
Add-Member -in $table NoteProperty Quiz "Surprise, hope you're ready!"
## Note how Measure-Object and Get-Member operate on the HASHTABLE
## There's only a single item, of course...
$table | measure-object | %{$_.Count}
## And we have 7 Properties, plus the Quiz NoteProperty
$table | get-member -type Properties | %{ $_.Name }
## But Format-List shows the properties of the ITEMS
## So we think we can list those properties like:
$table | format-list *
Clearly, the Format-* cmdlets have magic code that unwraps hashtables. Which just leads to even more confusion: $table looks the same (in the console output) as $table.GetEnumerator() ... but it doesn’t behave the same way, EXCEPT to the format cmdlets.
PowerShell 2.0 uses Hashtables moreIn PowerShell 2.0, the PowerShell team is adding another special feature based on hashtables (which appears at first to be based on IEnumerables):
Jeffrey Snover gave an example of splatting in his PDC presentation. Splatting is where a collection is unwrapped so that you can take an array of values and pass one to each parameter of a cmdlet or function. But in Jeffrey Snover’s demo, he splatted a hashtable. Basically, the hashtable keys are matched up to parameter names as though they had been specified by name. That made me wonder why splatting can’t work with custom objects, but after investigating a bit, I’m actually frustrated with the inconsistency of how hashtables are treated in Posh.
- In the splatting scenario they are unrolled as a collection of named parameters …
- If you pipe them, they’re treated as a single object instead of being unrolled …
- Even though you can access hashtable items using dotted property syntax, you can’t use them to set ValueFromPipelineByPropertyName values, because they aren’t really properties.
The new splatting feature seems to only work with simple arrays and hashtables … adding yet another scenario where the hashtable is being treated specially (even though it doesn’t need to be: if they just splatted IEnumerable, we could work with List, and any IEnumerable of DictionaryEntry objects, or KeyValuePair could be matched by name … that would make hashtables work, but it would also let you use the more powerful generic collections, etc.
You know what would be cool? If I could splat any object (like a custom PSObject that I have added members to), and have it’s property names matched to parameter names as though all the parameters had ValueFromPipelineByPropertyName set.
You know what would be really cool? If I could specify that I want pipeline objects splatted, forcing ALL parameters to be treated as ValueFromPipelineByPropertyName, without needing to use: ForEach-Object { Test-Splatting @_ } … maybe a syntax like: Get-HashTablesToSplat | Test-Splatting @@ …
A Better Get-Credential in one line of code
For too long I have ignored the deficiencies in Get-Credential, so now I am going to fix them. Ready?
function Get-Credential($caption,$msg,$domain,$name){$Host.UI.PromptForCredential($caption,$msg,$name,$domain)}
Ok, that’s better than the default, whew!
At least you can specify the prompt text and the domain and default user name … but there are so many other options that are missing from that dialog —like remembering my credentials for goodness sakes. I know many places forbid using the “remember” option for credentials, but why is that decision not up to me?
Well, I can’t make all of those options appear (at least, not without compiling a pinvoke function to call the Win32 API) nor can I force PowerShell to use the new Vista/2008 Credential function (which is Common Criteria compliant in Vista) instead of the older CredUIPromptForCredentials ... but I can give you the most requested feature for Get-Credential: a -Console option to force the prompt to happen in the console instead of in a “CredUI” pop up.
Note: I kind-of messed up here, this will break if you’re used to using the -Credential parameter for Get-Credential to provide a default user name. I’ll fix it shortly.
Clip.exe and the missing Paste.exe
One of the guys on #PowerShell in IRC was asking about how to get the contents of the clipboard in PowerShell, and after we had tried several different scripts, we realized that in Version 1, because PowerShell runs in an MTA thread, you cannot access the clipboard without creating a new thread (in STA mode). After I got done ranting, and pointing out that everyone needs to use PowerShell v2 (yeah, it’s still in CTP, so I was mostly kidding) in -STA mode … I whipped together the C# one liner and stuck it into a class with instructions for compiling, and figured I might as well share.
The code is below. There are two files: Clip.cs and Paste.cs … you probably only need Paste.cs, because Clip.exe is already included in Windows after Windows XP, but for the sake of completeness (and because it was just a few lines), I wrote them both.
To use them, you must compile them using Csc.exe, which is included as part of the .Net Framework. If it’s not already on your path, it’s in the folder for your most recent .Net Framework version (eg: C:\WINDOWS\Microsoft.NET\Framework\v3.5\csc.exe or C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc.exe). All you have to do is run: csc Paste.cs and csc Clip.cs and you should end up with a Paste.exe and Clip.exe which you can use to copy and paste from the command-line…
Paste.csusing System;
using System.Windows.Forms;
using System.Threading;
namespace Huddled {
public class Paste {
[STAThread]
static void Main( string[] args )
{
foreach(string line in Clipboard.GetText().Split(
new string[]{"\r\n","\n"},
StringSplitOptions.None ))
Console.WriteLine( line );
}
}
}
Clip.cs
using System;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace Huddled {
public class Clip {
[STAThread]
static void Main( string[] args )
{
string s;
StringBuilder output = new StringBuilder( string.Join(" ", args) );
while ((s = Console.ReadLine()) != null)
output.AppendLine(s);
Clipboard.SetText( output.ToString() );
}
}
}
Of DekiWiki and PowerShell: A Script (Module)
You may not have noticed, but there are several wiki’s with PowerShell content in them, but generally speaking, they are the results of the effort of a single person (or very a small group) ... and none of them seems to have captured the attention of the general PowerShell community. Certainly, the PowerShell Community site has never had it’s own wiki.
I’ve been pushing for an unofficial community wiki for awhile, and recently, I had an opportunity to try out MindTouch DekiWiki at work, and I was so impressed, I decided to take it upon myself to demo to Halr9000 and a few of the MVPs (and now … to all of you!) why I think we should set up a DekiWiki for our community 
It’s really quite simple. On top of having some really awesome features like hierarchical pages, a WYSIWYG editor, and built-in lucene search and file attachments … it also has a REST api, and you can edit the pages in plain-old HTML (not really surprising considering the WYSIWYG interface). The combination of these two things, and the MindTouch Dream sdk (along with good examples of using it with the Deki REST API) make it so easy to work with in .Net and PowerShell, that I just felt compelled to share my scripts…
First is the conversion script, which is based on New-HtmlHelp, some excellent work by one of the VMWare PowerShell guys …
But then, the more interesting stuff, written on top of MindTouch Dream (you need to download Dream from SourceForge.net to use it, or just grab both my scripts and the dlls in a 7-zip archive), a series of cmdlets which abstract away some of the intricacies of the DekiWiki API. So far, I’ve only written a few … most notably: Set-DekiContent, which will allow you to write a little loop like this:
foreach($cmd in (gcm -type cmdlet | ? { $_.PsSnapin -like "Microsoft.PowerShell*" })) {
Get-Help $cmd.Name -full -EA "SilentlyContinue" | ConvertTo-DekiContent Cmdlet_Help |
%{ Set-DekiContent "Cmdlet_Help/$($cmd.PSSnapin)/$($cmd.Name)" $_ }
}
Which produces this PowerShell Cmdlet Help … pretty cool, right? By the way, I think PowerShell Community is going to host our own wiki, so don’t put too much effort into that freebie from Wik.is until you hear more from me
...
Without futher ado, here’s the script module:
Stupid Laws: the Don’t Protect Me edition
This is just a rant about crazy politicians and the over-legislation of protecting people against themselves.
http://www.motorcycle.com/news/passenger-age-cap-proposed-in-ontario-87568.html
This law would (if passed) prevent driving a motorcycle on a highway if another person under the age of 14 years is a passenger on the motorcycle. In support of the law, Helena Jaczek “cited statistics that show 199 motorcycle passengers under the age of 15 were injured from 1995-2005” ...
Seriously?Let’s see, 200 injured people in 10 years? Wow, that does sound dangerous
. What other sorts of things are that dangerous? I mean, if we’re going to outlaw exhilarating fun just because you might get hurt doing it … clearly we want to start with the most dangerous stuff, right?
How about skateboarding? According to the American Academy of Pediatrics, skateboard-related injuries account for an estimated 50,000 emergency department visits and 1500 hospitalizations among children and adolescents in the United States each year.
How about roller coasters? The International Association of Amusement Parks and Attractions states that 1,713 ride related injuries occurred in 2005, and they average four fatalities a year going back to 1987! Oh my goodness! And of those injuries, over seven percent resulted in at least overnight stays in a hospital. (Of course, that’s not counting the 300 million other guests who safely enjoyed 1.8 billion rides).
Ms. Jaczek, maybe you’d like to start with actual statistics, like: what percentage of motorcycle rides ended up in an accident? A common one is injuries per “participant day” — that is, how many people participated in an activity each day. So, in order of priority, here’s a few things you should outlaw just to make your young people safer:
- Roller skating (912 emergency room treated injuries per million participant days)
- Basketball (799 per million)
- Football (704 per million)
- Soccer (405 per million)
- Fishing (85 injuries per million participant days)
- Golf (53 per million)
- Use of exercise equipment (nine injuries per million participant days)
- Billiards (eight per million)
- Camping (five per million)
Incidentally, according to the IAAP, the number for amusement parks is something like 0.00133 per million … and according to the Insurance Information Institute there were 6.2 million motorcycles on U.S. roads in 2005 … accounting for 0.4 percent of vehicle miles traveled in 2006 … with over 104,000 accidents and 4,810 deaths (the highest number up to that time since 1981). If we guess that those motorcycles get ridden for about half the year, maybe 5 times a week … that would be around 620 million “participant days” and would add up to about 0.00016 accidents per participant day.
As someone who neither motorcycles nor golfs, I think you should clearly outlaw Golf, since it not only has a higher rate of injury, but also restricts all that land from use by the rest of us who just want a place to throw a flying disc
PS: This politician was from Ontario, but the same idiots exist in New York: if you want to know what you should be spending your time on, I’ll give you one hint: stop spending money we don’t have.
PowerShell User Group and Community News!
I keep forgetting to mention this! I’m speaking at the next PowerShell Virtual User Group this Thursday, November 13th at 12PM EST. Join us if you’re interested in any of my PowerShell CodePlex projects (PoshConsole, PoshCode, WASP). Actually, join us even if you’re not, because there’s going to be a demo of the latest version of PowerShell Plus by the folks from Idera, and Nathan Winters will be discussing managing Exchange with PowerShell.
Also, the PowerShell Community is running a survey about the possibility of a PowerShell convention, and what you’re interested in seeing at one… it’ll just take a moment 
PowerShell Sessions 2008
Props to Luciano Guereche for the download links (and icons)... incidentally, the url format for those links works for any of the sessions, so you can just replace the “ES24” with whichever session you want and use my Get-Web Cmdlet or even the WGet Script to pull down whatever ones you want in a loop … or go to Luciano’s front page
Greg Duncan’s page where he has links to every single session that he’s tested to make sure he only links to the ones that exist (look here for instructions on how to use DownThemAll! to scrape whichever copy you want).
PDC 2008 – ES24 PowerShell: Creating Manageable Web Services
Presenter: Jeffrey Snover
![]()
Update: TechEd Barcelona Session
TechEd Barcelona 2008 – MGT322 PowerShell V2 – the next stage in the IT revolution
Presenter: Jeffrey Snover
(streaming only)
TechEd Barcelona 2008 – MGT318 Managing Large Infrastructures with PowerShell v2
Presenter: Jeffrey Snover
(streaming only)
TechEd Barcelona 2008 – PowerShell Tech Talk: Group forum
PowerShell community all-stars
![]()
![]()
MP3
TFS PowerTools
The VSTS 2008 TFS Power Tools made their October Release (yes, in November) ... and it’s packed full of yummy goodness.
- A Windows Shell Extension (think Tortoise SVN)
- A new tfpt command: searchcs for searching checkins/date/user/path/comments, etc.
- Support for shelvesets in the tfpt command-line app.
- PowerShell Cmdlets with pipeline support (just starting, but there’s 17 cmdlets already)
- Team Members support: start Chat/phone/video calls through Communicator or Live Messenger (you can write plugins for others), see pending changes, get notifications on Checkins, etc.
I think you can see that some of this will work with CodePlex, and some wont, but I’m most interested in the shell extension and PowerShell cmdlets! I wonder if they should implement a PSProvider…
What we mean by “Apple Fanboi”

Image via Wikipedia (not an iOcarina)
There’s an article on TechCrunch about ...How You Build A Great iPhone App which epitomizes the negative feelings I have about Apple fans.
Smule has done it again. The company behind the ingenious lighter app that took the iPhone by storm a few months ago has launched Ocarina, a networked musical instrument … perfectly executed ... by being first to market with this app, Smule has safeguarded itself against the competition …
So, uhm … an Ocarina. Really? From the company that brought you … the ingenious lighter app.
Really? Ingenious? I can’t begin to express my disdain for the twits that hold up cellphones at concerts … installing an app to make it look like a lighter just shows that you planned ahead of time to be a twit.
![[groupwoot]](http://HuddledMasses.org/wordpress/wp-content/plugins/smilingmasses/groupwoot.gif)
![[groupwoot]](http://HuddledMasses.org/wordpress/wp-content/plugins/smilingmasses/groupwoot.gif)
A networked Ocarina. Have you ever heard an Ocarina? I’m not saying that there’s no such thing as hauntingly beautiful ocarina tunes — I’ve heard professional players in Costa Rica — but the probability of the few people who have that skill owning an iPhone and sitting around playing tunes on it so you can listen is vanishingly small, and if you listen to anyone else, it sounds like a tribal version of a child’s recorder. You know, if they created a way to tip from the iPhone over that network, I bet you could get some of the buskers online… look for players in Central and South America 
If you don’t like what you’re listening to, you can hit the “next” button to start playing a new song (you’ll probably be using this button often, as many of the people playing are awful).
Yeah. Sounds great.
The worst thing is, I’m pretty sure the TechCrunch guys speak truthfully from experience when they mention “the competition” ... those iPhone app store developers are so ingenious. ![[greedy]](http://HuddledMasses.org/wordpress/wp-content/plugins/smilingmasses/greedy.gif)


Theme by