Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Dropbox file picker needs update
-
@ihf I think there may be multiple versions of Dropbox file picker. The one a modified referenced 'r', not 'response' as the object that receives the download file.
Here is my complete download function.
def download_file(path, dest_filename, progress=None): data = {'path':path} headers = {'Authorization': 'Bearer %s' % (TOKEN,),'Dropbox-API-Arg': json.dumps(data)} url = 'https://content.dropboxapi.com/2/files/download' r = requests.post(url, stream=True, headers=headers) dest_path = os.path.join(os.path.expanduser('~/Documents'), dest_filename) i = 1 while os.path.exists(dest_path): base, ext = os.path.splitext(dest_filename) dest_path = os.path.join(os.path.expanduser('~/Documents'), base + '-' + str(i) + ext) i += 1 size = r.headers.get('Content-Length', 0) bytes_written = 0 canceled = False with open(dest_path, 'w') as f: for chunk in r.iter_content(1024*10): f.write(chunk) bytes_written += len(chunk) if size > 0 and callable(progress): p = float(bytes_written) / float(size) should_cancel = progress(p) if should_cancel: canceled = True break if canceled: os.remove(dest_path)
-
Yes, that would explain it. I used the version that is downloaded if you run ptinstaller within stash. Since that is the repository of Pythonista tools, it would be great if whoever maintains that could update Dropbox File Picker with your version. (Perhaps the first line should be #!python3 in case someone's default is v2)
-
Here is the complete program that works with python3.
Thanks to @bosco and @ihf#!python3 # IMPORTANT SETUP INSTRUCTIONS: # # 1. Go to http://www.dropbox.com/developers/apps (log in if necessary) # 2. Select "Create App" # 3. Select the following settings: # * "Dropbox API app" # * "Files and datastores" # * "(No) My app needs access to files already on Dropbox" # * "All file types" # * (Choose any app name) # 4. On the newly-created app's summary page, click the "Generate" # button under "Generated access token" # 5. Copy the generated token (a long string of gibberish) and # paste it below (replace YOUR_TOKEN_HERE). # 6. (optional) Open the "wrench" (actions) menu in Pythonista and add # this script, so you can run it from everywhere. # Notes: # All selected files are downloaded into the root folder of the Pythonista # script library. If a file with the same name already exists, a numeric # suffix is appended automatically. TOKEN = 'your token here' import requests import urllib import os import ui import functools import json def list_folder(folder_path='/'): headers = {'Authorization': 'Bearer %s' % (TOKEN,),'Content-Type': 'application/json' } if folder_path == '/': folder_path = '' data = {'path':folder_path} r = requests.post('https://api.dropboxapi.com/2/files/list_folder', headers=headers, data=json.dumps(data)) result = r.json() return result.get('entries', None) def download_file(path, dest_filename, progress=None): data = {'path':path} headers = {'Authorization': 'Bearer %s' % (TOKEN,),'Dropbox-API-Arg': json.dumps(data)} url = 'https://content.dropboxapi.com/2/files/download' r = requests.post(url, stream=True, headers=headers) dest_path = os.path.join(os.path.expanduser('~/Documents'), dest_filename) i = 1 while os.path.exists(dest_path): base, ext = os.path.splitext(dest_filename) dest_path = os.path.join(os.path.expanduser('~/Documents'), base + '-' + str(i) + ext) i += 1 size = int(r.headers.get('Content-Length', 0)) bytes_written = 0 canceled = False with open(dest_path, 'wb') as f: for chunk in r.iter_content(1024*10): f.write(chunk) bytes_written += len(chunk) if size > 0 and callable(progress): p = float(bytes_written) / float(size) should_cancel = progress(p) if should_cancel: canceled = True break if canceled: os.remove(dest_path) class DropboxView (ui.View): def __init__(self, path='/'): tv = ui.TableView() tv.frame = self.bounds tv.flex = 'WH' ds = ui.ListDataSource([]) ds.action = self.item_selected tv.data_source = ds tv.delegate = ds self.tableview = tv self.add_subview(self.tableview) self.name = 'Dropbox' label = ui.Label(frame=self.bounds) label.flex = 'WH' label.background_color = (1, 1, 1, 0.95) label.text = 'Loading...' label.touch_enabled = True label.alignment = ui.ALIGN_CENTER self.path = path self.add_subview(label) self.status_label = label self.canceled = False def will_close(self): self.canceled = True def item_selected(self, sender): item = sender.items[sender.selected_row] if item.get('is_dir', False): self.status_label.text = 'Loading Folder...' self.status_label.hidden = False self.path = item['path'] self.load_folder() elif item.get('up', False): self.status_label.text = 'Loading Folder...' self.status_label.hidden = False self.path = os.path.split(self.path)[0] self.load_folder() else: path = item.get('path') self.download_file(path) @ui.in_background def download_file(self, path): self.status_label.text = 'Downloading %s...' % (path,) self.status_label.hidden = False download_file(path, os.path.split(path)[1], self.download_progress) self.status_label.hidden = True def download_progress(self, p): self.status_label.text = '%i %% Downloaded...' % (p*100,) return self.canceled @ui.in_background def load_folder(self): infos = list_folder(self.path) items = [] if self.path != '/': items.append({'title': '..', 'image': 'ionicons-arrow-up-c-32', 'up': True}) if not infos: import console console.alert('Error', 'Could not load folder. Please check if you entered the access token correctly.', 'OK', hide_cancel_button=True) self.status_label.hidden = True return for info in infos: path = info.get('path_display') name = os.path.split(path)[1] if name.startswith('.'): continue is_dir = True if info.get('.tag') == 'folder' else False item = {'title': name, 'image': 'ionicons-folder-32' if is_dir else 'ionicons-ios7-download-outline-32', 'accessory_type': 'disclosure_indicator' if is_dir else 'none', 'is_dir': is_dir, 'path': info['path_display']} items.append(item) def cmp(a, b): return (a > b) - (a < b ) def c(o1, o2): u_cmp = -1 * cmp(o1.get('up', False), o2.get('up', False)) if u_cmp != 0: return u_cmp d_cmp = -1 * cmp(o1.get('is_dir', False), o2.get('is_dir', False)) if d_cmp == 0: return cmp(o1.get('path', '').lower(), o2.get('path', '').lower()) return d_cmp items.sort(key=functools.cmp_to_key(c)) self.tableview.data_source.items = items self.status_label.hidden = True self.name = self.path root_view = DropboxView() root_view.present('fullscreen') root_view.load_folder()
-
@enceladus, thanks. Please host it in a repo or at least make it a gist (share - gist in pythonista), and we will put an updated link in Pythonista-Tools.
-
I would request @bosco to create the gist. He/she has done the main changes. (I have done small edits for running in python3). Anyway if he/she has not created the gist I will create after two/three days mentioning this thread.
-
@enceladus Please go ahead and make a gist. I hope contribute more in the future when I have more time. Thanks!
-
Ok. here is the gist. Thanks @bosco .
https://gist.github.com/encela95dus/67fd65aec0c25336ac8e70153ebcf7eb
-
The gist fails as follows:
Traceback (most recent call last):
File "/private/var/mobile/Containers/Shared/AppGroup/B86CEE4E-E4A9-4BB4-9CA7-6E13BDA2C2A4/Pythonista3/Documents/dp.py", line 127, in load_folder
infos = list_folder(self.path)
File "/private/var/mobile/Containers/Shared/AppGroup/B86CEE4E-E4A9-4BB4-9CA7-6E13BDA2C2A4/Pythonista3/Documents/dp.py", line 42, in list_folder
result = r.json()
File "/var/containers/Bundle/Application/A30CF941-D366-4CE5-BCAA-C22CBEC6C501/Pythonista3.app/Frameworks/Py3Kit.framework/pylib/site-packages/requests/models.py", line 809, in json
return complexjson.loads(self.text, **kwargs)
File "/private/var/mobile/Containers/Shared/AppGroup/B86CEE4E-E4A9-4BB4-9CA7-6E13BDA2C2A4/Pythonista3/Documents/site-packages/simplejson/init.py", line 516, in loads
return _default_decoder.decode(s)
File "/private/var/mobile/Containers/Shared/AppGroup/B86CEE4E-E4A9-4BB4-9CA7-6E13BDA2C2A4/Pythonista3/Documents/site-packages/simplejson/decoder.py", line 370, in decode
obj, end = self.raw_decode(s)
File "/private/var/mobile/Containers/Shared/AppGroup/B86CEE4E-E4A9-4BB4-9CA7-6E13BDA2C2A4/Pythonista3/Documents/site-packages/simplejson/decoder.py", line 400, in raw_decode
return self.scan_once(s, idx=_w(s, idx).end())
File "/private/var/mobile/Containers/Shared/AppGroup/B86CEE4E-E4A9-4BB4-9CA7-6E13BDA2C2A4/Pythonista3/Documents/site-packages/simplejson/scanner.py", line 127, in scan_once
return _scan_once(string, idx)
File "/private/var/mobile/Containers/Shared/AppGroup/B86CEE4E-E4A9-4BB4-9CA7-6E13BDA2C2A4/Pythonista3/Documents/site-packages/simplejson/scanner.py", line 118, in _scan_once
raise JSONDecodeError(errmsg, string, idx)
simplejson.scanner.JSONDecodeError: Expecting value: line 1 column 1 (char 0) -
I do not get this error. (I have tested with various file extensions .txt, .py, .zip, .jpg etc). I use beta version of pythonista. May be this is due to different versions of request or json module. May be others could throw some light on this.
Can you try with python2 with original omz version with @bosco changes?
-
I do not have this "simplejson" module. Rename this module and see if that works.
I think that this is the issue. See the following url/
https://github.com/requests/requests/issues/3052 -
Hmm..I renamed the SimpleJson folder which was in sitepackages but I get the same error regardless.Funny thing is that this worked before bu now even the version I created from thte earlier posts is failing the same way. It must be somethign other than that simplejson.
-
Try running the following code to see if simplejson module is imported. Did you restart the pythonista after renaming sinplejson module?
import simplejson
-
@ihf This script appears to work with python 2 or 3. Try changing #!python3 to #python2. It shouldn't be using simplejson, since json is a core python module. I see now that the your error may be a problem with your requests package. LIne 42 which reads result = r.json coverts the result to json. Line 41 contains json.dumps(data), which converts the arguemnts to a string.
@enceladus Thanks for creating the gist.
-
in requests.compat:
try: import simplejson as json except (ImportError, SyntaxError): # simplejson does not support Python 3.2, it throws a SyntaxError # because of u'...' Unicode literals. import json
If simplejson is present, requests will use it. delete simplejson and fo ce quit pythonista
-
I am at a loss. Something obviously changed in my setup but I have no clue as to what. I deleted simplejson, emptied trash and restarted Pythonista. Then I recreatied the script using the gist and adding my access key and I get a jsondecode error Expecting value: line 1 column 1 (char 0).
If I run under Python 2.7 I get "No JSON object could be decoded'import simplejson results in module not found so perhaps now I have a different problem.
-
I figured it out (sort of). I generated a new access key and now it works. Thank you all.
-
The new version is added to pythonista-tools. Thanks to all.
script name: Dropbox File Picker V2
Description: Script to import a file from Dropbox into Pythonista - Dropbox V2 APIhttps://github.com/Pythonista-Tools/Pythonista-Tools/blob/master/Utilities.md
-
This is great, thank you. While I am dreaming, it would be wonderful if someone in the future were able to make the picker, 2-way so that individual files could be uploaded to dropbox as well as downloaded as is the case now.
-
@ihf, I do not use Dropbox, but if you have the Dropbox client installed, is it not available when you Share... from Pythonista?
-
@mikael You are quite right....somehow I never noticed the Share from Pythonista only the Share to. Thanks for pointing that out.