{"id":374,"date":"2012-05-19T15:06:56","date_gmt":"2012-05-19T22:06:56","guid":{"rendered":"http:\/\/www.skierpage.com\/blog\/?p=374"},"modified":"2012-05-19T15:06:56","modified_gmt":"2012-05-19T22:06:56","slug":"web-automating-bits-of-sprint-picture-mail","status":"publish","type":"post","link":"https:\/\/www.skierpage.com\/blog\/2012\/05\/web-automating-bits-of-sprint-picture-mail\/","title":{"rendered":"web: automating bits of Sprint Picture Mail"},"content":{"rendered":"<p>(The good programmer is lazy; instead of typing something over and over, she writes a script to automate the steps. The wizard programmer writes a set of object classes that model the problem at hand, and tells those objects to behave. The genius programmer writes a new language suitable for the problem domain.)<\/p>\n<p>I wanted to automate <a title=\"my blog post\" href=\"\/blog\/2012\/05\/web-getting-your-pictures-off-sprint-picture-mail\/\">getting info about my pictures<\/a> from the soon-to-be-discontinued Sprint Picture Mail website.<\/p>\n<h2>The script so far<\/h2>\n<ol>\n<li>Login at the\u00a0 special log-in URL <a href=\"http:\/\/pictures.sprintpcs.com\/downloadmymedia\">http:\/\/pictures.sprintpcs.com\/downloadmymedia<\/a><\/li>\n<li>In a new tab, visit <a href=\"http:\/\/pictures.sprintpcs.com\/ajax\/view\/getAlbumListResults.do?sortCode=5\">http:\/\/pictures.sprintpcs.com\/ajax\/view\/getAlbumListResults.do?sortCode=5<\/a> which should produce a lot of <tt>[ {\"panel\":\"albums\", \"url\":\u00a0 ...,<\/tt> stuff.<\/li>\n<li>Select the entire following one-line bookmarklet, copy it, switch back to the tab in\u00a0 your browser where the [{&#8220;panel&#8221; blah blah stuff is, paste it into the location field, then press [Enter].<\/li>\n<\/ol>\n<pre>javascript:var linkWin=open(\"\",\"albums\",\"width=400,height=600,scrollbars,resizable\"),lw=linkWin.document,isJSON=!0;try{albums=JSON.parse(document.body.innerHTML.replace(\/\\\\'\/,\"'\"))}catch(e){isJSON=!1}if(isJSON&amp;&amp;typeof albums==\"object\"&amp;&amp;Array.isArray(albums)&amp;&amp;albums.length&gt;0){baseURL=document.baseURI.match(\/(.*?\\\/\\\/.*?)\\\/\/)[1],lw.write('&lt;base href=\"'+baseURL+'\" target=\"_blank\"&gt;\\n'),lw.write(\"&lt;ol&gt;\\n\");for(i=0;i&lt;albums.length;i++){var album=albums[i];lw.write(\"\u00a0 &lt;li&gt;\"+album.title.link(\"\/ui-refresh\/getMediaContainerJSON.do?componentType=mediaDetail&amp;sortCode=17&amp;containerID=\"+album.containerID)+\" - \"+album.coverTitle+\"&lt;\/li&gt;\\n\\n\")}lw.write(\"&lt;\/ol&gt;\\n\")}else lw.write(\"&lt;p&gt;window contents do not appear to be Sprint Picture Mail JSON info&lt;\/p&gt;\\n\"),lw.write('&lt;p&gt;Try visiting &lt;a href=\"http:\/\/pictures.sprintpcs.com\/ajax\/view\/getAlbumListResults.do?sortCode=5\"&gt;http:\/\/pictures.sprintpcs.com\/ajax\/view\/getAlbumListResults.do?sortCode=5&lt;\/a&gt;&lt;\/p&gt;\\n');<\/pre>\n<p>(If you want to read the code, <a title=\"source for Sprint Picture Mail JavaScript bookmarklet\" href=\"https:\/\/github.com\/skierpage\/sprint_picturemail\/blob\/master\/spm_albums.js\">here&#8217;s the original un-uglified easier-to-read version<\/a>.) This should pop up a new browser window that either lists your Picture Mail albums, or tells you what to do.<\/p>\n<p>Then for each album,<\/p>\n<ul>\n<li>right-click the link and Save Link As.. <em>Album name<\/em>.json. This file contains details for each picture in the album (description, creationDate, etc.)<\/li>\n<li>If it&#8217;s got less than 50 pictures and videos combined, then back in your SPM tab, drag the album&#8217;s thumbnail into &#8216;Download to my PC&#8217;<\/li>\n<\/ul>\n<h2>Bookmarklet background<\/h2>\n<p>Once you&#8217;re logged in to SPM, a bookmarklet can modify the contents of a web page to do something useful.<\/p>\n<p>Googling found a suitable bookmarklet half-way down a page of <a title=\"forum page full of bookmarklets\" href=\"http:\/\/www.pcreview.co.uk\/forums\/bookmarklets-marklets-favelet-powercons-re-link-grabber-bert-hibbard-re-web-page-info-david-lewis-t1961707.html\">bookmarklets from 2003<\/a>:<\/p>\n<pre>javascript:WN7z=open('','Z6','width=400,height=200,scrollbars,resizable,menubar');\n DL5e=document.links;with(WN7z.document){write('&lt;base\n target=_blank&gt;');for(lKi=0;lKi&lt;DL5e.length;lKi++){write(DL5e[lKi].toString().link(DL5e[lKi])+'&lt;br&gt;&lt;br&gt;')};void(close())}<\/pre>\n<p>Copy and paste this babble into your location bar and press enter, and it pops up a new window that lists all the URLs on a page!<\/p>\n<p>It&#8217;s hard to read because a bookmarklet is a URL, just like <tt>http:\/\/example.com\/path<\/tt> but starting with <tt>javascript:<\/tt> , so it has to be a single line. First thing to do is to find a tool to convert between compressed rubbish and a decent multi-line program&#8230; <a title=\"Github page for UglifyJS\" href=\"https:\/\/github.com\/mishoo\/UglifyJS\">UglifyJS<\/a>. Yay, more tools in JavaScript, it&#8217;s taking over the world.<\/p>\n<pre>npm install uglify-js\nsed 's\/javascript:\/\/' &lt; pcreview_bookmarklet \\\n\u00a0 | .\/node_modules\/uglify-js\/bin\/uglifyjs -b \\\n\u00a0 &gt; spm.js<\/pre>\n<p>Now I&#8217;ve got a multi-line JavaScript file. To turn this back into a bookmarklet,<\/p>\n<pre>.\/node_modules\/uglify-js\/bin\/uglifyjs spm.js \\\n  \u00a0| sed 's\/^\/javascript:\/'<\/pre>\n<p>I then paste the contents of the resulting single-line JavaScript into my browser&#8217;s location bar; to automate this step, pipe it into a clipboard utility such as <tt>xsel -b<\/tt>.<\/p>\n<p>Now, to do the coding&#8230;<\/p>\n<h3>Generating an album list<\/h3>\n<p>Once logged in, a useful URL to visit is <a href=\"http:\/\/pictures.sprintpcs.com\/ajax\/view\/getAlbumListResults.do?sortCode=5\">http:\/\/pictures.sprintpcs.com\/ajax\/view\/getAlbumListResults.do?sortCode=5<\/a><\/p>\n<p>this returns a JSON structure of all my picture albums. To parse it, just<\/p>\n<pre>albums=JSON.parse(document.body.innerHTML);<\/pre>\n<p>this fails! Sprint took the title of one album, 2512 house (DL&#8217;d!), and turned it into<\/p>\n<pre>\"title\": \"2512 house (DL\\'d!)\"<\/pre>\n<p>but this is invalid JSON, you <em>don&#8217;t<\/em> put a backslash in front of single quote.<\/p>\n<p>OK, so<\/p>\n<pre>albums=JSON.parse(document.body.innerHTML.replace(\/\\\\'\/, \"'\"));<\/pre>\n<p>Now iterate through this, for each album fabricate<\/p>\n<ol>\n<li>a\u00a0 link to its details<\/li>\n<li>and if it has fewer than 50 pictures, a link to download it.<\/li>\n<\/ol>\n<p>The first is easy, since my research found the URL that returns the album detail is just \/getMediaContainerJSON.do?componentType=mediaDetail&amp;sortCode=17&amp;containerID=<em>MMMMMMMM<\/em>)<\/p>\n<p>The second is much harder, as SPM seems to fabricate a hidden form that enumerates the elementIDs and extensions of pictures to download, and then dynamically modifies this to turn it into a POST to a \/downloadzip\/sprintpictures.zip?machineid=<em>xxxx<\/em> URL. This is hard to do with a bookmarklet.<\/p>\n<h2>Parsing an album&#8217;s media details<\/h2>\n<p>Once you&#8217;ve saved an album&#8217;s details as <em>Album name<\/em>.json\u00a0 you want to walk the structure looking for interesting info and warning about unexpected values. You could do this in another browser bookmarklet, but I want to change the names and modification times of files based on the info, and you can&#8217;t perform those operations in browser JavaScript. But the nice <a title=\"project site\" href=\"http:\/\/nodejs.org\/\">node.js<\/a> engine can. The script is <a title=\"script source on github\" href=\"https:\/\/github.com\/skierpage\/sprint_picturemail\/blob\/master\/spm_processmetadata.js\">here<\/a>.<\/p>\n<h2>Attempts\u00a0 to trigger downloads<\/h2>\n<p>Getting album and picture metadata is nice, but the most time-consuming part is selecting and dragging albums and pictures to [Download to my PC].<\/p>\n<p>I started writing a bookmarklet that would make SPM&#8217;s download form&#8217;s hidden form fields visible and label them, so you could then fill it with your own element IDs and drive your own arbitrary download. But you&#8217;d have to interactively step in to provide the machine ID, download URL, etc.<\/p>\n<p>It would be better to do it by driving the whole process from node.js. The script takes a login and password, supplies them in a request to some SPM login URL, then remember cookies sent back by SPM and uses them in subsequent requests. Useful links for this: <a title=\"StackOverflow Q+A\" href=\"http:\/\/stackoverflow.com\/questions\/6158933\/http-post-request-in-node-js\">How to POST in node.js<\/a>, <a title=\"StackOverflow Q+A\" href=\"http:\/\/stackoverflow.com\/questions\/9536516\/get-a-cookie-with-nodejs\">logging in with cookie<\/a>.<\/p>\n<p>But I can&#8217;t figure out SPM login, it&#8217;s a bear. Their login form&#8217;s &lt;noscript&gt; action is to call authenticate.jsp, but instead it does an AJAX in-page call to a different login URL, which returns script to check if cookies are enabled and then redirects. I tried both URLs using curl and node.js and even though I get a pmjsessionid cookie back, my subsequent requests fail. SPM must be looking for user agent or a particular sequence of requests, or something else.<em> Help wanted!<br \/>\n<\/em><\/p>\n<h2>To Dos<\/h2>\n<p>These kinds of explorations reveal shortcomings in the source material. In an open source read-write web, you can fix them:<\/p>\n<ul>\n<li>Rewrite the Wikipedia bookmarklet article: &#8220;it has been found that passive voice is to be avoided&#8221;, also you can just paste the code in without creating a bookmark<\/li>\n<li>File an issue for UglifyJS to support a &#8211;bookmarklet option to prepend javascript: .<\/li>\n<li>UglifyJS needs a &#8211;help arguments option.<\/li>\n<li>So why isn&#8217;t node.js built in to my browser? It&#8217;s all JavaScript.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>(The good programmer is lazy; instead of typing something over and over, she writes a script to automate the steps. The wizard programmer writes a set of object classes that model the problem at hand, and tells those objects to &hellip; <a href=\"https:\/\/www.skierpage.com\/blog\/2012\/05\/web-automating-bits-of-sprint-picture-mail\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_crdt_document":"","footnotes":""},"categories":[18,20],"tags":[],"class_list":["post-374","post","type-post","status-publish","format-standard","hentry","category-software","category-web"],"_links":{"self":[{"href":"https:\/\/www.skierpage.com\/blog\/wp-json\/wp\/v2\/posts\/374","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.skierpage.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.skierpage.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.skierpage.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.skierpage.com\/blog\/wp-json\/wp\/v2\/comments?post=374"}],"version-history":[{"count":0,"href":"https:\/\/www.skierpage.com\/blog\/wp-json\/wp\/v2\/posts\/374\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.skierpage.com\/blog\/wp-json\/wp\/v2\/media?parent=374"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.skierpage.com\/blog\/wp-json\/wp\/v2\/categories?post=374"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.skierpage.com\/blog\/wp-json\/wp\/v2\/tags?post=374"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}