Skip to content

Instantly share code, notes, and snippets.

@pudquick
Created November 22, 2012 10:09
Show Gist options
  • Save pudquick/4130373 to your computer and use it in GitHub Desktop.
Save pudquick/4130373 to your computer and use it in GitHub Desktop.
Create webclippings in Pythonista
import uuid, BaseHTTPServer, select, types, clipboard, console
from SimpleHTTPServer import SimpleHTTPRequestHandler
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
global mobile_config_str
mobile_config_str = ''
global base_mobileconfig
base_mobileconfig = ''
global keep_running
keep_running = True
# The Icon key contains a base64 encoded green version of the Pythonista low-res
# icon (for shortness)
base_mobileconfig = """
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>PayloadContent</key><array><dict>
<key>FullScreen</key><true/>
<key>Icon</key><data>
iVBORw0KGgoAAAANSUhEUgAAADkAAAA5CAMAAAC7xnO3AAACHFBMVEWl0KCp0qREej1DeDxIgkFHgUBKhkOYyZJMiUS32bPN5cpNik
Wz167C37612LHO5cuhzpy22bK93LlQkUjE4MGezJjF4cKz16/B3r2v1arD37+s06eQxYpOjUZRkkmy1q2RxYvR586ZyZO627ZRk0mP
xIiLwoRXnU6gzZpUmUxTlkuSxoyUx46WyJDf7t2GwH9vtGdrsWJnr15+u3bg7t7m8uVytWpztmt6uXJpsGDj8OF8unR2t26BvXposF
/c7Nro8+fs9evh7994uHBkrltgrFdwtGjw9+9ssmPk8OKCvnttsmRus2bV6dNeq1Xn8uZdqlR0tmzz+PKHwIDq9Ok9bjfU6NH2+vZ3
uG/S58+Fv377/fvb7Nnv9u5jrVp7unNlrlz6/Pr9/v07ajXV6dL2+vXl8eP3+/fu9u1hrFjX6tV/vHc7azXy+PHY6taDvnw+cDh/vH
g/cjnZ69f///8/czmJwYI5ZzPd7dv0+fOKwYPr9Oo5ZjP+/v46aDQ9bzdcp1P5/PldqVTI4sVBdjtcplNEez1CdztJhUJbpVLG4cM8
bDY3ZDJGfz82YzFYn0/K48dGfj+r06ZPjkdZoVC52rWkz5/J48ZYnk+Ow4e/3buw1auNw4ZIg0G+3bpao1FNi0XM5Mmdy5eo0aPQ5s
2aypRFfD6t1Kg0Xy9AdDqcy5ZaolGgzZu727dSlUpTl0un0aJKh0NPj0dVmk295pW9AAAJrUlEQVR4XiRUA9vlzBLML4pt+9i2/dpe
ez/buNYfvJ3dyjyTru6qnjknySDeB+RPT738apWHC5CE3ipBEgD7SCGEzGn+owUZASojI9Dm2ro532/CrM2byX1/Pl/P5/v7WnO91r
T5Wpvvz7V9NJiCHoDcAjyzqZ00myeopmknNErTJ0BpDTI0jaKo1tRoDYX4BAUVTQdeYkIuL/stE6UDUxQDMaCDgBZN1KRRMQhEVASf
iNImipomHaAmZEFnBkb/8hKcefAw4zEzFpkxQByPg48XEzAAoIHIiAFomADKojhmmDw4+72inskwReYD9GKxmMnojK4zGSYZwD/mgU
EFEjoETLHXR3orXdenIJgSpT9fXgxeLK9Lx4Pt8ni5XG6X14Pri+WytCwdl14MShcviKlxPjWKRnHVQyZTb2VMp63Pt69sBTsqbLCz
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
hLfvZ+RfO8pfTF65YHcAAAAASUVORK5CYII=
</data>
<key>IsRemovable</key><true/>
<key>Label</key><string>%s</string>
<key>PayloadDescription</key><string>Configures Web Clip</string>
<key>PayloadDisplayName</key><string>Web Clip (Pythonista)</string>
<key>PayloadIdentifier</key><string>com.pudquick.profile.%s</string>
<key>PayloadOrganization</key><string>Pythonista</string>
<key>PayloadType</key><string>com.apple.webClip.managed</string>
<key>PayloadUUID</key><string>%s</string>
<key>PayloadVersion</key><integer>1</integer>
<key>Precomposed</key><true/>
<key>URL</key><string>pythonista://%s?action=run&amp;args=%s</string>
</dict></array>
<key>PayloadDescription</key><string>Pythonista Web Clip</string>
<key>PayloadDisplayName</key><string>%s</string>
<key>PayloadIdentifier</key><string>com.pudquick.profile.%s</string>
<key>PayloadOrganization</key><string>with help from pudquick</string>
<key>PayloadRemovalDisallowed</key><false/>
<key>PayloadType</key><string>Configuration</string>
<key>PayloadUUID</key><string>%s</string>
<key>PayloadVersion</key><integer>1</integer>
</dict></plist>
"""
class MobileConfigHTTPRequestHandler(SimpleHTTPRequestHandler):
def offer_generic(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.send_header("Last-Modified", self.date_time_string())
self.end_headers()
f = StringIO()
f.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
f.write('<html><body><a href="\webclip.mobileconfig">Something odd happened, click here instead</a></body></html>\n')
f.seek(0)
self.copyfile(f, self.wfile)
f.close()
def offer_mobileconfig(self):
global mobile_config_str
if mobile_config_str:
mobileconfig = mobile_config_str
else:
mobileconfig = ' '
self.send_response(200)
self.send_header("Last-Modified", self.date_time_string())
self.send_header("Content-type", "application/x-apple-aspen-config")
self.send_header("Content-Length", len(mobileconfig))
self.end_headers()
f = StringIO()
f.write(mobileconfig)
f.seek(0)
self.copyfile(f, self.wfile)
f.close()
global keep_running
keep_running = False
def do_GET(self):
if (self.path.lower().endswith('.mobileconfig')):
return self.offer_mobileconfig()
return self.offer_generic()
class NicerHTTPServer(BaseHTTPServer.HTTPServer):
def serve_forever(self, poll_interval=0.5):
# More limited form of serve forever - shutdown after mobileconfig download
# Works in a single thread
global keep_running
while keep_running:
self._handle_request_noblock()
def serve_it_up(port):
global mobile_config_str
global base_mobileconfig
ip = '127.0.0.1'
icon_label = 'pyClock'
UUID1 = str(uuid.uuid4()).upper()
script_name = 'Clock'
arg_str = ''
payload_name = 'Pythonista - %s' % script_name
UUID2 = str(uuid.uuid4()).upper()
mobile_config_str = base_mobileconfig % (icon_label, UUID1, UUID1, script_name, arg_str, payload_name, UUID2, UUID2)
my_httpd = NicerHTTPServer((ip, port), MobileConfigHTTPRequestHandler)
print "Serving HTTP on %s:%s ..." % (ip,port)
my_httpd.serve_forever()
print "\n*** Webclip installed! ***"
port = 8000
clipboard.set('http://127.0.0.1:%s/webclip.mobileconfig' % port)
console.clear()
console.set_font('Futura', 22)
console.set_color(0.2,0.2,0.2)
print "Open Safari and paste in the URL on your clipboard\n"
console.set_color(0.9,0.2,0.2)
print "It will pause loading,\n then re-open Pythonista!\n"
console.set_font()
console.set_color()
serve_it_up(port)
@Uberi
Copy link

Uberi commented Feb 25, 2013

The updated version of this script for Pythonista 1.3 is here: https://gist.github.com/Uberi/5031803/

  • URL is automatically opened in Safari (safari-http feature in 1.3)
  • Currently open file is used (now you can add it to the actions menu)
  • Script name is URL encoded to fix bugs where name had spaces and such

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment