commit 86a4af718dc7bc9358e273200f7fa46a256829ab Author: grym Date: Fri Feb 23 20:08:28 2024 -0500 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb45954 --- /dev/null +++ b/.gitignore @@ -0,0 +1,857 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/images +# Edit at https://www.toptal.com/developers/gitignore?templates=images + +### Images ### +# JPEG +*.jpg +*.jpeg +*.jpe +*.jif +*.jfif +*.jfi + +# JPEG 2000 +*.jp2 +*.j2k +*.jpf +*.jpx +*.jpm +*.mj2 + +# JPEG XR +*.jxr +*.hdp +*.wdp + +# Graphics Interchange Format +*.gif + +# RAW +*.raw + +# Web P +*.webp + +# Portable Network Graphics +*.png + +# Animated Portable Network Graphics +*.apng + +# Multiple-image Network Graphics +*.mng + +# Tagged Image File Format +*.tiff +*.tif + +# Scalable Vector Graphics +*.svg +*.svgz + +# Portable Document Format +*.pdf + +# X BitMap +*.xbm + +# BMP +*.bmp +*.dib + +# ICO +*.ico + +# 3D Images +*.3dm +*.max + +# End of https://www.toptal.com/developers/gitignore/api/images +# Created by https://www.toptal.com/developers/gitignore/api/direnv +# Edit at https://www.toptal.com/developers/gitignore?templates=direnv + +### direnv ### +.direnv +.envrc + +# End of https://www.toptal.com/developers/gitignore/api/direnv + +# Created by https://www.toptal.com/developers/gitignore/api/visualstudio +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudio + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + +### VisualStudio Patch ### +# Additional files built by Visual Studio + +# End of https://www.toptal.com/developers/gitignore/api/visualstudio + +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode + +# Created by https://www.toptal.com/developers/gitignore/api/vim +# Edit at https://www.toptal.com/developers/gitignore?templates=vim + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +# End of https://www.toptal.com/developers/gitignore/api/vim + +# Created by https://www.toptal.com/developers/gitignore/api/emacs +# Edit at https://www.toptal.com/developers/gitignore?templates=emacs + +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + + +# End of https://www.toptal.com/developers/gitignore/api/emacs + +# Created by https://www.toptal.com/developers/gitignore/api/jetbrains+all +# Edit at https://www.toptal.com/developers/gitignore?templates=jetbrains+all + +### JetBrains+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### JetBrains+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +# End of https://www.toptal.com/developers/gitignore/api/jetbrains+all + +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# End of https://www.toptal.com/developers/gitignore/api/python diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..1621104 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,44 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +fail_fast: false +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-json + - id: check-merge-conflict + - id: debug-statements + - id: detect-private-key + - id: fix-byte-order-marker + - id: check-yaml + - id: mixed-line-ending + args: [--fix=lf] + - id: check-added-large-files + args: [--maxkb=5120] +- repo: https://github.com/psf/black + rev: 24.2.0 + hooks: + - id: black + name: Formatting + args: ["rofi_pinboard"] +- repo: https://github.com/pycqa/isort + rev: 5.13.2 + hooks: + - id: isort + name: Import order + args: ["--profile=black", "rofi_pinboard"] +- repo: https://github.com/PyCQA/autoflake + rev: v2.3.0 + hooks: + - id: autoflake + name: Import pruning + args: ["-i", "-r","--remove-unused-variables","--ignore-init-module-imports"] +- repo: local + hooks: + - id: breakpoint_statement + name: Check that breakpoint() isn't in source code + types: [python] + entry: '^\s*[^#]*breakpoint\(\)' + language: pygrep diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c4835ae --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +SHELL := /bin/bash +.PHONY: all venv + +VENV_DIR=$$(pwd)/venv +VENV_PYTHON=$(VENV_DIR)/bin/python +VENV_BIN=$(VENV_DIR)/bin + +venv: + @echo making venv at $(VENV_DIR) + @python -m venv $(VENV_DIR) + @$(VENV_PYTHON) -m pip install --upgrade pip setuptools wheel + +install: venv + @$(VENV_PYTHON) -m pip install --upgrade -e '.[dev]' + @$(VENV_PYTHON) -m pre_commit install + +docs: install + @$(VENV_BIN)/cog -r ./README.md + @$(VENV_BIN)/scriv collect diff --git a/README.md b/README.md new file mode 100644 index 0000000..8a2d2e2 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# rofi-pinboard + + +# Command line use + + + + + + + diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..6d51a21 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,47 @@ +[build-system] +requires = ["setuptools>=61.0.0", "wheel", "versioningit>=2.1.0"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.packages] +find = {namespaces = false} + + +[project] +name = "rofi-pinboard" +description = "rofi pinboard" +requires-python = ">=3.10" +dynamic = ["version"] +authors=[{name="grym", email="grym@ctrl-c.club"}] +dependencies = [ "typer[all]", # CLI argument parser + "pinboard >= 2.1.8", + "appdirs", + "tabulate" + ] + +[project.optional-dependencies] +dev = ["pre-commit", # automatic formatter/tool runner on git commit + "pytest", # unit testing framework + "pytest-cov", # test coverage metrics + "ipython", # featureful REPL + "pudb", # TUI debugger + "build", # wheel-maker for setuptools + "twine", # wheel-publisher + "scriv", # changelog management + "cogapp", # programmatically update documents + "debugpy", + ] + + +[project.scripts] +rofi-pinboard = "rofi_pinboard:__main__.app" + +[tool.versioningit] +default-version = "0+unknown" + +[tool.versioningit.format] +distance = "{version}+{distance}.{vcs}{rev}" +dirty = "{version}+{distance}.{vcs}{rev}.dirty" +distance-dirty = "{version}+{distance}.{vcs}{rev}.dirty" + +[tool.scriv] +format="md" diff --git a/rofi_pinboard/__init__.py b/rofi_pinboard/__init__.py new file mode 100644 index 0000000..db0ad1a --- /dev/null +++ b/rofi_pinboard/__init__.py @@ -0,0 +1,19 @@ +import logging +from importlib.metadata import version + +__version__ = version(__name__) + +logger = logging.getLogger(__name__) + +print_handler = logging.StreamHandler() +logfmt = ( + "%(asctime)s [%(levelname)-8s] %(module)s.%(funcName)s(+L%(lineno)-3d): %(message)s" +) + +logging.basicConfig( + level="INFO", + format=logfmt, + handlers=[ + print_handler, + ], +) diff --git a/rofi_pinboard/__main__.py b/rofi_pinboard/__main__.py new file mode 100644 index 0000000..861c05a --- /dev/null +++ b/rofi_pinboard/__main__.py @@ -0,0 +1,81 @@ +import re +import subprocess +import typing as t + +import typer + +from rofi_pinboard import __version__ +from rofi_pinboard.rofipinboard import RofiPinboard + +app = typer.Typer( + context_settings={ + "max_content_width": 80, + } +) + + +def version_callback(value: bool): + if value: + typer.echo(f"{__version__}") + raise typer.Exit() + + +@app.callback() +def main( + version: t.Optional[bool] = typer.Option( + None, + "--version", + callback=version_callback, + is_eager=True, + help="Show the version and exit.", + ), +): + """rofi pinboard""" + + +# @app.command() +# def all(): +# """Get all bookmarks""" +# rpb = RofiPinboard() +# rpb.get_bookmarks() +# rpb.rofi_print() + + +# @app.command() +# def url(id: int): +# """Get URL for one bookmark""" +# rpb = RofiPinboard() +# uri = rpb.get_url_from_id(id) +# print(uri, end="", flush=True) + + +@app.command() +def setup(api_token: str = t.Annotated[str, typer.Option(prompt=True)]): + """Setup Pinboard""" + rpb = RofiPinboard() + # config + config = {} + + # api token + api_token = typer.prompt("Please enter your Pinboard API token", type=str) + config["api_token"] = api_token + + rpb.save_config(config) + + +@app.command() +def rofi(): + """Call rofi and launch a browser with the selected url.""" + rbp = RofiPinboard() + rbp.get_bookmarks() + selection = subprocess.check_output( + ["rofi", "-lines", "15", "-width", "1000", "-dmenu"], + input=rbp.rofi_text(), + text=True, + ) + url = re.split(r"\s+", selection.strip())[-2] + subprocess.run(["xdg-open", url]) + + +# must be below all @app.command()s. +_subcommand_names = list(typer.main.get_command(app).commands) diff --git a/rofi_pinboard/rofipinboard.py b/rofi_pinboard/rofipinboard.py new file mode 100644 index 0000000..378c76f --- /dev/null +++ b/rofi_pinboard/rofipinboard.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +"""pinboard rofi script""" + +import json +import os +from pathlib import Path + +import appdirs +import click +import pinboard +from tabulate import tabulate + + +class RofiPinboard: + """Rofi Pinboard class""" + + book_marks = None + config = None + config_file = None + pin = None + + def __init__(self): + # set config file + cfg_path = Path(appdirs.user_config_dir(appname="rofi-pinboard")) + cfg_path.mkdir(exist_ok=True, parents=True) + self.config_file = cfg_path.joinpath("pinboard.json") + + def __setup(self): + """Setup RofiPinboard""" + # load api token + self.load_config() + + # initialise session + self.pin = pinboard.Pinboard(self.config["api_token"]) + + def __load_bookmarks(self): + bookmarks = None + temp_dir = self.__get_tmpdir() + + temp_file = Path(os.path.join(str(temp_dir), "bookmarks.pinboard")) + if temp_file.is_file(): + with open(temp_file) as bfh: + bookmarks = json.load(bfh) + temp_file.unlink() + return bookmarks + + def __get_tmpdir(self): + """Get the tmpdir""" + temp_dir = None + if "TMPDIR" in os.environ: + temp_dir = Path(os.environ["TMPDIR"]) + else: + temp_dir = Path("/tmp") + + # check if the temp dir exists + if not temp_dir.exists() or not temp_dir.is_dir(): + click.echo("Your TMPDIR does not exist.") + exit(42) + + return temp_dir + + def __save_bookmarks(self): + """Save bookmarks to tempdir""" + temp_dir = self.__get_tmpdir() + + temp_file = Path(os.path.join(str(temp_dir), "bookmarks.pinboard")) + + # unlink file if it exists + if temp_file.exists(): + temp_file.unlink() + + # write bookmarks to file + with open(temp_file, "w") as foh: + json.dump(self.book_marks, foh) + + def load_config(self): + """Load the config""" + if self.config_file.is_file(): + self.config = json.loads(self.config_file.read_text()) + else: + print("Could not find the config file.") + exit(42) + + def get_bookmarks(self): + """Get the bookmarks and data from""" + if not self.config: + self.__setup() + + output = [] + bookmarks = self.pin.posts.all() + + for bookmark in bookmarks: + pbm = { + "url": bookmark.url.rstrip(), + "desc": bookmark.description, + "tags": ",".join(bookmark.tags), + "hash": bookmark.hash, + } + output.append(pbm) + + self.book_marks = output + self.__save_bookmarks() + + def get_url_from_id(self, url_id=None): + """Get the URL for a given ID""" + bookmarks = self.__load_bookmarks() + if not bookmarks: + click.echo("Bookmarks file seems to be empty.") + exit(42) + return bookmarks[url_id]["url"] + + def rofi_text(self): + rows = [] + for i, bkm in enumerate(self.book_marks): + tags = bkm["tags"] + if not tags: + tags = "notag" + rows.append([i, bkm["desc"], bkm["url"], tags]) + return tabulate(rows, headers=[], tablefmt="plain") + + def rofi_print(self): + """Print urls and tags for rofi.""" + print(self.rofi_text) + + def save_config(self, config): + """Save the config file.""" + if not isinstance(config, dict): + print("Config object not of type dict, aborting.") + exit(42) + # write config + with open(self.config_file, "w") as cfg_file: + json.dump(config, cfg_file)