Skip to content

Instantly share code, notes, and snippets.

@bitoffdev
Last active February 2, 2025 12:12
Show Gist options
  • Save bitoffdev/89edf288a15fde45682a to your computer and use it in GitHub Desktop.
Save bitoffdev/89edf288a15fde45682a to your computer and use it in GitHub Desktop.
filedownloader.py
# Python file downloader for Pythonista by OMZ Software
# By: EJM Software ---- http://ejm.cloudvent.net
# Source: https://gist.github.com/89edf288a15fde45682a
# *****************************************
# This simple script uses the requests module to download files
# and the ui module to show a progress bar
# You can use this bookmarklet to download files from Safari:
# javascript:window.location='pythonista://filedownloader?action=run&argv='+encodeURIComponent(document.location.href);
import ui, console, clipboard, sys, requests, zipfile
class FileDownloader(ui.View):
def __init__(self):
#Setup the view
self.name = 'File Downloader'
self.background_color = 'white'
#Setup the ui elements
self.url_input = ui.TextField(frame = (0, 0, self.width, 50), flex='W', background_color='white')
self.url_input.placeholder = 'URL'
self.start_button = ui.Button(flex='LR', title='Download')
self.start_button.center = (self.width * 0.5, 70)
self.start_button.action = self.start_download
self.loading_bar = ui.Label(frame=(0,0,0,50), flex='', background_color=(0.00, 0.50, 1.00, 0.5))
self.activity_indicator = ui.ActivityIndicator(frame=(50,25,0,0), flex='W', alignment=ui.ALIGN_CENTER)
#Add subviews to main view and 'present' ui
self.add_subview(self.url_input)
self.add_subview(self.start_button)
self.add_subview(self.loading_bar)
self.add_subview(self.activity_indicator)
self.present('sheet')
def setprogress(self, progress=0):
self.loading_bar.width = self.width*progress/100
@ui.in_background
def start_download(self, sender):
self.download_file(self.url_input.text)
def download_file(self, url) :
self.start_button.enabled = False
self.activity_indicator.start()
localFilename = url.split('/')[-1]
if localFilename == '': localFilename = 'download'
with open(localFilename, 'wb') as f:
r = requests.get(url, stream=True)
total_length = r.headers.get('content-length')
if not total_length:
f.write(r.content)
else:
dl = 0
total_length = float(total_length)
for chunk in r.iter_content(1024):
dl += len(chunk)
f.write(chunk)
self.setprogress(dl/total_length*100.0)
self.start_button.enabled = True
self.activity_indicator.stop()
self.process_file(localFilename)
self.close()
def process_file(self, path):
if zipfile.is_zipfile(path):
if console.alert('Extract File?', '', 'OK'):
zipfile.ZipFile(path).extractall()
if __name__=='__main__':
view = FileDownloader()
if len(sys.argv) > 1:
view.url_input.text = sys.argv[1]
view.download_file(sys.argv[1])
elif '://' in clipboard.get():
view.url_input.text = clipboard.get()
@cclauss
Copy link

cclauss commented Aug 2, 2014

Looks great!

Is the done = int(50 * dl / total_length) line useful?

I would recommend changing if total_length is None: to if not total_length: which will match against None, 0, [], {}, '', etc.

@cclauss
Copy link

cclauss commented Aug 3, 2014

A nice addition would be if the download file was a zip file to ask the user if they want to unpack it. You could create a directory with the same name as the file with the .zip removed. You could then use Python's zipfile module to unpack the contents into that new folder. https://docs.python.org/2/library/zipfile.html

@bitoffdev
Copy link
Author

Thank you for the input, cclaus. I have redesigned the file downloader and added the option to unzip zip files after they have been downloaded.

@cclauss
Copy link

cclauss commented Aug 5, 2014

Really nice!! One more idea... If there is no command line argument but there is a URL on the clipboard then prepopulate the URL field but don't go ahead download the file because the clipboard might be stale.

Add these to lines to the end of the script:

    elif '://' in clipboard.get():
        view.url_input.text = clipboard.get()

Also, it would be good to add a close() method call so the user knows that it time for them to move on:

# After your call to process_file(), add a call to close()
        self.process_file(localFilename)
        self.close()  # add this line

@bitoffdev
Copy link
Author

Good idea! Sorry for the late reply, the code has been updated.

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