├── .gitignore ├── README.md ├── append_excel.py ├── markdown_preview.py ├── save_file.py ├── trim_trailing_whitespace.py └── youtube-dl.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Python template 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | .hypothesis/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | .static_storage/ 58 | .media/ 59 | local_settings.py 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # pyenv 78 | .python-version 79 | 80 | # celery beat schedule file 81 | celerybeat-schedule 82 | 83 | # SageMath parsed files 84 | *.sage.py 85 | 86 | # Environments 87 | .env 88 | .venv 89 | env/ 90 | venv/ 91 | ENV/ 92 | env.bak/ 93 | venv.bak/ 94 | 95 | # Spyder project settings 96 | .spyderproject 97 | .spyproject 98 | 99 | # Rope project settings 100 | .ropeproject 101 | 102 | # mkdocs documentation 103 | /site 104 | 105 | # mypy 106 | .mypy_cache/ 107 | 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Collection of scripts for [Pythonista](http://omz-software.com/pythonista/) for iOS. 2 | 3 | -------------------------------------------------------------------------------- /append_excel.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import base64 3 | import openpyxl 4 | import os 5 | import json 6 | import webbrowser 7 | from urllib.parse import quote 8 | 9 | 10 | def base64_to_file(s, filename): 11 | """Convert base64-encoded string to file. 12 | 13 | Parameters 14 | ---------- 15 | s : str 16 | base64encoded string 17 | filename : str 18 | filename of file 19 | """ 20 | file = open(filename, 'wb') 21 | file.write(base64.b64decode(s)) 22 | file.close() 23 | 24 | 25 | def excel_append_line(filename, linedata, sheet_idx=0): 26 | """Append list as newline to excel sheet. 27 | 28 | Parameters 29 | ---------- 30 | filename : str 31 | filename of xlsx-file 32 | linedata : list 33 | list, which will be appended as new line in sheet 34 | sheet_idx : int 35 | index of sheet, where to append linedata 36 | """ 37 | workbook = openpyxl.load_workbook(filename) 38 | sheet = workbook[workbook.get_sheet_names()[0]] 39 | sheet.append(linedata) 40 | workbook.save(filename) 41 | 42 | 43 | def main(): 44 | # argument parser 45 | parser = argparse.ArgumentParser() 46 | parser.add_argument('filecontents', 47 | help='Base64-encoded string of xlsx-file.') 48 | parser.add_argument('linecontent', nargs='+', 49 | help='Content of line to be appended in xlsx-file.') 50 | parser.add_argument('-f', '--filepath', type=str, 51 | help='Filename and path for Excel file.') 52 | parser.add_argument('-i', '--index', type=int, default=0, 53 | help='Sheet index of Excel file.') 54 | args = parser.parse_args() 55 | 56 | # define filename for temporary file 57 | filename = 'tmp.xlsx' 58 | 59 | # convert base64-encoded file 60 | base64_to_file(args.filecontents, filename) 61 | 62 | # append data to file 63 | excel_append_line(filename, args.linecontent, sheet_idx=args.index) 64 | 65 | # encode newly generated file to base64 and remove temporary file 66 | file = open(filename, 'rb') 67 | workflow_input = {'filecontents': base64.b64encode(file.read()) 68 | .decode('ascii')} 69 | file.close() 70 | os.remove(filename) 71 | 72 | # add filepath to dict if available 73 | if args.filepath is not None: 74 | workflow_input['path'], \ 75 | workflow_input['filename'] = os.path.split(args.filepath) 76 | 77 | # pass data back to Workflow 78 | webbrowser.open('workflow://run-workflow?name=SaveBase64ToDropbox&input=' + 79 | quote(json.dumps(workflow_input), '')) 80 | 81 | 82 | if __name__ == '__main__': 83 | main() 84 | -------------------------------------------------------------------------------- /markdown_preview.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import editor 3 | from markdown2 import markdown 4 | import ui 5 | 6 | TEMPLATE = ''' 7 | 8 | 9 | 10 | 11 | 12 | Preview 13 | 20 | 21 | {{CONTENT}} 22 | 23 | ''' 24 | 25 | 26 | def main(): 27 | text = editor.get_text() 28 | converted = markdown(text) 29 | html = TEMPLATE.replace('{{CONTENT}}', converted) 30 | webview = ui.WebView(name='Markdown Preview') 31 | webview.load_html(html) 32 | webview.present() 33 | 34 | 35 | if __name__ == '__main__': 36 | main() 37 | -------------------------------------------------------------------------------- /save_file.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import appex 3 | import os 4 | import shutil 5 | import console 6 | 7 | 8 | def main(): 9 | if not appex.is_running_extension(): 10 | print('This script is intended to be run from the sharing extension.') 11 | return 12 | # list contains two duplicate entries, get first one 13 | inputfile = appex.get_attachments()[0] 14 | comps = __file__.split(os.sep) 15 | # check if file exists 16 | if os.path.exists(inputfile): 17 | try: 18 | # Attempt to move source into destination 19 | shutil.copy2(inputfile, 20 | os.path.join(os.sep.join( 21 | comps[:comps.index('Documents')+1]), 22 | os.path.basename(inputfile))) 23 | console.hud_alert(os.path.basename(inputfile) + 24 | ' copied successful.') 25 | except Exception: 26 | console.hud_alert('Unable to copy ' + inputfile + '.') 27 | 28 | if __name__ == '__main__': 29 | main() 30 | -------------------------------------------------------------------------------- /trim_trailing_whitespace.py: -------------------------------------------------------------------------------- 1 | import editor 2 | import re 3 | 4 | 5 | def main(): 6 | """Trim trailing whitespaces in each line of the currently opened document 7 | in editor. 8 | """ 9 | contents = editor.get_text() 10 | # split editor content by trailing whitespaces 11 | contents = re.split(' +\n', contents) 12 | # join again with newlines and remove last character, because otherwise 13 | # there would be an additional newline 14 | contents = '\n'.join(contents)[:-1] 15 | editor.replace_text(0, len(editor.get_text()), contents) 16 | 17 | return 18 | 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /youtube-dl.py: -------------------------------------------------------------------------------- 1 | import json 2 | import sys 3 | from urllib.parse import quote 4 | 5 | import youtube_dl 6 | import webbrowser 7 | 8 | 9 | def main(): 10 | with youtube_dl.YoutubeDL() as ydl: 11 | info = ydl.extract_info(sys.argv[1], download=False) 12 | 13 | audio = {} 14 | video = {} 15 | for f in info['formats']: 16 | # determine filesize of media, if possible 17 | if f["filesize"] is not None: 18 | filesize = f' - {f["filesize"] / 1024**2:.0f} MB' 19 | else: 20 | filesize = '' 21 | 22 | # extract information about audio media 23 | if f['acodec'] != 'none': 24 | key = f'{f["ext"]}@{f["abr"]:.0f}{filesize}' 25 | audio[key] = { 26 | 'url': f['url'], 27 | 'ext': f['ext'], 28 | } 29 | 30 | # extract information about video media 31 | if f['acodec'] != 'none' and f['vcodec'] != 'none': 32 | key = f'{f["width"]}x{f["height"]}.{f["ext"]}@{f["tbr"]:.0f}{filesize}' 33 | video[key] = { 34 | 'url': f['url'], 35 | 'ext': f['ext'], 36 | } 37 | 38 | # return dicts back to shortcuts 39 | if sys.argv[-1].startswith('shortcuts-production://'): 40 | url = sys.argv[-1].replace('shortcuts-production', 'shortcuts') 41 | print(url) 42 | url += f'?x-source=Pythonista3' 43 | url += f'&title={quote(info["title"])}' 44 | url += f'&audio={quote(json.dumps(audio))}' 45 | url += f'&video={quote(json.dumps(video))}' 46 | webbrowser.open(url) 47 | 48 | 49 | if __name__ == '__main__': 50 | main() 51 | 52 | --------------------------------------------------------------------------------