diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-02 12:16:38 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-02 12:16:38 -0500 |
| commit | b10cba594db836c0747066addad48bda4d30cd02 (patch) | |
| tree | 063119a623fa3f7139feda4ef302896d8f5f934c /dotfiles/common/.config | |
| parent | 49c2ba9c4510bf6e1acd306687473bc8ba9ad8dd (diff) | |
| download | archsetup-b10cba594db836c0747066addad48bda4d30cd02.tar.gz archsetup-b10cba594db836c0747066addad48bda4d30cd02.zip | |
refactor: drop in-repo dotfiles/, move stow tooling to the dotfiles repo
Since the installer clones DOTFILES_REPO into ~/.dotfiles and stows from there, the in-repo dotfiles/ tree was dead weight. Nothing reads it at install time. I removed it (831 files) now that both machines are migrated.
The Makefile's stow / restow / reset / unstow / import targets and the dotfile-script unit suites moved to the dotfiles repo. They sit alongside the scripts they manage and run standalone (cd ~/.dotfiles && make ...). This Makefile keeps the VM-integration targets and the installer-helper suite (safe-rm-rf).
I updated CLAUDE.md and README.md so stow operations run from ~/.dotfiles, and the dotfile-management, theme, and unit-test sections point at the standalone repo. The README was already describing the old in-repo model from before the installer switched to cloning. This brings it in line.
Diffstat (limited to 'dotfiles/common/.config')
97 files changed, 0 insertions, 12835 deletions
diff --git a/dotfiles/common/.config/.cmailpass.gpg b/dotfiles/common/.config/.cmailpass.gpg deleted file mode 100644 index 48c4632..0000000 --- a/dotfiles/common/.config/.cmailpass.gpg +++ /dev/null @@ -1,2 +0,0 @@ -Œ
-O;ŒŠª¨ƒÿÒKꡎÕýg{*F´¤pK4 _á aØ›¶ÎÁmÍrÉj"Τß;܃¢ÒêÌÌ€™ z¼šxx-šìûÝç9Ñô=ü`=£{ôDYà³
\ No newline at end of file diff --git a/dotfiles/common/.config/.dmailpass.gpg b/dotfiles/common/.config/.dmailpass.gpg Binary files differdeleted file mode 100644 index bc8dd4d..0000000 --- a/dotfiles/common/.config/.dmailpass.gpg +++ /dev/null diff --git a/dotfiles/common/.config/.gmailpass.gpg b/dotfiles/common/.config/.gmailpass.gpg deleted file mode 100644 index cea3fe1..0000000 --- a/dotfiles/common/.config/.gmailpass.gpg +++ /dev/null @@ -1 +0,0 @@ -Œ
¨‘q~–¤9KÿÒEp…é[,/Fd?aNTÒ†o%#JWÙ-«•‚rsšW_£ídíMïG¾>Óv³ª£õÃ~Bñzô²W[˜hQ°r
\ No newline at end of file diff --git a/dotfiles/common/.config/.tidal-dl.json b/dotfiles/common/.config/.tidal-dl.json deleted file mode 100644 index 9c39da2..0000000 --- a/dotfiles/common/.config/.tidal-dl.json +++ /dev/null @@ -1 +0,0 @@ -{"albumFolderFormat": "{ArtistName}/{AlbumTitle} ({AlbumYear})", "apiKeyIndex": 1, "audioQuality": "HiFi", "checkExist": true, "downloadDelay": true, "downloadPath": "/home/cjennings/music/", "includeEP": true, "language": "0", "lyricFile": false, "multiThread": true, "playlistFolderFormat": "{PlaylistName}", "saveAlbumInfo": false, "saveCovers": true, "showProgress": false, "showTrackInfo": false, "trackFileFormat": "{TrackNumber} {TrackTitle}", "usePlaylistFolder": false, "videoFileFormat": "{VideoNumber} - {ArtistName} - {VideoTitle}", "videoQuality": "P1080"}
\ No newline at end of file diff --git a/dotfiles/common/.config/.tidal-dl.token.json b/dotfiles/common/.config/.tidal-dl.token.json deleted file mode 100644 index 64396c3..0000000 --- a/dotfiles/common/.config/.tidal-dl.token.json +++ /dev/null @@ -1 +0,0 @@ -eyJhY2Nlc3NUb2tlbiI6ICJleUpyYVdRaU9pSjJPVTFHYkZocVdTSXNJbUZzWnlJNklrVlRNalUySW4wLmV5SjBlWEJsSWpvaWJ6SmZZV05qWlhOeklpd2lkV2xrSWpvek5qa3pOems0TUN3aWMyTnZjR1VpT2lKeVgzVnpjaUIzWDNWemNpQjNYM04xWWlJc0ltZFdaWElpT2pBc0luTldaWElpT2pFc0ltTnBaQ0k2TkRJMU9Td2laWGh3SWpveE56WXdOamcwT1RreUxDSnphV1FpT2lKaE5UTXpaalV3WkMxa056azVMVFExTXpJdE9UUmlNUzAwTWpFd1pqVXdOakZqTURRaUxDSnBjM01pT2lKb2RIUndjem92TDJGMWRHZ3VkR2xrWVd3dVkyOXRMM1l4SW4wLk9VaFJNNFlRMXNJQzB0QTVTX0ptck1FWjlzWmMxcUVwVzlNa203YUlIbDZKQlVxdGdGbnJFcFFBTmx5dFNYYXFDa0E3NWJ2aUV6TldGUE5iQmxyQUh3IiwgImNvdW50cnlDb2RlIjogIlVTIiwgImV4cGlyZXNBZnRlciI6IDE3NjA2ODQ5OTIuMzYzMTA4MiwgInJlZnJlc2hUb2tlbiI6ICJleUpyYVdRaU9pSm9VekZLWVRkVk1DSXNJbUZzWnlJNklrVlROVEV5SW4wLmV5SjBlWEJsSWpvaWJ6SmZjbVZtY21WemFDSXNJblZwWkNJNk16WTVNemM1T0RBc0luTmpiM0JsSWpvaWNsOTFjM0lnZDE5MWMzSWdkMTl6ZFdJaUxDSmphV1FpT2pReU5Ua3NJbk5XWlhJaU9qRXNJbWRXWlhJaU9qQXNJbWx6Y3lJNkltaDBkSEJ6T2k4dllYVjBhQzUwYVdSaGJDNWpiMjB2ZGpFaWZRLkFhQ1FFV1ZvWlZGU2p1NXpPLWJiMXV4QmpGZV9FVG1nWUhRRlpIb1BJQ2xnQW1IcWFBWUY2OXFacURmN0lpVktyLVBmdTRTN1p6ZHhiM3E4Mm5xY2F0WGtBRDNmV01sS0VDekxoNGwxTE50ODBFaU9LZk51T2lWZ2pnMmxpMzdweGltR3RuNGdybW5LS3pUTVlHV0dfSnBHLXY1SWRDdjdYVmtyRTRMNFpEM05DSGo1IiwgInVzZXJpZCI6IDM2OTM3OTgwfQ==
\ No newline at end of file diff --git a/dotfiles/common/.config/audacious/QtUi.conf b/dotfiles/common/.config/audacious/QtUi.conf deleted file mode 100644 index 18b3f4c..0000000 --- a/dotfiles/common/.config/audacious/QtUi.conf +++ /dev/null @@ -1,3 +0,0 @@ -[General] -geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0\0\0\0\0\xe\0\0\x4g\0\0\x2\xef\0\0\0\0\0\0\0\xe\0\0\x4g\0\0\x2\xef\0\0\0\0\0\0\0\0\x4h\0\0\0\0\0\0\0\xe\0\0\x4g\0\0\x2\xef) -windowState=@ByteArray(\0\0\0\xff\0\0\0\0\xfd\0\0\0\0\0\0\x4h\0\0\x2\x93\0\0\0\x4\0\0\0\x4\0\0\0\b\0\0\0\b\xfc\0\0\0\x1\0\0\0\x2\0\0\0\x1\0\0\0\x16\0M\0\x61\0i\0n\0T\0o\0o\0l\0\x42\0\x61\0r\x1\0\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0) diff --git a/dotfiles/common/.config/audacious/config b/dotfiles/common/.config/audacious/config deleted file mode 100644 index fd1fdd4..0000000 --- a/dotfiles/common/.config/audacious/config +++ /dev/null @@ -1,23 +0,0 @@ - -[audacious] -repeat=TRUE - -[audgui] -filesel_path=/home/cjennings/playlists/andor - -[audqt] -icon_theme=audacious-flat -theme=dark - -[qtui] -player_height=738 -player_width=1128 - -[skins] -equalizer_x=178 -equalizer_y=22 -player_x=614 -player_y=442 -playlist_x=674 -playlist_y=181 -skin=/home/cjennings/.local/share/audacious/Skins/nadamp.zip diff --git a/dotfiles/common/.config/audacious/playlist-state b/dotfiles/common/.config/audacious/playlist-state deleted file mode 100644 index 985b3d2..0000000 --- a/dotfiles/common/.config/audacious/playlist-state +++ /dev/null @@ -1,12 +0,0 @@ -active 1 -playing 1 -playlist 0 -position 0 -shuffle 0 -resume-state 1 -resume-time 84873 -playlist 1 -position 0 -shuffle 0 -resume-state 1 -resume-time 5276 diff --git a/dotfiles/common/.config/audacious/playlists/1000.audpl b/dotfiles/common/.config/audacious/playlists/1000.audpl deleted file mode 100644 index 3334184..0000000 --- a/dotfiles/common/.config/audacious/playlists/1000.audpl +++ /dev/null @@ -1,5 +0,0 @@ -title=New%20Playlist -uri=file:///home/cjennings/playlists/andor/andor.opus -length=215907 -bitrate=128 -codec=Opus diff --git a/dotfiles/common/.config/audacious/playlists/order b/dotfiles/common/.config/audacious/playlists/order deleted file mode 100644 index c9a7c13..0000000 --- a/dotfiles/common/.config/audacious/playlists/order +++ /dev/null @@ -1 +0,0 @@ -1000 1001
\ No newline at end of file diff --git a/dotfiles/common/.config/audacious/plugin-registry b/dotfiles/common/.config/audacious/plugin-registry deleted file mode 100644 index 9cedebb..0000000 --- a/dotfiles/common/.config/audacious/plugin-registry +++ /dev/null @@ -1,926 +0,0 @@ -format 11 -transport /usr/lib/audacious/Transport/gio.so -stamp 1758887725 -version 48 -flags 0 -name GIO Plugin -domain audacious-plugins -priority 0 -about 1 -config 0 -enabled 1 -scheme ftp -scheme sftp -scheme smb -scheme mtp -transport /usr/lib/audacious/Transport/mms.so -stamp 1758887725 -version 48 -flags 0 -name MMS Plugin -domain audacious-plugins -priority 0 -about 0 -config 0 -enabled 1 -scheme mms -transport /usr/lib/audacious/Transport/neon.so -stamp 1758887725 -version 48 -flags 0 -name Neon HTTP/HTTPS Plugin -domain audacious-plugins -priority 0 -about 0 -config 0 -enabled 1 -scheme http -scheme https -playlist /usr/lib/audacious/Container/asx.so -stamp 1758887725 -version 48 -flags 0 -name ASXv1/ASXv2 Playlists -domain audacious-plugins -priority 0 -about 0 -config 0 -enabled 1 -ext asx -saves 0 -playlist /usr/lib/audacious/Container/asx3.so -stamp 1758887725 -version 48 -flags 0 -name ASXv3 Playlists -domain audacious-plugins -priority 0 -about 0 -config 0 -enabled 1 -ext asx -saves 1 -playlist /usr/lib/audacious/Container/audpl.so -stamp 1758887725 -version 48 -flags 0 -name Audacious Playlists (audpl) -domain audacious-plugins -priority 0 -about 0 -config 0 -enabled 1 -ext audpl -saves 1 -playlist /usr/lib/audacious/Container/cue.so -stamp 1758887725 -version 48 -flags 0 -name Cue Sheet Plugin -domain audacious-plugins -priority 0 -about 0 -config 0 -enabled 1 -ext cue -saves 0 -playlist /usr/lib/audacious/Container/m3u.so -stamp 1758887725 -version 48 -flags 0 -name M3U Playlists -domain audacious-plugins -priority 0 -about 0 -config 0 -enabled 1 -ext m3u -ext m3u8 -saves 1 -playlist /usr/lib/audacious/Container/pls.so -stamp 1758887725 -version 48 -flags 0 -name PLS Playlists -domain audacious-plugins -priority 0 -about 0 -config 0 -enabled 1 -ext pls -saves 1 -playlist /usr/lib/audacious/Container/xspf.so -stamp 1758887725 -version 48 -flags 0 -name XML Shareable Playlists (XSPF) -domain audacious-plugins -priority 0 -about 0 -config 0 -enabled 1 -ext xspf -saves 1 -input /usr/lib/audacious/Input/xsf.so -stamp 1758887725 -version 48 -flags 0 -name 2SF Decoder -domain audacious-plugins -priority 5 -about 0 -config 1 -enabled 1 -scheme 2sf -scheme mini2sf -subtunes 0 -writes 0 -input /usr/lib/audacious/Input/aac-raw.so -stamp 1758887725 -version 48 -flags 0 -name AAC (Raw) Decoder -domain audacious-plugins -priority 5 -about 0 -config 0 -enabled 1 -scheme aac -ext audio/aac -subtunes 0 -writes 0 -input /usr/lib/audacious/Input/amidi-plug.so -stamp 1758887725 -version 48 -flags 0 -name AMIDI-Plug (MIDI Player) -domain audacious-plugins -priority 5 -about 1 -config 1 -enabled 1 -scheme mid -scheme midi -scheme rmi -scheme rmid -ext audio/midi -subtunes 0 -writes 0 -input /usr/lib/audacious/Input/cdaudio-ng.so -stamp 1758887725 -version 48 -flags 0 -name Audio CD Plugin -domain audacious-plugins -priority 5 -about 1 -config 1 -enabled 1 -mime cdda -subtunes 2 -writes 0 -input /usr/lib/audacious/Input/console.so -stamp 1758887725 -version 48 -flags 0 -name Game Console Music Decoder -domain audacious-plugins -priority 5 -about 1 -config 1 -enabled 1 -scheme ay -scheme gbs -scheme gym -scheme hes -scheme kss -scheme nsf -scheme nsfe -scheme sap -scheme spc -scheme vgm -scheme vgz -subtunes 2 -writes 0 -input /usr/lib/audacious/Input/madplug.so -stamp 1758887725 -version 48 -flags 0 -name MPG123 Plugin -domain audacious-plugins -priority 5 -about 0 -config 1 -enabled 1 -scheme mp3 -scheme mp2 -scheme mp1 -scheme bmu -ext audio/mp3 -ext audio/mpeg -ext audio/x-mp3 -ext audio/x-mpeg -subtunes 0 -writes 1 -input /usr/lib/audacious/Input/openmpt.so -stamp 1758887725 -version 48 -flags 0 -name OpenMPT (Module Player) -domain audacious-plugins -priority 5 -about 1 -config 1 -enabled 1 -scheme 669 -scheme amf -scheme ams -scheme dbm -scheme digi -scheme dmf -scheme dsm -scheme far -scheme gdm -scheme ice -scheme imf -scheme it -scheme j2b -scheme m15 -scheme mdl -scheme med -scheme mmcmp -scheme mms -scheme mo3 -scheme mod -scheme mptm -scheme mt2 -scheme mtm -scheme nst -scheme okt -scheme plm -scheme ppm -scheme psm -scheme pt36 -scheme ptm -scheme s3m -scheme sfx -scheme sfx2 -scheme st26 -scheme stk -scheme stm -scheme ult -scheme umx -scheme wow -scheme xm -scheme xpk -subtunes 0 -writes 0 -input /usr/lib/audacious/Input/psf2.so -stamp 1758887725 -version 48 -flags 0 -name OpenPSF PSF1/PSF2 Decoder -domain audacious-plugins -priority 5 -about 0 -config 1 -enabled 1 -scheme psf -scheme minipsf -scheme psf2 -scheme minipsf2 -scheme spu -scheme spx -subtunes 0 -writes 0 -input /usr/lib/audacious/Input/opus.so -stamp 1758887725 -version 48 -flags 0 -name Opus Decoder -domain audacious-plugins -priority 5 -about 1 -config 0 -enabled 1 -scheme opus -ext application/ogg -ext audio/ogg -ext audio/opus -ext audio/x-opus+ogg -subtunes 0 -writes 0 -input /usr/lib/audacious/Input/sid.so -stamp 1758887725 -version 48 -flags 0 -name SID Player -domain audacious-plugins -priority 5 -about 0 -config 1 -enabled 1 -scheme sid -scheme psid -ext audio/prs.sid -subtunes 2 -writes 0 -input /usr/lib/audacious/Input/metronom.so -stamp 1758887725 -version 48 -flags 0 -name Tact Generator -domain audacious-plugins -priority 5 -about 1 -config 0 -enabled 1 -mime tact -subtunes 0 -writes 0 -input /usr/lib/audacious/Input/tonegen.so -stamp 1758887725 -version 48 -flags 0 -name Tone Generator -domain audacious-plugins -priority 5 -about 1 -config 0 -enabled 1 -mime tone -subtunes 0 -writes 0 -input /usr/lib/audacious/Input/vtx.so -stamp 1758887725 -version 48 -flags 0 -name VTX Decoder -domain audacious-plugins -priority 5 -about 1 -config 0 -enabled 1 -scheme vtx -subtunes 0 -writes 0 -input /usr/lib/audacious/Input/wavpack.so -stamp 1758887725 -version 48 -flags 0 -name WavPack Decoder -domain audacious-plugins -priority 5 -about 1 -config 0 -enabled 1 -scheme wv -ext audio/x-wavpack -subtunes 0 -writes 1 -input /usr/lib/audacious/Input/flacng.so -stamp 1758887725 -version 48 -flags 0 -name FLAC Decoder -domain audacious-plugins -priority 6 -about 1 -config 0 -enabled 1 -scheme flac -scheme fla -ext audio/flac -ext audio/x-flac -ext audio/ogg -ext application/ogg -subtunes 0 -writes 1 -input /usr/lib/audacious/Input/modplug.so -stamp 1758887725 -version 48 -flags 0 -name ModPlug (Module Player) -domain audacious-plugins -priority 6 -about 0 -config 1 -enabled 1 -scheme amf -scheme ams -scheme dbm -scheme dbf -scheme dsm -scheme far -scheme mdl -scheme stm -scheme ult -scheme mt2 -scheme mod -scheme s3m -scheme dmf -scheme umx -scheme it -scheme 669 -scheme xm -scheme mtm -scheme psm -scheme ft2 -subtunes 2 -writes 0 -input /usr/lib/audacious/Input/vorbis.so -stamp 1758887725 -version 48 -flags 0 -name Ogg Vorbis Decoder -domain audacious-plugins -priority 7 -about 1 -config 0 -enabled 1 -scheme ogg -scheme ogm -scheme oga -ext application/ogg -ext application/x-ogg -ext audio/ogg -ext audio/x-vorbis+ogg -subtunes 0 -writes 1 -input /usr/lib/audacious/Input/sndfile.so -stamp 1758887725 -version 48 -flags 0 -name Sndfile Plugin -domain audacious-plugins -priority 9 -about 1 -config 0 -enabled 1 -scheme aiff -scheme au -scheme raw -scheme wav -ext audio/wav -ext audio/x-wav -subtunes 0 -writes 0 -input /usr/lib/audacious/Input/ffaudio.so -stamp 1758887725 -version 48 -flags 0 -name FFmpeg Plugin -domain audacious-plugins -priority 10 -about 1 -config 0 -enabled 1 -scheme mpc -scheme mp+ -scheme mpp -scheme wma -scheme shn -scheme aa3 -scheme oma -scheme aac -scheme ac3 -scheme adx -scheme ape -scheme dts -scheme vqf -scheme m4a -scheme mp4 -scheme wav -scheme ogg -scheme oga -scheme spx -scheme tta -scheme webm -scheme mka -scheme mkv -ext application/ogg -ext audio/aac -ext audio/mp4 -ext audio/ogg -subtunes 0 -writes 1 -effect /usr/lib/audacious/Effect/bs2b.so -stamp 1758887725 -version 48 -flags 0 -name Bauer Stereophonic-to-Binaural (BS2B) -domain audacious-plugins -priority 0 -about 0 -config 1 -enabled 0 -effect /usr/lib/audacious/Effect/bitcrusher.so -stamp 1758887725 -version 48 -flags 0 -name Bitcrusher -domain audacious-plugins -priority 0 -about 0 -config 1 -enabled 0 -effect /usr/lib/audacious/Effect/crystalizer.so -stamp 1758887725 -version 48 -flags 0 -name Crystalizer -domain audacious-plugins -priority 0 -about 0 -config 1 -enabled 0 -effect /usr/lib/audacious/Effect/compressor.so -stamp 1758887725 -version 48 -flags 0 -name Dynamic Range Compressor -domain audacious-plugins -priority 0 -about 1 -config 1 -enabled 0 -effect /usr/lib/audacious/Effect/echo.so -stamp 1758887725 -version 48 -flags 0 -name Echo -domain audacious-plugins -priority 0 -about 1 -config 1 -enabled 0 -effect /usr/lib/audacious/Effect/stereo.so -stamp 1758887725 -version 48 -flags 0 -name Extra Stereo -domain audacious-plugins -priority 0 -about 1 -config 1 -enabled 0 -effect /usr/lib/audacious/Effect/silence-removal.so -stamp 1758887725 -version 48 -flags 0 -name Silence Removal -domain audacious-plugins -priority 0 -about 1 -config 1 -enabled 0 -effect /usr/lib/audacious/Effect/speed-pitch.so -stamp 1758887725 -version 48 -flags 0 -name Speed and Pitch -domain audacious-plugins -priority 0 -about 0 -config 1 -enabled 0 -effect /usr/lib/audacious/Effect/voice_removal.so -stamp 1758887725 -version 48 -flags 0 -name Voice Removal -domain audacious-plugins -priority 0 -about 0 -config 0 -enabled 0 -effect /usr/lib/audacious/Effect/mixer.so -stamp 1758887725 -version 48 -flags 0 -name Channel Mixer -domain audacious-plugins -priority 2 -about 1 -config 1 -enabled 0 -effect /usr/lib/audacious/Effect/resample.so -stamp 1758887725 -version 48 -flags 0 -name Sample Rate Converter -domain audacious-plugins -priority 2 -about 1 -config 1 -enabled 0 -effect /usr/lib/audacious/Effect/sox-resampler.so -stamp 1758887725 -version 48 -flags 0 -name SoX Resampler -domain audacious-plugins -priority 2 -about 1 -config 1 -enabled 0 -effect /usr/lib/audacious/Effect/crossfade.so -stamp 1758887725 -version 48 -flags 0 -name Crossfade -domain audacious-plugins -priority 5 -about 1 -config 1 -enabled 0 -effect /usr/lib/audacious/Effect/background_music.so -stamp 1758887725 -version 48 -flags 0 -name Background Music -domain audacious-plugins -priority 10 -about 1 -config 1 -enabled 0 -output /usr/lib/audacious/Output/pipewire.so -stamp 1758887725 -version 48 -flags 0 -name PipeWire Output -domain audacious-plugins -priority 2 -about 1 -config 0 -enabled 0 -output /usr/lib/audacious/Output/pulse_audio.so -stamp 1758887725 -version 48 -flags 0 -name PulseAudio Output -domain audacious-plugins -priority 2 -about 1 -config 1 -enabled 1 -output /usr/lib/audacious/Output/alsa.so -stamp 1758887725 -version 48 -flags 0 -name ALSA Output -domain audacious-plugins -priority 5 -about 1 -config 1 -enabled 0 -output /usr/lib/audacious/Output/oss4.so -stamp 1758887725 -version 48 -flags 0 -name OSS3 Output -domain audacious-plugins -priority 6 -about 1 -config 1 -enabled 0 -output /usr/lib/audacious/Output/sdlout.so -stamp 1758887725 -version 48 -flags 0 -name SDL Output -domain audacious-plugins -priority 9 -about 1 -config 0 -enabled 0 -output /usr/lib/audacious/Output/filewriter.so -stamp 1758887725 -version 48 -flags 0 -name FileWriter Plugin -domain audacious-plugins -priority 10 -about 1 -config 1 -enabled 0 -output /usr/lib/audacious/Output/jack-ng.so -stamp 1758887725 -version 48 -flags 0 -name JACK Output -domain audacious-plugins -priority 10 -about 0 -config 1 -enabled 0 -vis /usr/lib/audacious/Visualization/blur_scope-qt.so -stamp 1758887725 -version 48 -flags 2 -name Blur Scope -domain audacious-plugins -priority 0 -about 0 -config 1 -enabled 0 -vis /usr/lib/audacious/Visualization/gl-spectrum-qt.so -stamp 1758887725 -version 48 -flags 2 -name OpenGL Spectrum Analyzer -domain audacious-plugins -priority 0 -about 1 -config 0 -enabled 0 -vis /usr/lib/audacious/Visualization/qt-spectrum.so -stamp 1758887725 -version 48 -flags 2 -name Spectrum Analyzer -domain audacious-plugins -priority 0 -about 0 -config 0 -enabled 0 -vis /usr/lib/audacious/Visualization/vumeter-qt.so -stamp 1758887725 -version 48 -flags 2 -name VU Meter -domain audacious-plugins -priority 0 -about 1 -config 1 -enabled 0 -general /usr/lib/audacious/General/albumart-qt.so -stamp 1758887725 -version 48 -flags 2 -name Album Art -domain audacious-plugins -priority 0 -about 0 -config 0 -enabled 0 -general /usr/lib/audacious/General/cd-menu-items.so -stamp 1758887725 -version 48 -flags 0 -name Audio CD Menu Items -domain audacious-plugins -priority 0 -about 0 -config 0 -enabled 1 -general /usr/lib/audacious/General/delete-files.so -stamp 1758887725 -version 48 -flags 0 -name Delete Files -domain audacious-plugins -priority 0 -about 0 -config 1 -enabled 0 -general /usr/lib/audacious/General/notify.so -stamp 1758887725 -version 48 -flags 0 -name Desktop Notifications -domain audacious-plugins -priority 0 -about 1 -config 1 -enabled 0 -general /usr/lib/audacious/General/qthotkey.so -stamp 1758887725 -version 48 -flags 2 -name Global Hotkeys -domain audacious-plugins -priority 0 -about 1 -config 1 -enabled 0 -general /usr/lib/audacious/General/lirc.so -stamp 1758887725 -version 48 -flags 0 -name LIRC Plugin -domain audacious-plugins -priority 0 -about 1 -config 1 -enabled 0 -general /usr/lib/audacious/General/lyrics-qt.so -stamp 1758887725 -version 48 -flags 2 -name Lyrics -domain audacious-plugins -priority 0 -about 0 -config 1 -enabled 0 -general /usr/lib/audacious/General/mpris2.so -stamp 1758887725 -version 48 -flags 0 -name MPRIS 2 Server -domain audacious-plugins -priority 0 -about 1 -config 0 -enabled 1 -general /usr/lib/audacious/General/playback-history-qt.so -stamp 1758887725 -version 48 -flags 2 -name Playback History -domain audacious-plugins -priority 0 -about 1 -config 1 -enabled 0 -general /usr/lib/audacious/General/playlist-manager-qt.so -stamp 1758887725 -version 48 -flags 2 -name Playlist Manager -domain audacious-plugins -priority 0 -about 0 -config 0 -enabled 0 -general /usr/lib/audacious/General/scrobbler.so -stamp 1758887725 -version 48 -flags 0 -name Scrobbler 2.0 -domain audacious-plugins -priority 0 -about 1 -config 1 -enabled 0 -general /usr/lib/audacious/General/search-tool-qt.so -stamp 1758887725 -version 48 -flags 2 -name Search Tool -domain audacious-plugins -priority 0 -about 0 -config 1 -enabled 0 -general /usr/lib/audacious/General/song_change.so -stamp 1758887725 -version 48 -flags 0 -name Song Change -domain audacious-plugins -priority 0 -about 0 -config 1 -enabled 0 -general /usr/lib/audacious/General/song-info-qt.so -stamp 1758887725 -version 48 -flags 2 -name Song Info -domain audacious-plugins -priority 0 -about 0 -config 0 -enabled 0 -general /usr/lib/audacious/General/statusicon-qt.so -stamp 1758887725 -version 48 -flags 2 -name Status Icon -domain audacious-plugins -priority 0 -about 1 -config 1 -enabled 0 -iface /usr/lib/audacious/General/qtui.so -stamp 1758887725 -version 48 -flags 2 -name Qt Interface -domain audacious-plugins -priority 0 -about 0 -config 1 -enabled 0 -iface /usr/lib/audacious/General/skins-qt.so -stamp 1758887725 -version 48 -flags 2 -name Winamp Classic Interface -domain audacious-plugins -priority 0 -about 0 -config 1 -enabled 1 diff --git a/dotfiles/common/.config/btop/btop.conf b/dotfiles/common/.config/btop/btop.conf deleted file mode 100644 index a6c8052..0000000 --- a/dotfiles/common/.config/btop/btop.conf +++ /dev/null @@ -1,286 +0,0 @@ -#? Config file for btop v.1.4.7 - -#* Name of a btop++/bpytop/bashtop formatted ".theme" file, "Default" and "TTY" for builtin themes. -#* Themes should be placed in "../share/btop/themes" relative to binary or "$HOME/.config/btop/themes" -color_theme = "Default" - -#* If the theme set background should be shown, set to False if you want terminal background transparency. -theme_background = true - -#* Sets if 24-bit truecolor should be used, will convert 24-bit colors to 256 color (6x6x6 color cube) if false. -truecolor = true - -#* Set to true to force tty mode regardless if a real tty has been detected or not. -#* Will force 16-color mode and TTY theme, set all graph symbols to "tty" and swap out other non tty friendly symbols. -force_tty = false - -#* Option to disable presets. Either the default preset, custom presets, or all presets. -#* "Off" All presets are enabled. -#* "Default" preset is disabled.#* "Custom" presets are disabled.#* "All" presets are disabled. -disable_presets = "Off" - -#* Define presets for the layout of the boxes. Preset 0 is always all boxes shown with default settings. Max 9 presets. -#* Format: "box_name:P:G,box_name:P:G" P=(0 or 1) for alternate positions, G=graph symbol to use for box. -#* Use whitespace " " as separator between different presets. -#* Example: "cpu:0:default,mem:0:tty,proc:1:default cpu:0:braille,proc:0:tty" -presets = "cpu:1:default,proc:0:default cpu:0:default,mem:0:default,net:0:default cpu:0:block,net:0:tty" - -#* Set to True to enable "h,j,k,l,g,G" keys for directional control in lists. -#* Conflicting keys for h:"help" and k:"kill" is accessible while holding shift. -vim_keys = false - -#* Disable all mouse events. -disable_mouse = false - -#* Rounded corners on boxes, is ignored if TTY mode is ON. -rounded_corners = true - -#* Use terminal synchronized output sequences to reduce flickering on supported terminals. -terminal_sync = true - -#* Default symbols to use for graph creation, "braille", "block" or "tty". -#* "braille" offers the highest resolution but might not be included in all fonts. -#* "block" has half the resolution of braille but uses more common characters. -#* "tty" uses only 3 different symbols but will work with most fonts and should work in a real TTY. -#* Note that "tty" only has half the horizontal resolution of the other two, so will show a shorter historical view. -graph_symbol = "braille" - -# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty". -graph_symbol_cpu = "default" - -# Graph symbol to use for graphs in gpu box, "default", "braille", "block" or "tty". -graph_symbol_gpu = "default" - -# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty". -graph_symbol_mem = "default" - -# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty". -graph_symbol_net = "default" - -# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty". -graph_symbol_proc = "default" - -#* Manually set which boxes to show. Available values are "cpu mem net proc" and "gpu0" through "gpu5", separate values with whitespace. -shown_boxes = "mem net proc cpu" - -#* Update time in milliseconds, recommended 2000 ms or above for better sample times for graphs. -update_ms = 2000 - -#* Processes sorting, "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu direct", -#* "cpu lazy" sorts top process over time (easier to follow), "cpu direct" updates top process directly. -proc_sorting = "cpu lazy" - -#* Reverse sorting order, True or False. -proc_reversed = false - -#* Show processes as a tree. -proc_tree = false - -#* Use the cpu graph colors in the process list. -proc_colors = true - -#* Use a darkening gradient in the process list. -proc_gradient = true - -#* If process cpu usage should be of the core it's running on or usage of the total available cpu power. -proc_per_core = false - -#* Show process memory as bytes instead of percent. -proc_mem_bytes = true - -#* Show cpu graph for each process. -proc_cpu_graphs = true - -#* Use /proc/[pid]/smaps for memory information in the process info box (very slow but more accurate) -proc_info_smaps = false - -#* Show proc box on left side of screen instead of right. -proc_left = false - -#* (Linux) Filter processes tied to the Linux kernel(similar behavior to htop). -proc_filter_kernel = false - -#* Should the process list follow the selected process when detailed view is open. -proc_follow_detailed = true - -#* In tree-view, always accumulate child process resources in the parent process. -proc_aggregate = false - -#* Should cpu and memory usage display be preserved for dead processes when paused. -keep_dead_proc_usage = false - -#* Sets the CPU stat shown in upper half of the CPU graph, "total" is always available. -#* Select from a list of detected attributes from the options menu. -cpu_graph_upper = "Auto" - -#* Sets the CPU stat shown in lower half of the CPU graph, "total" is always available. -#* Select from a list of detected attributes from the options menu. -cpu_graph_lower = "Auto" - -#* If gpu info should be shown in the cpu box. Available values = "Auto", "On" and "Off". -show_gpu_info = "Auto" - -#* Toggles if the lower CPU graph should be inverted. -cpu_invert_lower = true - -#* Set to True to completely disable the lower CPU graph. -cpu_single_graph = false - -#* Show cpu box at bottom of screen instead of top. -cpu_bottom = false - -#* Shows the system uptime in the CPU box. -show_uptime = true - -#* Shows the CPU package current power consumption in watts. Requires running `make setcap` or `make setuid` or running with sudo. -show_cpu_watts = true - -#* Show cpu temperature. -check_temp = true - -#* Which sensor to use for cpu temperature, use options menu to select from list of available sensors. -cpu_sensor = "Auto" - -#* Show temperatures for cpu cores also if check_temp is True and sensors has been found. -show_coretemp = true - -#* Set a custom mapping between core and coretemp, can be needed on certain cpus to get correct temperature for correct core. -#* Use lm-sensors or similar to see which cores are reporting temperatures on your machine. -#* Format "x:y" x=core with wrong temp, y=core with correct temp, use space as separator between multiple entries. -#* Example: "4:0 5:1 6:3" -cpu_core_map = "" - -#* Which temperature scale to use, available values: "celsius", "fahrenheit", "kelvin" and "rankine". -temp_scale = "celsius" - -#* Use base 10 for bits/bytes sizes, KB = 1000 instead of KiB = 1024. -base_10_sizes = false - -#* Show CPU frequency. -show_cpu_freq = true - -#* How to calculate CPU frequency, available values: "first", "range", "lowest", "highest" and "average". -freq_mode = "first" - -#* Draw a clock at top of screen, formatting according to strftime, empty string to disable. -#* Special formatting: /host = hostname | /user = username | /uptime = system uptime -clock_format = "%X" - -#* Update main ui in background when menus are showing, set this to false if the menus is flickering too much for comfort. -background_update = true - -#* Custom cpu model name, empty string to disable. -custom_cpu_name = "" - -#* Optional filter for shown disks, should be full path of a mountpoint, separate multiple values with whitespace " ". -#* Only disks matching the filter will be shown. Prepend exclude= to only show disks not matching the filter. Examples: disk_filter="/boot /home/user", disks_filter="exclude=/boot /home/user" -disks_filter = "" - -#* Show graphs instead of meters for memory values. -mem_graphs = true - -#* Show mem box below net box instead of above. -mem_below_net = false - -#* Count ZFS ARC in cached and available memory. -zfs_arc_cached = true - -#* If swap memory should be shown in memory box. -show_swap = true - -#* Show swap as a disk, ignores show_swap value above, inserts itself after first disk. -swap_disk = true - -#* If mem box should be split to also show disks info. -show_disks = true - -#* Filter out non physical disks. Set this to False to include network disks, RAM disks and similar. -only_physical = true - -#* Read disks list from /etc/fstab. This also disables only_physical. -use_fstab = true - -#* Setting this to True will hide all datasets, and only show ZFS pools. (IO stats will be calculated per-pool) -zfs_hide_datasets = false - -#* Set to true to show available disk space for privileged users. -disk_free_priv = false - -#* Toggles if io activity % (disk busy time) should be shown in regular disk usage view. -show_io_stat = true - -#* Toggles io mode for disks, showing big graphs for disk read/write speeds. -io_mode = false - -#* Set to True to show combined read/write io graphs in io mode. -io_graph_combined = false - -#* Set the top speed for the io graphs in MiB/s (100 by default), use format "mountpoint:speed" separate disks with whitespace " ". -#* Example: "/mnt/media:100 /:20 /boot:1". -io_graph_speeds = "" - -#* Swap the positions of the upload and download speed graphs. When true, upload will be on top. -swap_upload_download = false - -#* Set fixed values for network graphs in Mebibits. Is only used if net_auto is also set to False. -net_download = 100 - -net_upload = 100 - -#* Use network graphs auto rescaling mode, ignores any values set above and rescales down to 10 Kibibytes at the lowest. -net_auto = true - -#* Sync the auto scaling for download and upload to whichever currently has the highest scale. -net_sync = true - -#* Starts with the Network Interface specified here. -net_iface = "" - -#* "True" shows bitrates in base 10 (Kbps, Mbps). "False" shows bitrates in binary sizes (Kibps, Mibps, etc.). "Auto" uses base_10_sizes. -base_10_bitrate = "Auto" - -#* Show battery stats in top right if battery is present. -show_battery = true - -#* Which battery to use if multiple are present. "Auto" for auto detection. -selected_battery = "Auto" - -#* Show power stats of battery next to charge indicator. -show_battery_watts = true - -#* Set loglevel for "~/.local/state/btop.log" levels are: "ERROR" "WARNING" "INFO" "DEBUG". -#* The level set includes all lower levels, i.e. "DEBUG" will show all logging info. -log_level = "WARNING" - -#* Automatically save current settings to config file on exit. -save_config_on_exit = true - -#* Measure PCIe throughput on NVIDIA cards, may impact performance on certain cards. -nvml_measure_pcie_speeds = true - -#* Measure PCIe throughput on AMD cards, may impact performance on certain cards. -rsmi_measure_pcie_speeds = true - -#* Horizontally mirror the GPU graph. -gpu_mirror_graph = true - -#* Set which GPU vendors to show. Available values are "nvidia amd intel apple" -shown_gpus = "nvidia amd intel" - -#* Custom gpu0 model name, empty string to disable. -custom_gpu_name0 = "" - -#* Custom gpu1 model name, empty string to disable. -custom_gpu_name1 = "" - -#* Custom gpu2 model name, empty string to disable. -custom_gpu_name2 = "" - -#* Custom gpu3 model name, empty string to disable. -custom_gpu_name3 = "" - -#* Custom gpu4 model name, empty string to disable. -custom_gpu_name4 = "" - -#* Custom gpu5 model name, empty string to disable. -custom_gpu_name5 = "" diff --git a/dotfiles/common/.config/calibre/.gitignore b/dotfiles/common/.config/calibre/.gitignore deleted file mode 100644 index f80339c..0000000 --- a/dotfiles/common/.config/calibre/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Calibre runtime files (not worth tracking) -caches/ -fonts/scanner_cache.json -icons-dark.rcc -viewer/annots/ diff --git a/dotfiles/common/.config/calibre/conversion/epub_output.py b/dotfiles/common/.config/calibre/conversion/epub_output.py deleted file mode 100644 index e69de29..0000000 --- a/dotfiles/common/.config/calibre/conversion/epub_output.py +++ /dev/null diff --git a/dotfiles/common/.config/calibre/conversion/look_and_feel.py b/dotfiles/common/.config/calibre/conversion/look_and_feel.py deleted file mode 100644 index e69de29..0000000 --- a/dotfiles/common/.config/calibre/conversion/look_and_feel.py +++ /dev/null diff --git a/dotfiles/common/.config/calibre/conversion/lrf_output.py b/dotfiles/common/.config/calibre/conversion/lrf_output.py deleted file mode 100644 index e69de29..0000000 --- a/dotfiles/common/.config/calibre/conversion/lrf_output.py +++ /dev/null diff --git a/dotfiles/common/.config/calibre/conversion/mobi_output.py b/dotfiles/common/.config/calibre/conversion/mobi_output.py deleted file mode 100644 index e69de29..0000000 --- a/dotfiles/common/.config/calibre/conversion/mobi_output.py +++ /dev/null diff --git a/dotfiles/common/.config/calibre/conversion/page_setup.py b/dotfiles/common/.config/calibre/conversion/page_setup.py deleted file mode 100644 index e69de29..0000000 --- a/dotfiles/common/.config/calibre/conversion/page_setup.py +++ /dev/null diff --git a/dotfiles/common/.config/calibre/custom_recipes/The Economist_1001.recipe b/dotfiles/common/.config/calibre/custom_recipes/The Economist_1001.recipe deleted file mode 100644 index bcb8364..0000000 --- a/dotfiles/common/.config/calibre/custom_recipes/The Economist_1001.recipe +++ /dev/null @@ -1,684 +0,0 @@ -#!/usr/bin/env python -# License: GPLv3 Copyright: 2008, Kovid Goyal <kovid at kovidgoyal.net> - -import json -import re -import time -from collections import defaultdict -from datetime import datetime, timedelta -from urllib.parse import quote, urlencode -from uuid import uuid4 - -from html5_parser import parse -from lxml import etree - -from calibre import replace_entities -from calibre.ebooks.BeautifulSoup import BeautifulSoup, NavigableString, Tag -from calibre.ptempfile import PersistentTemporaryFile -from calibre.scraper.simple import read_url -from calibre.utils.date import parse_only_date -from calibre.web.feeds.news import BasicNewsRecipe - - -def E(parent, name, text='', **attrs): - ans = parent.makeelement(name, **attrs) - ans.text = text - parent.append(ans) - return ans - - -def process_node(node, html_parent): - ntype = node.get('type') - if ntype == 'tag': - c = html_parent.makeelement(node['name']) - c.attrib.update({k: v or '' for k, v in node.get('attribs', {}).items()}) - html_parent.append(c) - for nc in node.get('children', ()): - process_node(nc, c) - elif ntype == 'text': - text = node.get('data') - if text: - text = replace_entities(text) - if len(html_parent): - t = html_parent[-1] - t.tail = (t.tail or '') + text - else: - html_parent.text = (html_parent.text or '') + text - - -def safe_dict(data, *names): - ans = data - for x in names: - ans = ans.get(x) or {} - return ans - - -class JSONHasNoContent(ValueError): - pass - - -def load_article_from_json(raw, root): - # open('/t/raw.json', 'w').write(raw) - data = json.loads(raw) - body = root.xpath('//body')[0] - article = E(body, 'article') - E(article, 'div', data['flyTitle'], style='color: red; font-size:small; font-weight:bold;') - E(article, 'h1', data['title'], title=safe_dict(data, 'url', 'canonical') or '') - E(article, 'div', data['rubric'], style='font-style: italic; color:#202020;') - try: - date = data['dateModified'] - except Exception: - date = data['datePublished'] - dt = datetime.fromisoformat(date[:-1]) + timedelta(seconds=time.timezone) - dt = dt.strftime('%b %d, %Y %I:%M %p') - if data['dateline'] is None: - E(article, 'p', dt, style='color: gray; font-size:small;') - else: - E(article, 'p', dt + ' | ' + (data['dateline']), style='color: gray; font-size:small;') - main_image_url = safe_dict(data, 'image', 'main', 'url').get('canonical') - if main_image_url: - div = E(article, 'div') - try: - E(div, 'img', src=main_image_url) - except Exception: - pass - for node in data.get('text') or (): - process_node(node, article) - - -def process_web_list(li_node): - li_html = '' - for li in li_node['items']: - if li.get('textHtml'): - li_html += f'<li>{li.get("textHtml")}</li>' - else: - li_html += f'<li>{li.get("text", "")}</li>' - return li_html - - -def process_info_box(bx): - info = '' - for x in safe_dict(bx, 'components'): - info += f'<blockquote>{process_web_node(x)}</blockquote>' - return info - - -def process_web_node(node): - ntype = node.get('type', '') - if ntype == 'CROSSHEAD': - if node.get('textHtml'): - return f'<h4>{node.get("textHtml")}</h4>' - return f'<h4>{node.get("text", "")}</h4>' - elif ntype in ['PARAGRAPH', 'BOOK_INFO']: - if node.get('textHtml'): - return f'<p>{node.get("textHtml")}</p>' - return f'<p>{node.get("text", "")}</p>' - elif ntype == 'IMAGE': - alt = '' if node.get('altText') is None else node.get('altText') - cap = '' - if node.get('caption'): - if node['caption'].get('textHtml') is not None: - cap = node['caption']['textHtml'] - return f'<div><img src="{node["url"]}" title="{alt}"></div><div style="text-align:center; font-size:small;">{cap}</div>' - elif ntype == 'PULL_QUOTE': - if node.get('textHtml'): - return f'<blockquote>{node.get("textHtml")}</blockquote>' - return f'<blockquote>{node.get("text", "")}</blockquote>' - elif ntype == 'DIVIDER': - return '<hr>' - elif ntype == 'INFOGRAPHIC': - if node.get('fallback'): - return process_web_node(node['fallback']) - elif ntype == 'INFOBOX': - return process_info_box(node) - elif ntype == 'UNORDERED_LIST': - if node.get('items'): - return process_web_list(node) - elif ntype: - print('** ', ntype) - return '' - - -def load_article_from_web_json(raw): - # open('/t/raw.json', 'w').write(raw) - body = '' - try: - data = json.loads(raw)['props']['pageProps']['cp2Content'] - except Exception: - data = json.loads(raw)['props']['pageProps']['content'] - body += f'<div style="color: red; font-size:small; font-weight:bold;">{data.get("flyTitle", "")}</div>' - body += f'<h1>{data["headline"]}</h1>' - if data.get('rubric') and data.get('rubric') is not None: - body += f'<div style="font-style: italic; color:#202020;">{data.get("rubric", "")}</div>' - try: - date = data['dateModified'] - except Exception: - date = data['datePublished'] - dt = datetime.fromisoformat(date[:-1]) + timedelta(seconds=time.timezone) - dt = dt.strftime('%b %d, %Y %I:%M %p') - if data.get('dateline') is None: - body += f'<p style="color: gray; font-size: small;">{dt}</p>' - else: - body += f'<p style="color: gray; font-size: small;">{dt + " | " + (data["dateline"])}</p>' - main_image_url = safe_dict(data, 'leadComponent') or '' - if main_image_url: - body += process_web_node(data['leadComponent']) - for node in data.get('body'): - body += process_web_node(node) - return '<html><body><article>' + body + '</article></body></html>' - - -def cleanup_html_article(root): - main = root.xpath('//main')[0] - body = root.xpath('//body')[0] - for child in tuple(body): - body.remove(child) - body.append(main) - main.set('id', '') - main.tag = 'article' - for x in root.xpath('//*[@style]'): - x.set('style', '') - for x in root.xpath('//button'): - x.getparent().remove(x) - - -def classes(classes): - q = frozenset(classes.split(' ')) - return dict(attrs={ - 'class': lambda x: x and frozenset(x.split()).intersection(q)}) - - -def new_tag(soup, name, attrs=()): - impl = getattr(soup, 'new_tag', None) - if impl is not None: - return impl(name, attrs=dict(attrs)) - return Tag(soup, name, attrs=attrs or None) - - -class NoArticles(Exception): - pass - - -def process_url(url): - if url.startswith('/'): - url = 'https://www.economist.com' + url - return url - - -class Economist(BasicNewsRecipe): - title = 'The Economist' - language = 'en_GB' - encoding = 'utf-8' - masthead_url = 'https://www.livemint.com/lm-img/dev/economist-logo-oneline.png' - - __author__ = 'Kovid Goyal' - description = ( - 'Global news and current affairs from a European' - ' perspective. Best downloaded on Friday mornings (GMT)' - ) - extra_css = ''' - em { color:#202020; } - img {display:block; margin:0 auto;} - ''' - oldest_article = 7.0 - resolve_internal_links = True - remove_tags = [ - dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent', 'aside', 'footer', 'svg']), - dict(attrs={'aria-label': 'Article Teaser'}), - dict(attrs={'id': 'player'}), - dict(attrs={ - 'class': [ - 'dblClkTrk', 'ec-article-info', 'share_inline_header', - 'related-items', 'main-content-container', 'ec-topic-widget', - 'teaser', 'blog-post__bottom-panel-bottom', 'blog-post__comments-label', - 'blog-post__foot-note', 'blog-post__sharebar', 'blog-post__bottom-panel', - 'newsletter-form', 'share-links-header', 'teaser--wrapped', 'latest-updates-panel__container', - 'latest-updates-panel__article-link', 'blog-post__section' - ] - } - ), - dict(attrs={ - 'class': lambda x: x and 'blog-post__siblings-list-aside' in x.split()}), - dict(attrs={'id': lambda x: x and 'gpt-ad-slot' in x}), - classes( - 'share-links-header teaser--wrapped latest-updates-panel__container' - ' latest-updates-panel__article-link blog-post__section newsletter-form blog-post__bottom-panel' - ) - ] - keep_only_tags = [dict(name='article', id=lambda x: not x)] - no_stylesheets = True - remove_attributes = ['data-reactid', 'width', 'height'] - # economist.com has started throttling after about 60% of the total has - # downloaded with connection reset by peer (104) errors. - delay = 3 - browser_type = 'webengine' - from_archive = True - recipe_specific_options = { - 'date': { - 'short': 'The date of the edition to download (YYYY-MM-DD format)', - 'long': 'For example, 2024-07-19', - }, - 'res': { - 'short': 'For hi-res images, select a resolution from the\nfollowing options: 834, 960, 1096, 1280, 1424', - 'long': 'This is useful for non e-ink devices, and for a lower file size\nthan the default, use from 480, 384, 360, 256.', - 'default': '600', - }, - 'de': { - 'short': 'Web Edition', - 'long': 'Yes/No. Digital Edition does not skip some articles based on your location.', - 'default': 'No', - } - } - - def __init__(self, *args, **kwargs): - BasicNewsRecipe.__init__(self, *args, **kwargs) - d = self.recipe_specific_options.get('de') - if d and isinstance(d, str): - if d.lower().strip() == 'yes': - self.from_archive = True - - needs_subscription = False - - def get_browser(self, *args, **kwargs): - if self.from_archive: - kwargs['user_agent'] = ( - 'Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.103 Mobile Safari/537.36 Liskov' - ) - br = BasicNewsRecipe.get_browser(self, *args, **kwargs) - else: - kwargs['user_agent'] = 'TheEconomist-Lamarr-android' - br = BasicNewsRecipe.get_browser(self, *args, **kwargs) - br.addheaders += [ - ('accept', '*/*'), - ('content-type', 'application/json'), - ('apollographql-client-name', 'mobile-app-apollo'), - ('apollographql-client-version', '3.50.0'), - ('x-request-id', str(uuid4())), - ] - return br - - def publication_date(self): - edition_date = self.recipe_specific_options.get('date') - if edition_date and isinstance(edition_date, str): - return parse_only_date(edition_date, as_utc=False) - try: - url = self.browser.open('https://www.economist.com/printedition').geturl() - except Exception as e: - self.log('Failed to fetch publication date with error: ' + str(e)) - return super().publication_date() - return parse_only_date(url.split('/')[-1], as_utc=False) - - def economist_test_article(self): - return [('Articles', [{'title':'test', - 'url':'https://www.economist.com/leaders/2025/03/13/americas-bullied-allies-need-to-toughen-up' - }])] - - def economist_return_index(self, ans): - if not ans: - raise NoArticles( - 'Could not find any articles, either the ' - 'economist.com server is having trouble and you should ' - 'try later or the website format has changed and the ' - 'recipe needs to be updated.' - ) - return ans - - def get_content_id(self, ed_date): - id_query = { - 'query': 'query EditionsQuery($from:Int$size:Int$ref:String!){section:canonical(ref:$ref){...EditionFragment __typename}}fragment EditionFragment on Content{hasPart(from:$from size:$size sort:"datePublished:desc"){total parts{id datePublished image{...ImageCoverFragment __typename}__typename}__typename}__typename}fragment ImageCoverFragment on Media{cover{headline width height url{canonical __typename}regionsAllowed __typename}__typename}', # noqa: E501 - 'operationName': 'EditionsQuery', - 'variables':'{"from":0,"size":24,"ref":"/content/d06tg8j85rifiq3oo544c6b9j61dno2n"}', - } - id_url = 'https://cp2-graphql-gateway.p.aws.economist.com/graphql?' + urlencode(id_query, safe='()!', quote_via=quote) - raw_id_data = self.index_to_soup(id_url, raw=True) - data = json.loads(raw_id_data)['data']['section']['hasPart']['parts'] - for x in data: - if ed_date in x['datePublished']: - return x['id'] - return None - - def parse_index(self): - if self.from_archive: - return self.parse_web_index() - edition_date = self.recipe_specific_options.get('date') - # return self.economist_test_article() - # url = 'https://www.economist.com/weeklyedition/archive' - query = { - 'query': 'query LatestWeeklyAutoEditionQuery($ref:String!){canonical(ref:$ref){hasPart(from:0 size:1 sort:"datePublished:desc"){parts{...WeeklyEditionFragment __typename}__typename}__typename}}fragment WeeklyEditionFragment on Content{id type datePublished image{...ImageCoverFragment __typename}url{canonical __typename}hasPart(size:100 sort:"publication.context.position"){parts{...ArticleFragment __typename}__typename}__typename}fragment ArticleFragment on Content{ad{grapeshot{channels{name __typename}__typename}__typename}articleSection{internal{id title:headline __typename}__typename}audio{main{id duration(format:"seconds")source:channel{id __typename}url{canonical __typename}__typename}__typename}byline dateline dateModified datePublished dateRevised flyTitle:subheadline id image{...ImageInlineFragment ...ImageMainFragment ...ImagePromoFragment __typename}print{title:headline flyTitle:subheadline rubric:description section{id title:headline __typename}__typename}publication{id tegID title:headline flyTitle:subheadline datePublished regionsAllowed url{canonical __typename}__typename}rubric:description source:channel{id __typename}tegID text(format:"json")title:headline type url{canonical __typename}topic contentIdentity{forceAppWebview mediaType articleType __typename}__typename}fragment ImageInlineFragment on Media{inline{url{canonical __typename}width height __typename}__typename}fragment ImageMainFragment on Media{main{url{canonical __typename}width height __typename}__typename}fragment ImagePromoFragment on Media{promo{url{canonical __typename}id width height __typename}__typename}fragment ImageCoverFragment on Media{cover{headline width height url{canonical __typename}regionsAllowed __typename}__typename}', # noqa: E501 - 'operationName': 'LatestWeeklyAutoEditionQuery', - 'variables': '{"ref":"/content/d06tg8j85rifiq3oo544c6b9j61dno2n"}', - } - if edition_date and isinstance(edition_date, str): - content_id = self.get_content_id(edition_date) - if content_id: - query = { - 'query': 'query SpecificWeeklyEditionQuery($path:String!){section:canonical(ref:$path){...WeeklyEditionFragment __typename}}fragment WeeklyEditionFragment on Content{id type datePublished image{...ImageCoverFragment __typename}url{canonical __typename}hasPart(size:100 sort:"publication.context.position"){parts{...ArticleFragment __typename}__typename}__typename}fragment ArticleFragment on Content{ad{grapeshot{channels{name __typename}__typename}__typename}articleSection{internal{id title:headline __typename}__typename}audio{main{id duration(format:"seconds")source:channel{id __typename}url{canonical __typename}__typename}__typename}byline dateline dateModified datePublished dateRevised flyTitle:subheadline id image{...ImageInlineFragment ...ImageMainFragment ...ImagePromoFragment __typename}print{title:headline flyTitle:subheadline rubric:description section{id title:headline __typename}__typename}publication{id tegID title:headline flyTitle:subheadline datePublished regionsAllowed url{canonical __typename}__typename}rubric:description source:channel{id __typename}tegID text(format:"json")title:headline type url{canonical __typename}topic contentIdentity{forceAppWebview mediaType articleType __typename}__typename}fragment ImageInlineFragment on Media{inline{url{canonical __typename}width height __typename}__typename}fragment ImageMainFragment on Media{main{url{canonical __typename}width height __typename}__typename}fragment ImagePromoFragment on Media{promo{url{canonical __typename}id width height __typename}__typename}fragment ImageCoverFragment on Media{cover{headline width height url{canonical __typename}regionsAllowed __typename}__typename}', # noqa: E501 - 'operationName': 'SpecificWeeklyEditionQuery', - 'variables': '{{"path":"{}"}}'.format(content_id), - } - url = 'https://cp2-graphql-gateway.p.aws.economist.com/graphql?' + urlencode(query, safe='()!', quote_via=quote) - try: - if edition_date and isinstance(edition_date, str): - if not content_id: - self.log(edition_date, ' not found, trying web edition.') - self.from_archive = True - return self.parse_web_index() - raw = self.index_to_soup(url, raw=True) - except Exception: - self.log('Digital Edition Server is not reachable, try again after some time.') - self.from_archive = True - return self.parse_web_index() - ans = self.economist_parse_index(raw) - return self.economist_return_index(ans) - - def economist_parse_index(self, raw): - # edition_date = self.recipe_specific_options.get('date') - # if edition_date and isinstance(edition_date, str): - # data = json.loads(raw)['data']['section'] - # else: - # data = json.loads(raw)['data']['canonical']['hasPart']['parts'][0] - try: - data = json.loads(raw)['data']['section'] - except KeyError: - data = json.loads(raw)['data']['canonical']['hasPart']['parts'][0] - dt = datetime.fromisoformat(data['datePublished'][:-1]) + timedelta(seconds=time.timezone) - dt = dt.strftime('%b %d, %Y') - self.timefmt = ' [' + dt + ']' - # get local issue cover, title - try: - region = json.loads(self.index_to_soup('https://geolocation-db.com/json', raw=True))['country_code'] - except Exception: - region = '' - for cov in data['image']['cover']: - if region in cov['regionsAllowed']: - self.description = cov['headline'] - self.cover_url = cov['url']['canonical'].replace('economist.com/', - 'economist.com/cdn-cgi/image/width=960,quality=80,format=auto/') - break - else: - self.description = data['image']['cover'][0]['headline'] - self.cover_url = data['image']['cover'][0]['url']['canonical'].replace('economist.com/', - 'economist.com/cdn-cgi/image/width=960,quality=80,format=auto/') - self.log('Got cover:', self.cover_url, '\n', self.description) - - feeds_dict = defaultdict(list) - for part in safe_dict(data, 'hasPart', 'parts'): - try: - section = part['articleSection']['internal'][0]['title'] - except Exception: - section = safe_dict(part, 'print', 'section', 'title') or 'section' - if section not in feeds_dict: - self.log(section) - title = safe_dict(part, 'title') - desc = safe_dict(part, 'rubric') or '' - sub = safe_dict(part, 'flyTitle') or '' - if sub and section != sub: - desc = sub + ' :: ' + desc - pt = PersistentTemporaryFile('.html') - pt.write(json.dumps(part).encode('utf-8')) - pt.close() - url = 'file:///' + pt.name - feeds_dict[section].append({'title': title, 'url': url, 'description': desc}) - self.log('\t', title, '\n\t\t', desc) - return list(feeds_dict.items()) - - def populate_article_metadata(self, article, soup, first): - if not self.from_archive: - article.url = soup.find('h1')['title'] - - def preprocess_html(self, soup): - width = '600' - w = self.recipe_specific_options.get('res') - if w and isinstance(w, str): - width = w - for img in soup.findAll('img', src=True): - qua = 'economist.com/cdn-cgi/image/width=' + width + ',quality=80,format=auto/' - img['src'] = img['src'].replace('economist.com/', qua) - return soup - - def preprocess_raw_html(self, raw, url): - if self.from_archive: - return self.preprocess_raw_web_html(raw, url) - - # open('/t/raw.html', 'wb').write(raw.encode('utf-8')) - - body = '<html><body><article></article></body></html>' - root = parse(body) - load_article_from_json(raw, root) - - if '/interactive/' in url: - return ('<html><body><article><h1>' + root.xpath('//h1')[0].text + '</h1><em>' - 'This article is supposed to be read in a browser.' - '</em></article></body></html>') - - for div in root.xpath('//div[@class="lazy-image"]'): - noscript = list(div.iter('noscript')) - if noscript and noscript[0].text: - img = list(parse(noscript[0].text).iter('img')) - if img: - p = noscript[0].getparent() - idx = p.index(noscript[0]) - p.insert(idx, p.makeelement('img', src=img[0].get('src'))) - p.remove(noscript[0]) - for x in root.xpath('//*[name()="script" or name()="style" or name()="source" or name()="meta"]'): - x.getparent().remove(x) - # the economist uses <small> for small caps with a custom font - for init in root.xpath('//span[@data-caps="initial"]'): - init.set('style', 'font-weight:bold;') - for x in root.xpath('//small'): - if x.text and len(x) == 0: - x.text = x.text.upper() - x.tag = 'span' - x.set('style', 'font-variant: small-caps') - for h2 in root.xpath('//h2'): - h2.tag = 'h4' - for x in root.xpath('//figcaption'): - x.set('style', 'text-align:center; font-size:small;') - for x in root.xpath('//cite'): - x.tag = 'blockquote' - x.set('style', 'color:#404040;') - raw = etree.tostring(root, encoding='unicode') - return raw - - def parse_index_from_printedition(self): - # return self.economist_test_article() - edition_date = self.recipe_specific_options.get('date') - if edition_date and isinstance(edition_date, str): - url = 'https://www.economist.com/weeklyedition/' + edition_date - self.timefmt = ' [' + edition_date + ']' - else: - url = 'https://www.economist.com/printedition' - # raw = open('/t/raw.html').read() - raw = self.index_to_soup(url, raw=True) - # with open('/t/raw.html', 'wb') as f: - # f.write(raw) - soup = self.index_to_soup(raw) - # nav = soup.find(attrs={'class':'navigation__wrapper'}) - # if nav is not None: - # a = nav.find('a', href=lambda x: x and '/printedition/' in x) - # if a is not None: - # self.log('Following nav link to current edition', a['href']) - # soup = self.index_to_soup(process_url(a['href'])) - ans = self.economist_parse_index(soup) - if not ans: - raise NoArticles( - 'Could not find any articles, either the ' - 'economist.com server is having trouble and you should ' - 'try later or the website format has changed and the ' - 'recipe needs to be updated.' - ) - return ans - - def eco_find_image_tables(self, soup): - for x in soup.findAll('table', align=['right', 'center']): - if len(x.findAll('font')) in (1, 2) and len(x.findAll('img')) == 1: - yield x - - def postprocess_html(self, soup, first): - for img in soup.findAll('img', srcset=True): - del img['srcset'] - for table in list(self.eco_find_image_tables(soup)): - caption = table.find('font') - img = table.find('img') - div = new_tag(soup, 'div') - div['style'] = 'text-align:left;font-size:70%' - ns = NavigableString(self.tag_to_string(caption)) - div.insert(0, ns) - div.insert(1, new_tag(soup, 'br')) - del img['width'] - del img['height'] - img.extract() - div.insert(2, img) - table.replaceWith(div) - return soup - - def canonicalize_internal_url(self, url, is_link=True): - if url.endswith('/print'): - url = url.rpartition('/')[0] - return BasicNewsRecipe.canonicalize_internal_url(self, url, is_link=is_link) - - # archive code - def parse_web_index(self): - edition_date = self.recipe_specific_options.get('date') - # return self.economist_test_article() - if edition_date and isinstance(edition_date, str): - url = 'https://www.economist.com/weeklyedition/' + edition_date - self.timefmt = ' [' + edition_date + ']' - else: - url = 'https://www.economist.com/weeklyedition' - soup = self.index_to_soup(url) - ans = self.economist_parse_web_index(soup) - return self.economist_return_index(ans) - - def economist_parse_web_index(self, soup): - script_tag = soup.find('script', id='__NEXT_DATA__') - if script_tag is not None: - data = json.loads(script_tag.string) - # open('/t/raw.json', 'w').write(json.dumps(data, indent=2, sort_keys=True)) - self.description = safe_dict(data, 'props', 'pageProps', 'content', 'headline') - self.timefmt = ' [' + safe_dict(data, 'props', 'pageProps', 'content', 'formattedIssueDate') + ']' - self.cover_url = safe_dict(data, 'props', 'pageProps', 'content', 'cover', 'url').replace( - 'economist.com/', 'economist.com/cdn-cgi/image/width=960,quality=80,format=auto/').replace('SQ_', '') - self.log('Got cover:', self.cover_url) - - feeds = [] - - for part in safe_dict( - data, 'props', 'pageProps', 'content', 'headerSections' - ) + safe_dict(data, 'props', 'pageProps', 'content', 'sections'): - section = safe_dict(part, 'name') or '' - if not section: - continue - self.log(section) - - articles = [] - - for ar in part['articles']: - title = safe_dict(ar, 'headline') or '' - url = process_url(safe_dict(ar, 'url') or '') - if not title or not url: - continue - desc = safe_dict(ar, 'rubric') or '' - sub = safe_dict(ar, 'flyTitle') or '' - if sub and section != sub: - desc = sub + ' :: ' + desc - self.log('\t', title, '\n\t', desc, '\n\t\t', url) - articles.append({'title': title, 'url': url, 'description': desc}) - feeds.append((section, articles)) - return feeds - else: - return [] - - def preprocess_raw_web_html(self, raw, url): - # open('/t/raw.html', 'wb').write(raw.encode('utf-8')) - root_ = parse(raw) - if '/interactive/' in url: - return ('<html><body><article><h1>' + root_.xpath('//h1')[0].text + '</h1><em>' - 'This article is supposed to be read in a browser' - '</em></article></body></html>') - - script = root_.xpath('//script[@id="__NEXT_DATA__"]') - - html = load_article_from_web_json(script[0].text) - - root = parse(html) - for div in root.xpath('//div[@class="lazy-image"]'): - noscript = list(div.iter('noscript')) - if noscript and noscript[0].text: - img = list(parse(noscript[0].text).iter('img')) - if img: - p = noscript[0].getparent() - idx = p.index(noscript[0]) - p.insert(idx, p.makeelement('img', src=img[0].get('src'))) - p.remove(noscript[0]) - for x in root.xpath('//*[name()="script" or name()="style" or name()="source" or name()="meta"]'): - x.getparent().remove(x) - # the economist uses <small> for small caps with a custom font - for init in root.xpath('//span[@data-caps="initial"]'): - init.set('style', 'font-weight:bold;') - for x in root.xpath('//small'): - if x.text and len(x) == 0: - x.text = x.text.upper() - x.tag = 'span' - x.set('style', 'font-variant: small-caps') - for h2 in root.xpath('//h2'): - h2.tag = 'h4' - for x in root.xpath('//figcaption'): - x.set('style', 'text-align:center; font-size:small;') - for x in root.xpath('//cite'): - x.tag = 'blockquote' - x.set('style', 'color:#404040;') - raw = etree.tostring(root, encoding='unicode') - return raw - - raw_ar = read_url([], 'https://archive.is/latest/' + url) - archive = BeautifulSoup(str(raw_ar)) - art = archive.find('article') - if art: - bdy = art.findAll('section') - if len(bdy) != 0: - content = bdy[-1] - else: - content = archive.find('div', attrs={'itemprop':'text'}) - soup = BeautifulSoup(raw) - article = soup.find('section', attrs={'id':'body'}) - if not article: - article = soup.find('div', attrs={'itemprop':'text'}) - if not article: - article = soup.find(attrs={'itemprop':'blogPost'}) - if article and content: - self.log('**fetching archive content') - article.append(content) - - div = soup.findAll(attrs={'style': lambda x: x and x.startswith( - ('color:rgb(13, 13, 13);', 'color: rgb(18, 18, 18);') - )}) - for p in div: - p.name = 'p' - return str(soup) - return raw - return raw - - def preprocess_web_html(self, soup): - for img in soup.findAll('img', attrs={'old-src':True}): - img['src'] = img['old-src'] - for a in soup.findAll('a', href=True): - a['href'] = 'http' + a['href'].split('http')[-1] - for fig in soup.findAll('figure'): - fig['class'] = 'sub' - for sty in soup.findAll(attrs={'style':True}): - del sty['style'] - width = '600' - w = self.recipe_specific_options.get('res') - if w and isinstance(w, str): - width = w - for img in soup.findAll('img', src=True): - if '/cdn-cgi/image/' not in img['src']: - qua = 'economist.com/cdn-cgi/image/width=' + width + ',quality=80,format=auto/' - img['src'] = img['src'].replace('economist.com/', qua) - else: - img['src'] = re.sub(r'width=\d+', 'width=' + width, img['src']) - return soup - - -calibre_most_common_ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/custom_recipes/The New York Times_1000.recipe b/dotfiles/common/.config/calibre/custom_recipes/The New York Times_1000.recipe deleted file mode 100644 index db3c40b..0000000 --- a/dotfiles/common/.config/calibre/custom_recipes/The New York Times_1000.recipe +++ /dev/null @@ -1,368 +0,0 @@ -#!/usr/bin/env python -# vim:fileencoding=utf-8 -# License: GPLv3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net> - -from __future__ import absolute_import, division, print_function, unicode_literals - -import datetime -import json -import re - -import mechanize - -from calibre import strftime -from calibre.ebooks.BeautifulSoup import Tag -from calibre.utils.date import strptime -from calibre.web.feeds.news import BasicNewsRecipe -from polyglot.urllib import urlencode - -is_web_edition = True -use_wayback_machine = False - -# This is an Apollo persisted query hash which you can get -# from looking at the XHR requests made by: https://www.nytimes.com/section/todayspaper -# or by https://www.nytimes.com/section/world -persistedQuery = '1f99120a11e94dd62a9474f68ee1255537ee3cf7eac20a0377819edb2fa1fef7' - -# The sections to download when downloading the web edition, comment out -# the section you are not interested in -web_sections = [ - ('World', 'world'), - ('U.S.', 'us'), - ('Politics', 'politics'), - # ('New York', 'nyregion'), - ('Business', 'business'), - ('Technology', 'technology'), - ('Sports', 'sports'), - ('Science', 'science'), - ('Health', 'health'), - ('Opinion', 'opinion'), - # ('Arts', 'arts'), - ('Books', 'books'), - # ('Movies', 'movies'), - # ('Music', 'arts/music'), - # ('Television', 'arts/television'), - # ('Style', 'style'), - # ('Dining & Wine', 'food'), - # ('Fashion & Style', 'fashion'), - # ('Home & Garden', 'garden'), - # ('Travel', 'travel'), - ('Education', 'education'), - # ('Multimedia', 'multimedia'), - ('Obituaries', 'obituaries'), - # ('Sunday Magazine', 'magazine') -] -# web_sections = [ ('Business', 'business'), ] -url_date_pat = re.compile(r'/(2\d\d\d)/(\d\d)/(\d\d)/') - - -def date_from_url(url): - m = url_date_pat.search(url) - if m is not None: - return datetime.date(*map(int, m.groups())) - - -def format_date(d): - try: - return strftime(' [%a, %d %b %Y]', d) - except Exception: - return strftime(' [%Y/%m/%d]', d) - - -def classes(classes): - q = frozenset(classes.split(' ')) - return dict(attrs={ - 'class': lambda x: x and frozenset(x.split()).intersection(q)}) - - -def new_tag(soup, name, attrs=()): - impl = getattr(soup, 'new_tag', None) - if impl is not None: - return impl(name, attrs=dict(attrs)) - return Tag(soup, name, attrs=attrs or None) - - -class NewYorkTimes(BasicNewsRecipe): - if is_web_edition: - title = 'The New York Times (Web)' - description = ( - 'New York Times (Web). You can edit the recipe to remove sections you are not interested in. ' - 'Use advanced menu to make changes to fetch Todays Paper' - ) - else: - title = 'The New York Times' - description = ( - 'New York Times. Todays Paper ' - 'Use advanced menu to make changes to fetch Web Edition' - ) - encoding = 'utf-8' - __author__ = 'Kovid Goyal' - language = 'en_US' - ignore_duplicate_articles = {'title', 'url'} - no_stylesheets = True - oldest_web_edition_article = 1 # days - - extra_css = ''' - .byl, .time { font-size:small; color:#202020; } - .cap { font-size:small; text-align:center; } - .cred { font-style:italic; font-size:small; } - em, blockquote { color: #202020; } - .sc { font-variant: small-caps; } - .lbl { font-size:small; color:#404040; } - img { display:block; margin:0 auto; } - ''' - - @property - def nyt_parser(self): - ans = getattr(self, '_nyt_parser', None) - if ans is None: - from calibre.live import load_module - self._nyt_parser = ans = load_module('calibre.web.site_parsers.nytimes') - return ans - - def get_nyt_page(self, url, skip_wayback=False): - if use_wayback_machine and not skip_wayback: - from calibre import browser - return self.nyt_parser.download_url(url, browser()) - return self.index_to_soup(url, raw=True) - - def preprocess_raw_html(self, raw_html, url): - cleaned = self.nyt_parser.clean_js_json(raw_html) - return self.nyt_parser.extract_html(self.index_to_soup(cleaned), url) - - articles_are_obfuscated = use_wayback_machine - - if use_wayback_machine: - def get_obfuscated_article(self, url): - from calibre.ptempfile import PersistentTemporaryFile - with PersistentTemporaryFile() as tf: - tf.write(self.get_nyt_page(url)) - return tf.name - - recipe_specific_options = { - 'web': { - 'short': 'Type in yes, if you want ' + ('Todays Paper' if is_web_edition else 'Web Edition'), - 'default': 'Web Edition' if is_web_edition else 'Todays Paper', - }, - 'days': { - 'short': 'Oldest article to download from this news source. In days ', - 'long': 'For example, 1, gives you articles from the past 24 hours\n(Works only for Web_Edition)', - 'default': str(oldest_web_edition_article) - }, - 'date': { - 'short': 'The date of the edition to download (YYYY/MM/DD format)\nUsed to fetch past editions of NYT newspaper', - 'long': 'For example, 2024/07/16' - }, - 'res': { - 'short': ( - 'For hi-res images, select a resolution from the following\noptions: ' - 'popup, jumbo, mobileMasterAt3x, superJumbo' - ), - 'long': ( - 'This is useful for non e-ink devices, and for a lower file size\nthan ' - 'the default, use mediumThreeByTwo440, mediumThreeByTwo225, articleInline.' - ), - }, - 'comp': { - 'short': 'Compress News Images?', - 'long': 'enter yes', - 'default': 'no' - } - } - - def __init__(self, *args, **kwargs): - BasicNewsRecipe.__init__(self, *args, **kwargs) - c = self.recipe_specific_options.get('comp') - d = self.recipe_specific_options.get('days') - w = self.recipe_specific_options.get('web') - self.is_web_edition = is_web_edition - if w and isinstance(w, str): - if w == 'yes': - self.is_web_edition = not is_web_edition - if d and isinstance(d, str): - self.oldest_web_edition_article = float(d) - if c and isinstance(c, str): - if c.lower() == 'yes': - self.compress_news_images = True - - def read_todays_paper(self): - pdate = self.recipe_specific_options.get('date') - templ = 'https://www.nytimes.com/issue/todayspaper/{}/todays-new-york-times' - if pdate and isinstance(pdate, str): - return pdate, self.index_to_soup(templ.format(pdate)) - # Cant figure out how to get the date so just try todays and yesterdays dates - date = datetime.date.today() - pdate = date.strftime('%Y/%m/%d') - try: - soup = self.index_to_soup(templ.format(pdate)) - except Exception as e: - if getattr(e, 'code', None) == 404: - date -= datetime.timedelta(days=1) - pdate = date.strftime('%Y/%m/%d') - soup = self.index_to_soup(templ.format(pdate)) - else: - raise - self.log("Using today's paper from:", pdate) - return pdate, soup - - def read_nyt_metadata(self): - pdate, soup = self.read_todays_paper() - date = strptime(pdate, '%Y/%m/%d', assume_utc=False, as_utc=False) - self.cover_url = 'https://static01.nyt.com/images/{}/nytfrontpage/scan.jpg'.format(pdate) - self.timefmt = strftime(' [%d %b, %Y]', date) - self.nytimes_publication_date = pdate - script = soup.findAll('script', text=lambda x: x and 'window.__preloadedData' in x)[0] - script = type(u'')(script) - raw_json = script[script.find('{'):script.rfind(';')].strip().rstrip(';') # }} - clean_json = self.nyt_parser.clean_js_json(raw_json) - self.nytimes_graphql_config = json.loads(clean_json)['config'] - return soup - - def nyt_graphql_query(self, qid, operationName='CollectionsQuery'): - query = { - 'operationName': operationName, - 'variables': json.dumps({ - 'id': qid, - 'first': 10, - 'exclusionMode': 'HIGHLIGHTS_AND_EMBEDDED', - 'isFetchMore':False, - 'isTranslatable':False, - 'isEspanol':False, - 'highlightsListUri':'nyt://per/personalized-list/__null__', - 'highlightsListFirst':0, - 'hasHighlightsList':False - }, separators=',:'), - 'extensions': json.dumps({ - 'persistedQuery': { - 'version':1, - 'sha256Hash': persistedQuery, - }, - }, separators=',:') - } - url = self.nytimes_graphql_config['gqlUrlClient'] + '?' + urlencode(query) - br = self.browser - # br.set_debug_http(True) - headers = dict(self.nytimes_graphql_config['gqlRequestHeaders']) - headers['Accept'] = 'application/json' - req = mechanize.Request(url, headers=headers) - raw = br.open(req).read() - # open('/t/raw.json', 'wb').write(raw) - return json.loads(raw) - - def parse_todays_page(self): - self.read_nyt_metadata() - query_id = '/issue/todayspaper/{}/todays-new-york-times'.format(self.nytimes_publication_date) - data = self.nyt_graphql_query(query_id) - return parse_todays_page(data, self.log) - - def parse_web_sections(self): - self.read_nyt_metadata() - feeds = [] - for section_title, slug in web_sections: - query_id = '/section/' + slug - try: - data = self.nyt_graphql_query(query_id) - self.log('Section:', section_title) - articles = parse_web_section(data, log=self.log, title=section_title) - except Exception as e: - self.log('Failed to parse section:', section_title, 'with error:', e) - articles = [] - if articles: - feeds.append((section_title, articles)) - else: - # open('/t/raw.json', 'w').write(json.dumps(data, indent=2)) - self.log(' No articles found in section:', section_title) - if self.test and len(feeds) >= self.test[0]: - break - return feeds - - def parse_index(self): - # return [('All articles', [ - # {'title': 'XXXXX', 'url': 'https://www.nytimes.com/2020/11/27/world/americas/coronavirus-migrants-venezuela.html'}, - # ])] - if self.is_web_edition: - return self.parse_web_sections() - return self.parse_todays_page() - - def get_browser(self, *args, **kwargs): - kwargs['user_agent'] = 'User-Agent: Mozilla/5.0 (compatible; archive.org_bot; Wayback Machine Live Record; +http://archive.org/details/archive.org_bot)' - br = BasicNewsRecipe.get_browser(self, *args, **kwargs) - return br - - def preprocess_html(self, soup): - w = self.recipe_specific_options.get('res') - if w and isinstance(w, str): - res = '-' + w - for img in soup.findAll('img', attrs={'src':True}): - if '-article' in img['src']: - ext = img['src'].split('?')[0].split('.')[-1] - img['src'] = img['src'].rsplit('-article', 1)[0] + res + '.' + ext - for c in soup.findAll('div', attrs={'class':'cap'}): - for p in c.findAll(['p', 'div']): - p.name = 'span' - return soup - - def get_article_url(self, article): - url = BasicNewsRecipe.get_article_url(self, article) - if not re.search(r'/video/|/athletic/|/card/', url): - return url - self.log('\tSkipping ', url) - - -def asset_to_article(asset): - title = asset['headline']['default'] - return {'title': title, 'url': asset['url'], 'description': asset['summary']} - - -def parse_todays_page(data, log=print): - containers = data['data']['legacyCollection']['groupings'][0]['containers'] - feeds = [] - for cont in containers: - if cont['__typename'] != 'LegacyCollectionContainer': - continue - section_name = cont['label'].strip() - if not section_name: - continue - log(section_name) - articles = [] - for rel in cont['relations']: - if rel.get('__typename') == 'LegacyCollectionRelation': - asset = rel['asset'] - if asset['__typename'] == 'Article': - articles.append(asset_to_article(asset)) - log(' ', articles[-1]['title'] + ':', articles[-1]['url']) - if articles: - feeds.append((section_name, articles)) - return feeds - - -def parse_web_section(data, log=print, title=''): - articles = [] - try: - containers = data['data']['legacyCollection']['collectionsPage'] - if containers.get('embeddedCollections'): - containers = containers['embeddedCollections'] - else: - containers = [containers] - except Exception as e: - log('Failed to parse web section', title, 'with error:', e) - return articles - for cont in containers: - for s in cont['stream']['edges']: - asset = s['node'] - if asset['__typename'] == 'Article': - articles.append(asset_to_article(asset)) - log(' ', articles[-1]['title'] + ':', articles[-1]['url']) - return articles - - -if __name__ == '__main__': - import sys - data = json.loads(open(sys.argv[-1], 'rb').read()) - if is_web_edition: - parse_web_section(data) - else: - parse_todays_page(data) - - -calibre_most_common_ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36' diff --git a/dotfiles/common/.config/calibre/custom_recipes/index.json b/dotfiles/common/.config/calibre/custom_recipes/index.json deleted file mode 100644 index 0d9451b..0000000 --- a/dotfiles/common/.config/calibre/custom_recipes/index.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "1001": [ - "The Economist", - "The Economist_1001.recipe" - ] -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/customize.py.json b/dotfiles/common/.config/calibre/customize.py.json deleted file mode 100644 index 4434e16..0000000 --- a/dotfiles/common/.config/calibre/customize.py.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "disabled_plugins": { - "__class__": "set", - "__value__": [ - "EbooksGratuits.com", - "Beam EBooks DE", - "ebook.de", - "Woblink", - "OZON.ru", - "Bubok Portugal", - "Baen Ebooks", - "Nexto", - "Bubok Spain", - "МоÑта библиотека", - "Amazon CA Kindle", - "Библио.бг", - "Amazon ES Kindle", - "Amazon IN Kindle", - "Empik", - "Weightless Books", - "Amazon UK Kindle", - "Amazon FR Kindle", - "Åšwiat Ebooków", - "Feedbooks", - "Legimi", - "Kobo", - "RW2010", - "Amazon AU Kindle", - "Ebookpoint", - "eBook.nl", - "Smashwords", - "Virtualo", - "Mills and Boon UK", - "Wolne Lektury", - "Amazon IT Kindle", - "MobileRead", - "Publio", - "Amazon DE Kindle", - "LitRes" - ] - }, - "enabled_plugins": { - "__class__": "set", - "__value__": [ - "Amazon Kindle", - "eBooks.com", - "Pragmatic Bookshelf", - "Project Gutenberg", - "Archive.org", - "Google Images", - "ManyBooks", - "Big Book Search", - "Google Books", - "Barnes and Noble" - ] - }, - "filetype_mapping": {}, - "plugin_customization": {}, - "plugins": { - "Apple Books covers": "/home/cjennings/.config/calibre/plugins/Apple Books covers.zip", - "Barnes & Noble": "/home/cjennings/.config/calibre/plugins/Barnes & Noble.zip", - "Clean Comments": "/home/cjennings/.config/calibre/plugins/Clean Comments.zip", - "Comments Cleaner": "/home/cjennings/.config/calibre/plugins/Comments Cleaner.zip", - "DeDRM": "/home/cjennings/.config/calibre/plugins/DeDRM.zip", - "Extract ISBN": "/home/cjennings/.config/calibre/plugins/Extract ISBN.zip", - "Favourites Menu": "/home/cjennings/.config/calibre/plugins/Favourites Menu.zip", - "Find Duplicates": "/home/cjennings/.config/calibre/plugins/Find Duplicates.zip", - "Hardcover": "/home/cjennings/.config/calibre/plugins/Hardcover.zip", - "KFX Input": "/home/cjennings/.config/calibre/plugins/KFX Input.zip", - "KFX Output": "/home/cjennings/.config/calibre/plugins/KFX Output.zip", - "Kindle Collections": "/home/cjennings/.config/calibre/plugins/Kindle Collections.zip", - "Kindle hi-res covers": "/home/cjennings/.config/calibre/plugins/Kindle hi-res covers.zip", - "Kobo Metadata": "/home/cjennings/.config/calibre/plugins/Kobo Metadata.zip", - "Kobo Utilities": "/home/cjennings/.config/calibre/plugins/Kobo Utilities.zip", - "Markdown Output": "/home/cjennings/.config/calibre/plugins/Markdown Output.zip", - "Reading List": "/home/cjennings/.config/calibre/plugins/Reading List.zip", - "Search The Internet": "/home/cjennings/.config/calibre/plugins/Search The Internet.zip", - "Standard Ebooks": "/home/cjennings/.config/calibre/plugins/Standard Ebooks.zip" - } -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/device_drivers_KINDLE2.py.json b/dotfiles/common/.config/calibre/device_drivers_KINDLE2.py.json deleted file mode 100644 index ad99d45..0000000 --- a/dotfiles/common/.config/calibre/device_drivers_KINDLE2.py.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "extra_customization": [ - true, - "fast", - "", - "", - true - ], - "format_map": [ - "azw", - "mobi", - "azw3", - "prc", - "azw1", - "tpz", - "azw4", - "kfx", - "pobi", - "txt", - "epub" - ], - "read_metadata": true, - "save_template": "{author_sort}/{title} - {authors}", - "use_author_sort": false, - "use_subdirs": true -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/device_drivers_KOBOTOUCH.py.json b/dotfiles/common/.config/calibre/device_drivers_KOBOTOUCH.py.json deleted file mode 100644 index 230e5b8..0000000 --- a/dotfiles/common/.config/calibre/device_drivers_KOBOTOUCH.py.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "affect_hyphenation": false, - "bookstats_pagecount_template": "", - "bookstats_timetoread_lower_template": "", - "bookstats_timetoread_upper_template": "", - "bookstats_wordcount_template": "", - "collections_columns": "", - "collections_template": "", - "create_collections": false, - "debugging_title": "", - "delete_empty_collections": false, - "disable_hyphenation": false, - "dithered_covers": false, - "driver_version": "2.6.0", - "extra_customization": [], - "force_series_id": false, - "format_map": [ - "kepub", - "epub", - "cbz", - "cbr" - ], - "hyphenation_limit_lines": 2, - "hyphenation_min_chars": 6, - "hyphenation_min_chars_after": 3, - "hyphenation_min_chars_before": 3, - "ignore_collections_names": "", - "keep_cover_aspect": false, - "kepubify": true, - "letterbox_fs_covers": false, - "letterbox_fs_covers_color": "#000000", - "manage_collections": true, - "modify_css": false, - "override_kobo_replace_existing": true, - "per_device_css": "{}", - "png_covers": false, - "read_metadata": true, - "save_template": "{author_sort}/{title} - {authors}", - "series_index_template": "", - "show_archived_books": false, - "show_previews": false, - "show_recommendations": false, - "subtitle_template": "", - "support_newer_firmware": false, - "template_for_kepubify": "", - "update_bookstats": false, - "update_core_metadata": false, - "update_device_metadata": true, - "update_purchased_kepubs": false, - "update_series": true, - "update_subtitle": false, - "upload_covers": true, - "upload_grayscale": false, - "use_author_sort": false, - "use_collections_columns": true, - "use_collections_template": false, - "use_series_index_template": false, - "use_subdirs": true -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/device_drivers_KOBOTOUCHEXTENDED.py.json b/dotfiles/common/.config/calibre/device_drivers_KOBOTOUCHEXTENDED.py.json deleted file mode 100644 index bfc0600..0000000 --- a/dotfiles/common/.config/calibre/device_drivers_KOBOTOUCHEXTENDED.py.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "bookstats_pagecount_template": "", - "bookstats_timetoread_lower_template": "", - "bookstats_timetoread_upper_template": "", - "bookstats_wordcount_template": "", - "clean_markup": false, - "collections_columns": "", - "collections_template": "", - "create_collections": false, - "debugging_title": "", - "delete_empty_collections": false, - "disable_hyphenation": false, - "dithered_covers": false, - "driver_version": "3.6.3", - "extra_customization": [], - "extra_features": true, - "file_copy_dir": "", - "format_map": [ - "kepub", - "epub", - "cbr", - "cbz" - ], - "full_page_numbers": false, - "hyphenate": false, - "hyphenate_chars": 6, - "hyphenate_chars_after": 3, - "hyphenate_chars_before": 3, - "hyphenate_limit_lines": 2, - "ignore_collections_names": "", - "keep_cover_aspect": false, - "kepubify_template": "", - "letterbox_fs_covers": false, - "letterbox_fs_covers_color": "#000000", - "manage_collections": true, - "modify_css": false, - "override_kobo_replace_existing": true, - "png_covers": false, - "read_metadata": true, - "save_template": "{author_sort}/{title} - {authors}", - "show_archived_books": false, - "show_previews": false, - "show_recommendations": false, - "skip_failed": false, - "smarten_punctuation": false, - "subtitle_template": "", - "support_newer_firmware": false, - "update_bookstats": false, - "update_core_metadata": false, - "update_device_metadata": true, - "update_purchased_kepubs": false, - "update_series": true, - "update_subtitle": false, - "upload_covers": false, - "upload_encumbered": false, - "upload_grayscale": false, - "use_author_sort": false, - "use_collections_columns": true, - "use_collections_template": false, - "use_subdirs": true, - "use_template": false -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/device_drivers_SMART_DEVICE_APP.py.json b/dotfiles/common/.config/calibre/device_drivers_SMART_DEVICE_APP.py.json deleted file mode 100644 index 09a8c01..0000000 --- a/dotfiles/common/.config/calibre/device_drivers_SMART_DEVICE_APP.py.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "extra_customization": [ - false, - "", - "", - "", - true, - "9090", - false, - "", - "", - "", - false, - "", - true, - "75", - true, - "", - "", - false - ], - "format_map": [ - "epub", - "fb2", - "mobi", - "azw", - "xps", - "doc", - "docx", - "djv", - "djvu", - "pdf", - "cbz", - "htm", - "html", - "xhtml", - "pdb", - "prc", - "rtf", - "txt", - "md", - "chm", - "zip" - ], - "read_metadata": true, - "save_template": "{title} - {authors} ({id})", - "use_author_sort": false, - "use_subdirs": true -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/device_drivers_USER_DEFINED.py.json b/dotfiles/common/.config/calibre/device_drivers_USER_DEFINED.py.json deleted file mode 100644 index 7963676..0000000 --- a/dotfiles/common/.config/calibre/device_drivers_USER_DEFINED.py.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "extra_customization": [ - "", - "", - "", - null, - "", - "", - "", - "", - "", - "", - false - ], - "format_map": [ - "epub", - "mobi", - "pdf" - ], - "read_metadata": true, - "save_template": "{author_sort}/{title} - {authors}", - "use_author_sort": false, - "use_subdirs": true -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/dynamic.pickle.json b/dotfiles/common/.config/calibre/dynamic.pickle.json deleted file mode 100644 index 8b74dcb..0000000 --- a/dotfiles/common/.config/calibre/dynamic.pickle.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "auto add path choose": "/home/cjennings/downloads/ebooks", - "library_delete_books_again": false, - "s_r_search_mode": 0, - "save to disk dialog": "/home/cjennings/projects/homelab/inbox", - "sort_history": [ - [ - "pubdate", - false - ], - [ - "timestamp", - false - ], - [ - "marked", - true - ], - [ - "timestamp", - false - ] - ], - "welcome_wizard_was_run": true -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/global.py.json b/dotfiles/common/.config/calibre/global.py.json deleted file mode 100644 index 43f47a0..0000000 --- a/dotfiles/common/.config/calibre/global.py.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "add_formats_to_existing": false, - "case_sensitive": false, - "check_for_dupes_on_ctl": false, - "database_path": "/home/cjennings/library1.db", - "filename_pattern": "(?P<title>.+) - (?P<author>[^_]+)", - "input_format_order": [ - "EPUB", - "PDF", - "AZW3", - "MOBI", - "LIT", - "PRC", - "FB2", - "HTML", - "HTM", - "XHTM", - "SHTML", - "XHTML", - "ZIP", - "DOCX", - "ODT", - "RTF", - "TXT", - "CB7", - "SHTM", - "SNB", - "PMLZ", - "TCR", - "FBZ", - "DJVU", - "DOCM", - "LRF", - "TXTZ", - "KEPUB", - "POBI", - "PDB", - "CHM", - "CBR", - "HTMLZ", - "RB", - "MD", - "CBC", - "MARKDOWN", - "TEXT", - "AZW", - "UPDB", - "RECIPE", - "PML", - "DOWNLOADED_RECIPE", - "TEXTILE", - "RAR", - "CBZ", - "OPF", - "AZW4", - "DJV" - ], - "installation_uuid": "4c998702-215a-4787-a019-abdee4cdf53c", - "isbndb_com_key": "", - "language": "en", - "library_path": "/home/cjennings/sync/books", - "limit_search_columns": false, - "limit_search_columns_to": [ - "title", - "authors", - "tags", - "publisher" - ], - "manage_device_metadata": "manual", - "mark_new_books": true, - "migrated": false, - "network_timeout": 5, - "new_book_tags": [], - "numeric_collation": false, - "output_format": "epub", - "read_file_metadata": true, - "saved_searches": {}, - "swap_author_names": false, - "use_primary_find_in_search": true, - "user_categories": {}, - "worker_process_priority": "normal" -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/gui.json b/dotfiles/common/.config/calibre/gui.json deleted file mode 100644 index 08e387c..0000000 --- a/dotfiles/common/.config/calibre/gui.json +++ /dev/null @@ -1,1469 +0,0 @@ -{ - "Keyboard shortcut dialog": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAV7AAABzAAAB54AAAMFAAAFewAAAcwAAAeeAAADBQAAAAACAAAADXAAAAV7AAABzAAAB54AAAMF" - }, - "action-layout-toolbar": [ - "Choose Library", - null, - "Edit Metadata", - null, - "Favourites Menu", - null, - "View", - null, - "Add Books", - "Save To Disk", - null, - "Remove Books", - null, - "Preferences" - ], - "action-layout-toolbar-device": [ - "Choose Library", - null, - "Location Manager", - "Send To Device", - null, - "Edit Metadata", - null, - "View", - null, - null, - "Add Books", - "Fetch News", - "Save To Disk", - null, - "Remove Books", - null, - "Preferences", - "Help" - ], - "all_on_one_metadata_splitter_1_state": { - "__class__": "bytearray", - "__value__": "AAAA/wAAAAEAAAACAAABnQAACX4B/////wEAAAACAA==" - }, - "all_on_one_metadata_splitter_2_state": { - "__class__": "bytearray", - "__value__": "AAAA/wAAAAEAAAACAAADLAAAATwB/////wEAAAABAA==" - }, - "all_on_one_metadata_splitter_3_state": { - "__class__": "bytearray", - "__value__": "AAAA/wAAAAEAAAACAAAB4gAAAGAB/////wEAAAACAA==" - }, - "auto_add_auto_convert": false, - "auto_add_check_for_duplicates": true, - "auto_add_path": "/home/cjennings/downloads/ebooks", - "basic_metadata_widget_splitter_state": { - "__class__": "bytearray", - "__value__": "AAAA/wAAAAEAAAADAAAA+gAAAXoAAAEKAf////8BAAAAAQA=" - }, - "book_details_widget_splitter_state_horizontal": { - "__class__": "bytearray", - "__value__": "AAAA/wAAAAEAAAACAAABmQAABYcB/////wEAAAABAA==" - }, - "book_info_dialog_splitter_state": { - "__class__": "bytearray", - "__value__": "AAAA/wAAAAEAAAACAAABXgAAAV4B/////wEAAAABAA==" - }, - "book_list_pin_splitter_state": { - "__class__": "bytearray", - "__value__": "AAAA/wAAAAEAAAACAAABAAAAAEYA/////wEAAAABAA==" - }, - "bulk-mde-casing-algorithm": "title_case", - "bulk_metadata_window_tab": 0, - "choose-merge-dialog-splitter-state": { - "__class__": "bytearray", - "__value__": "AAAA/wAAAAEAAAACAAABhgAAAQAA/////wEAAAABAA==" - }, - "choose_icon_theme_initial_tab": 2, - "color_palette": "dark", - "cover_grid_background": { - "dark": [ - 45, - 45, - 45 - ], - "dark_texture": null, - "light": [ - 80, - 80, - 80 - ], - "light_texture": null, - "migrated": true - }, - "custom_colors_for_color_dialog": [ - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ] - ], - "dark_palette_name": "", - "dark_palettes": { - "__current__": { - "AlternateBase": "#f7f7f7", - "Base": "#ffffff", - "BrightText": "#ffffff", - "BrightText-disabled": "#ffffff", - "Button": "#efefef", - "ButtonText": "#000000", - "ButtonText-disabled": "#bebebe", - "Highlight": "#308cc6", - "HighlightedText": "#ffffff", - "HighlightedText-disabled": "#ffffff", - "Link": "#0000ff", - "LinkVisited": "#ff00ff", - "PlaceholderText": "#000000", - "PlaceholderText-disabled": "#000000", - "Text": "#000000", - "Text-disabled": "#bebebe", - "ToolTipBase": "#ffffdc", - "ToolTipText": "#000000", - "ToolTipText-disabled": "#000000", - "Window": "#efefef", - "WindowText": "#000000", - "WindowText-disabled": "#bebebe" - }, - "dupre": { - "AlternateBase": "#252321", - "Base": "#151311", - "BrightText": "#d7af5f", - "BrightText-disabled": "#58574e", - "Button": "#474544", - "ButtonText": "#d0cbc0", - "ButtonText-disabled": "#58574e", - "Highlight": "#d7af5f", - "HighlightedText": "#151311", - "HighlightedText-disabled": "#58574e", - "Link": "#67809c", - "LinkVisited": "#b294bb", - "PlaceholderText": "#58574e", - "PlaceholderText-disabled": "#58574e", - "Text": "#f0fef0", - "Text-disabled": "#58574e", - "ToolTipBase": "#252321", - "ToolTipText": "#d0cbc0", - "ToolTipText-disabled": "#58574e", - "Window": "#151311", - "WindowText": "#d0cbc0", - "WindowText-disabled": "#58574e" - } - }, - "duplicate finder plugin:duplicate dialog": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAAAkEAAAImAAAAAAAAAAAAAAJBAAACJgAAAAACAAAADXAAAAAAAAAAAAAAAkEAAAIm" - }, - "edit_metadata_single_layout": "alt2", - "filename_pattern_history": [ - "(?P<title>.+) - (?P<author>[^_]+)", - "(?P<title>.+)", - "(?P<author>[^_-]+) -?\\s*(?P<series>[^_0-9-]*)(?P<series_index>[0-9]*)\\s*-\\s*(?P<title>[^_].+) ?" - ], - "geometry-of-Plugin Updater plugin:plugin updater dialog": { - "frame_geometry": { - "height": 480, - "width": 968, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 480, - "width": 968, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 480, - "width": 968, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAAA8cAAAHfAAAAAAAAAAAAAAPHAAAB3wAAAAACAAAADXAAAAAAAAAAAAAAA8cAAAHf" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-book_info_dialog_geometry": { - "frame_geometry": { - "height": 1340, - "width": 1670, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 1340, - "width": 1670, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 1340, - "width": 1670, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAABoUAAAU7AAAAAAAAAAAAAAaFAAAFOwAAAAACAAAADXAAAAAAAAAAAAAABoUAAAU7" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-bulk_metadata_window_geometry": { - "frame_geometry": { - "height": 888, - "width": 1638, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 888, - "width": 1638, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 888, - "width": 1638, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAABmUAAAN3AAAAAAAAAAAAAAZlAAADdwAAAAACAAAADXAAAAAAAAAAAAAABmUAAAN3" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-calibre_main_window_geometry": { - "frame_geometry": { - "height": 1314, - "width": 1828, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 1314, - "width": 1828, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 1314, - "width": 1828, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAAByMAAAUhAAAAAAAAAAAAAAcjAAAFIQAAAAACAAAADXAAAAAAAAAAAAAAByMAAAUh" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-choose-icon-theme-dialog-geometry": { - "frame_geometry": { - "height": 1365, - "width": 900, - "x": 5, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 1365, - "width": 900, - "x": 5, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 1365, - "width": 900, - "x": 5, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAFAAAAAAAAA4gAAAVUAAAABQAAAAAAAAOIAAAFVAAAAAACAAAADXAAAAAFAAAAAAAAA4gAAAVU" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "HDMI-A-1", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-choose-merge-dialog-geometry": { - "frame_geometry": { - "height": 309, - "width": 668, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 309, - "width": 668, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 309, - "width": 668, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAAApsAAAE0AAAAAAAAAAAAAAKbAAABNAAAAAACAAAADXAAAAAAAAAAAAAAApsAAAE0" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-create-virtual-library-dialog": { - "frame_geometry": { - "height": 341, - "width": 798, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 341, - "width": 798, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 341, - "width": 798, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAAAx0AAAFUAAAAAAAAAAAAAAMdAAABVAAAAAACAAAADXAAAAAAAAAAAAAAAx0AAAFU" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-customize-palette-geometry": { - "frame_geometry": { - "height": 555, - "width": 826, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 555, - "width": 826, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 555, - "width": 826, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAAAzkAAAIqAAAAAAAAAAAAAAM5AAACKgAAAAACAAAADXAAAAAAAAAAAAAAAzkAAAIq" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-duplicates-question-dialog-geometry": { - "frame_geometry": { - "height": 378, - "width": 560, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 378, - "width": 560, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 378, - "width": 560, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAAAi8AAAF5AAAAAAAAAAAAAAIvAAABeQAAAAACAAAADXAAAAAAAAAAAAAAAi8AAAF5" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-edit-tag-mapper-rule-geometry": { - "frame_geometry": { - "height": 1314, - "width": 1828, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 1314, - "width": 1828, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 1314, - "width": 1828, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAAByMAAAUhAAAAAAAAAAAAAAcjAAAFIQAAAAACAAAADXAAAAAAAAAAAAAAByMAAAUh" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-edit-tag-mapper-rules-geometry": { - "frame_geometry": { - "height": 358, - "width": 922, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 358, - "width": 922, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 358, - "width": 922, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAAA5kAAAFlAAAAAAAAAAAAAAOZAAABZQAAAAACAAAADXAAAAAAAAAAAAAAA5kAAAFl" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-jobs_dialog_geometry": { - "frame_geometry": { - "height": 612, - "width": 747, - "x": 712, - "y": 450 - }, - "full_screened": false, - "geometry": { - "height": 612, - "width": 747, - "x": 712, - "y": 450 - }, - "maximized": true, - "normal_geometry": { - "height": 612, - "width": 747, - "x": 712, - "y": 450 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAALIAAABwgAABbIAAAQlAAACyAAAAcIAAAWyAAAEJQAAAAACAAAADXAAAALIAAABwgAABbIAAAQl" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-metadata_single_gui_geom": { - "frame_geometry": { - "height": 664, - "width": 1101, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 664, - "width": 1101, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 664, - "width": 1101, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAABEwAAAKXAAAAAAAAAAAAAARMAAAClwAAAAACAAAADXAAAAAAAAAAAAAABEwAAAKX" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-metasingle_window_geometry3": { - "frame_geometry": { - "height": 1043, - "width": 1272, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 1043, - "width": 1272, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 1043, - "width": 1272, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAABPcAAAQSAAAAAAAAAAAAAAT3AAAEEgAAAAACAAAADXAAAAAAAAAAAAAABPcAAAQS" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-plugin config dialog:User interface action:Extract ISBN": { - "frame_geometry": { - "height": 305, - "width": 487, - "x": 1434, - "y": 465 - }, - "full_screened": false, - "geometry": { - "height": 305, - "width": 487, - "x": 1434, - "y": 465 - }, - "maximized": true, - "normal_geometry": { - "height": 305, - "width": 487, - "x": 1434, - "y": 465 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAWaAAAB0QAAB4AAAAMBAAAFmgAAAdEAAAeAAAADAQAAAAACAAAADXAAAAWaAAAB0QAAB4AAAAMB" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-plugin config dialog:User interface action:Favourites Menu": { - "frame_geometry": { - "height": 1006, - "width": 1349, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 1006, - "width": 1349, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 1006, - "width": 1349, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAABUQAAAPtAAAAAAAAAAAAAAVEAAAD7QAAAAACAAAADXAAAAAAAAAAAAAABUQAAAPt" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-plugin config dialog:User interface action:Reading List": { - "frame_geometry": { - "height": 874, - "width": 1034, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 874, - "width": 1034, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 874, - "width": 1034, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAABAkAAANpAAAAAAAAAAAAAAQJAAADaQAAAAACAAAADXAAAAAAAAAAAAAABAkAAANp" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-preferences dialog geometry": { - "frame_geometry": { - "height": 826, - "width": 1077, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 826, - "width": 1077, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 826, - "width": 1077, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAABDQAAAM5AAAAAAAAAAAAAAQ0AAADOQAAAAACAAAADXAAAAAAAAAAAAAABDQAAAM5" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-scheduler_dialog_geometry": { - "frame_geometry": { - "height": 1306, - "width": 1828, - "x": 1310, - "y": 380 - }, - "full_screened": false, - "geometry": { - "height": 1306, - "width": 1828, - "x": 1310, - "y": 380 - }, - "maximized": true, - "normal_geometry": { - "height": 1306, - "width": 1828, - "x": 1310, - "y": 380 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAUeAAABfAAADEEAAAaVAAAFHgAAAXwAAAxBAAAGlQAAAAACAAAADXAAAAUeAAABfAAADEEAAAaV" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "HDMI-A-1", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-single-cover-fetch-dialog-geometry": { - "frame_geometry": { - "height": 635, - "width": 962, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 635, - "width": 962, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 635, - "width": 962, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAAA8EAAAJ6AAAAAAAAAAAAAAPBAAACegAAAAACAAAADXAAAAAAAAAAAAAAA8EAAAJ6" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-tag_editor_geometry": { - "frame_geometry": { - "height": 324, - "width": 600, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 324, - "width": 600, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 324, - "width": 600, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAAAlcAAAFDAAAAAAAAAAAAAAJXAAABQwAAAAACAAAADXAAAAAAAAAAAAAAAlcAAAFD" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "geometry-of-trash-view-for-library-geometry": { - "frame_geometry": { - "height": 650, - "width": 563, - "x": 1396, - "y": 292 - }, - "full_screened": false, - "geometry": { - "height": 650, - "width": 563, - "x": 1396, - "y": 292 - }, - "maximized": true, - "normal_geometry": { - "height": 650, - "width": 563, - "x": 1396, - "y": 292 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAV0AAABJAAAB6YAAAOtAAAFdAAAASQAAAemAAADrQAAAAACAAAADXAAAAV0AAABJAAAB6YAAAOt" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "grid view visible": false, - "jobs view column layout3": { - "__class__": "bytearray", - "__value__": "AAAA/wAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv4AAAAFAQEBAAAAAAAAAAAAAAAAAGT/////AAAAhAAAAAAAAAAFAAABbgAAAAEAAAAAAAAAZAAAAAEAAAAAAAAAZAAAAAEAAAAAAAAAZAAAAAEAAAAAAAAAZAAAAAEAAAAAAAAD6AAAAAAAAAAAAAAAAAAAAAAAAAAAAQ==" - }, - "library_usage_stats": { - "/home/cjennings/archive/books": 10, - "/home/cjennings/sync/books": 74 - }, - "light_palette_name": "", - "light_palettes": { - "__current__": { - "AlternateBase": "#f5f5f5", - "Base": "#ffffff", - "BrightText": "#ff0000", - "BrightText-disabled": "#ff0000", - "Button": "#f0f0f0", - "ButtonText": "#000000", - "ButtonText-disabled": "#787878", - "Highlight": "#308cc6", - "HighlightedText": "#ffffff", - "HighlightedText-disabled": "#787878", - "Link": "#0000ff", - "LinkVisited": "#ff00ff", - "PlaceholderText": "#787878", - "PlaceholderText-disabled": "#787878", - "Text": "#000000", - "Text-disabled": "#787878", - "ToolTipBase": "#ffffdc", - "ToolTipText": "#000000", - "ToolTipText-disabled": "#000000", - "Window": "#f0f0f0", - "WindowText": "#000000", - "WindowText-disabled": "#000000" - } - }, - "main_window_central_widget_state": { - "layout": "narrow", - "narrow_desires": { - "book_details_height": 0.2306843267108168, - "cover_browser_width": 0.3496932515337423, - "quick_view_height": 0.2560706401766004, - "tag_browser_width": 0.1487964989059081 - }, - "narrow_visibility": { - "book_details": true, - "book_list": true, - "cover_browser": false, - "quick_view": false, - "tag_browser": true - }, - "wide_desires": { - "book_details_width": 0.3, - "cover_browser_height": 0.2858606557377049, - "quick_view_height": 0.23770491803278687, - "tag_browser_width": 0.3 - }, - "wide_visibility": { - "book_details": true, - "book_list": true, - "cover_browser": false, - "quick_view": false, - "tag_browser": true - } - }, - "metadata-download-identify-widget-splitter-state": { - "__class__": "bytearray", - "__value__": "AAAA/wAAAAEAAAACAAACAAAAAQAA/////wEAAAABAA==" - }, - "previous_sort_order_BooksView": { - "#isbn": true, - "authors": true, - "marked": true, - "pubdate": false, - "timestamp": false, - "title": true - }, - "quick_start_guide_added": true, - "qv_open_at_shutdown": false, - "reading list plugin:edit list dialog": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAXdAAABsgAACcsAAAR3AAAF3QAAAbIAAAc8AAADIAAAAAACAAAADXAAAAXdAAABsgAACcsAAAR3" - }, - "recently_used_languages": [ - "English" - ], - "search bar visible": true, - "server3_warning_done": true, - "show_splash_screen": false, - "show_vl_tabs": true, - "tag browser search box visible": false, - "toolbar_icon_size": "small" -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/gui.py.json b/dotfiles/common/.config/calibre/gui.py.json deleted file mode 100644 index e3c44c5..0000000 --- a/dotfiles/common/.config/calibre/gui.py.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "LRF_conversion_defaults": [], - "LRF_ebook_viewer_options": null, - "asked_library_thing_password": false, - "auto_download_cover": false, - "autolaunch_server": false, - "column_map": [ - "title", - "ondevice", - "authors", - "size", - "timestamp", - "rating", - "publisher", - "tags", - "series", - "pubdate" - ], - "confirm_delete": false, - "cover_flow_queue_length": 6, - "default_send_to_device_action": "DeviceAction:main::False:False", - "delete_news_from_library_on_upload": false, - "disable_animations": true, - "disable_tray_notification": false, - "enforce_cpu_limit": true, - "get_social_metadata": true, - "gui_layout": "wide", - "highlight_search_matches": false, - "internally_viewed_formats": [ - "AZW", - "AZW3", - "FB2", - "FBZ", - "HTML", - "HTMLZ", - "KEPUB", - "LIT", - "LRF", - "MOBI", - "PDB", - "POBI", - "PRC", - "RB", - "SNB" - ], - "jobs_search_history": [], - "lrf_viewer_search_history": [], - "main_search_history": [ - "title:architecture", - "tag:favorite", - "tag:review-author", - "tag:review-author author:calibre", - "tag:news", - "tag:Computers", - "software architecture", - "architecture", - "author:lee braver", - "caminos", - "contaldo", - "genarro", - "zevon", - "warren zevon", - "author:kant", - "title:critique of pure reason", - "title critique of pure reason", - "title:being and time", - "heidegger basic writings", - "title:question concerning technology", - "tag:epub", - "question concerning technology heidegger", - "poetry language thought heidegger", - "basic writings heidegger", - "author:corrington" - ], - "main_window_geometry": null, - "match_tags_type": "any", - "new_version_notification": false, - "oldest_news": 0, - "overwrite_author_title_metadata": true, - "plugin_search_history": [ - "kobo", - "covers", - "cover", - "rsync" - ], - "save_to_disk_template_history": [ - "{author_sort}/{title}/{title} - {authors}" - ], - "scheduler_search_history": [ - "Jacobin", - "New Republic" - ], - "search_as_you_type": false, - "send_to_device_template_history": [], - "send_to_storage_card_by_default": false, - "separate_cover_flow": false, - "shortcuts_search_history": [ - "quit", - "quickview", - "q" - ], - "show_avg_rating": true, - "sort_tags_by": "name", - "systray_icon": false, - "tag_browser_hidden_categories": { - "__class__": "set", - "__value__": [] - }, - "tweaks_search_history": [], - "upload_news_to_device": false, - "use_roman_numerals_for_series_number": false, - "viewer_search_history": [], - "viewer_toc_search_history": [], - "worker_limit": 6 -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/history.plist b/dotfiles/common/.config/calibre/history.plist deleted file mode 100644 index 14189a8..0000000 --- a/dotfiles/common/.config/calibre/history.plist +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>lineedit_history_choose_library_dialog</key> - <array> - <string>/home/cjennings/sync/books</string> - <string>/home/cjennings/archive/books</string> - <string>/home/cjennings/books</string> - <string>/home/cjennings/Library</string> - </array> - <key>lineedit_history_preferences_setting_auto_add_path</key> - <array> - <string>/home/cjennings/downloads/ebooks</string> - <string>/home/cjennings/</string> - <string>/home/cjennings/Downloads/ebooks</string> - <string>/home/cjennings/Downloads/eBooks</string> - <string>/home/cjennings/Documents/eBooks</string> - </array> - <key>lineedit_history_store_config_chooser_query</key> - <array/> - <key>lineedit_history_tag_browser_search</key> - <array/> -</dict> -</plist> diff --git a/dotfiles/common/.config/calibre/metadata-sources-cache.json b/dotfiles/common/.config/calibre/metadata-sources-cache.json deleted file mode 100644 index 20a91e9..0000000 --- a/dotfiles/common/.config/calibre/metadata-sources-cache.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "amazon": "#!/usr/bin/env python\n# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai\n# License: GPLv3 Copyright: 2011, Kovid Goyal <kovid at kovidgoyal.net>\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport re\nimport socket\nimport string\nimport time\nfrom functools import partial\n\ntry:\n from queue import Empty, Queue\nexcept ImportError:\n from Queue import Empty, Queue\n\nfrom threading import Thread\n\ntry:\n from urllib.parse import urlparse\nexcept ImportError:\n from urlparse import urlparse\n\nfrom mechanize import HTTPError\n\nfrom calibre import as_unicode, browser, random_user_agent, xml_replace_entities\nfrom calibre.ebooks.metadata import check_isbn\nfrom calibre.ebooks.metadata.book.base import Metadata\nfrom calibre.ebooks.metadata.sources.base import Option, Source, fixauthors, fixcase\nfrom calibre.utils.icu import lower as icu_lower\nfrom calibre.utils.localization import canonicalize_lang\nfrom calibre.utils.random_ua import accept_header_for_ua\n\n\ndef sort_matches_preferring_kindle_editions(matches):\n upos_map = {url:i for i, url in enumerate(matches)}\n\n def skey(url):\n opos = upos_map[url]\n parts = url.split('/')\n try:\n idx = parts.index('dp')\n except Exception:\n idx = -1\n if idx < 0 or idx + 1 >= len(parts) or not parts[idx+1].startswith('B'):\n return 1, opos\n return 0, opos\n matches.sort(key=skey)\n return matches\n\n\ndef iri_quote_plus(url):\n from calibre.ebooks.oeb.base import urlquote\n ans = urlquote(url)\n if isinstance(ans, bytes):\n ans = ans.decode('utf-8')\n return ans.replace('%20', '+')\n\n\ndef user_agent_is_ok(ua):\n return 'Mobile/' not in ua and 'Mobile ' not in ua\n\n\nclass CaptchaError(Exception):\n pass\n\n\nclass SearchFailed(ValueError):\n pass\n\n\nclass UrlNotFound(ValueError):\n\n def __init__(self, url):\n ValueError.__init__(self, 'The URL {} was not found (HTTP 404)'.format(url))\n\n\nclass UrlTimedOut(ValueError):\n\n def __init__(self, url):\n ValueError.__init__(self, 'Timed out fetching {} try again later'.format(url))\n\n\ndef parse_html(raw):\n try:\n from html5_parser import parse\n except ImportError:\n # Old versions of calibre\n import html5lib\n return html5lib.parse(raw, treebuilder='lxml', namespaceHTMLElements=False)\n else:\n return parse(raw)\n\n\ndef parse_details_page(url, log, timeout, browser, domain):\n from lxml.html import tostring\n\n from calibre.ebooks.chardet import xml_to_unicode\n from calibre.utils.cleantext import clean_ascii_chars\n try:\n from calibre.ebooks.metadata.sources.update import search_engines_module\n get_data_for_cached_url = search_engines_module().get_data_for_cached_url\n except Exception:\n def get_data_for_cached_url(*a):\n return None\n raw = get_data_for_cached_url(url)\n if raw:\n log('Using cached details for url:', url)\n else:\n log('Downloading details from:', url)\n try:\n raw = browser.open_novisit(url, timeout=timeout).read().strip()\n except Exception as e:\n if callable(getattr(e, 'getcode', None)) and e.getcode() == 404:\n log.error('URL not found: %r' % url)\n raise UrlNotFound(url)\n attr = getattr(e, 'args', [None])\n attr = attr or [None]\n if isinstance(attr[0], socket.timeout):\n msg = 'Details page timed out. Try again later.'\n log.error(msg)\n raise UrlTimedOut(url)\n else:\n msg = 'Failed to make details query: %r' % url\n log.exception(msg)\n raise ValueError('Could not make details query for {}'.format(url))\n\n oraw = raw\n if 'amazon.com.br' in url:\n # amazon.com.br serves utf-8 but has an incorrect latin1 <meta> tag\n raw = raw.decode('utf-8')\n raw = xml_to_unicode(raw, strip_encoding_pats=True,\n resolve_entities=True)[0]\n if '<title>404 - ' in raw:\n raise ValueError('Got a 404 page for: %r' % url)\n if '>Could not find the requested document in the cache.<' in raw:\n raise ValueError('No cached entry for %s found' % url)\n\n try:\n root = parse_html(clean_ascii_chars(raw))\n except Exception:\n msg = 'Failed to parse amazon details page: %r' % url\n log.exception(msg)\n raise ValueError(msg)\n if domain == 'jp':\n for a in root.xpath('//a[@href]'):\n if ('black-curtain-redirect.html' in a.get('href')) or ('/black-curtain/save-eligibility/black-curtain' in a.get('href')):\n url = a.get('href')\n if url:\n if url.startswith('/'):\n url = 'https://amazon.co.jp' + a.get('href')\n log('Black curtain redirect found, following')\n return parse_details_page(url, log, timeout, browser, domain)\n\n errmsg = root.xpath('//*[@id=\"errorMessage\"]')\n if errmsg:\n msg = 'Failed to parse amazon details page: %r' % url\n msg += tostring(errmsg, method='text', encoding='unicode').strip()\n log.error(msg)\n raise ValueError(msg)\n\n from css_selectors import Select\n selector = Select(root)\n return oraw, root, selector\n\n\ndef parse_asin(root, log, url):\n try:\n link = root.xpath('//link[@rel=\"canonical\" and @href]')\n for l in link:\n return l.get('href').rpartition('/')[-1]\n except Exception:\n log.exception('Error parsing ASIN for url: %r' % url)\n\n\nclass Worker(Thread): # Get details {{{\n\n '''\n Get book details from amazons book page in a separate thread\n '''\n\n def __init__(self, url, result_queue, browser, log, relevance, domain,\n plugin, timeout=20, testing=False, preparsed_root=None,\n cover_url_processor=None, filter_result=None):\n Thread.__init__(self)\n self.cover_url_processor = cover_url_processor\n self.preparsed_root = preparsed_root\n self.daemon = True\n self.testing = testing\n self.url, self.result_queue = url, result_queue\n self.log, self.timeout = log, timeout\n self.filter_result = filter_result or (lambda x, log: True)\n self.relevance, self.plugin = relevance, plugin\n self.browser = browser\n self.cover_url = self.amazon_id = self.isbn = None\n self.domain = domain\n from lxml.html import tostring\n self.tostring = tostring\n\n months = { # {{{\n 'de': {\n 1: ['jän', 'januar'],\n 2: ['februar'],\n 3: ['märz'],\n 5: ['mai'],\n 6: ['juni'],\n 7: ['juli'],\n 10: ['okt', 'oktober'],\n 12: ['dez', 'dezember']\n },\n 'it': {\n 1: ['gennaio', 'enn'],\n 2: ['febbraio', 'febbr'],\n 3: ['marzo'],\n 4: ['aprile'],\n 5: ['maggio', 'magg'],\n 6: ['giugno'],\n 7: ['luglio'],\n 8: ['agosto', 'ag'],\n 9: ['settembre', 'sett'],\n 10: ['ottobre', 'ott'],\n 11: ['novembre'],\n 12: ['dicembre', 'dic'],\n },\n 'fr': {\n 1: ['janv'],\n 2: ['févr'],\n 3: ['mars'],\n 4: ['avril'],\n 5: ['mai'],\n 6: ['juin'],\n 7: ['juil'],\n 8: ['août'],\n 9: ['sept'],\n 10: ['oct', 'octobre'],\n 11: ['nov', 'novembre'],\n 12: ['déc', 'décembre'],\n },\n 'br': {\n 1: ['janeiro'],\n 2: ['fevereiro'],\n 3: ['março'],\n 4: ['abril'],\n 5: ['maio'],\n 6: ['junho'],\n 7: ['julho'],\n 8: ['agosto'],\n 9: ['setembro'],\n 10: ['outubro'],\n 11: ['novembro'],\n 12: ['dezembro'],\n },\n 'es': {\n 1: ['enero'],\n 2: ['febrero'],\n 3: ['marzo'],\n 4: ['abril'],\n 5: ['mayo'],\n 6: ['junio'],\n 7: ['julio'],\n 8: ['agosto'],\n 9: ['septiembre', 'setiembre'],\n 10: ['octubre'],\n 11: ['noviembre'],\n 12: ['diciembre'],\n },\n 'se': {\n 1: ['januari'],\n 2: ['februari'],\n 3: ['mars'],\n 4: ['april'],\n 5: ['maj'],\n 6: ['juni'],\n 7: ['juli'],\n 8: ['augusti'],\n 9: ['september'],\n 10: ['oktober'],\n 11: ['november'],\n 12: ['december'],\n },\n 'jp': {\n 1: ['1月'],\n 2: ['2月'],\n 3: ['3月'],\n 4: ['4月'],\n 5: ['5月'],\n 6: ['6月'],\n 7: ['7月'],\n 8: ['8月'],\n 9: ['9月'],\n 10: ['10月'],\n 11: ['11月'],\n 12: ['12月'],\n },\n 'nl': {\n 1: ['januari'], 2: ['februari'], 3: ['maart'], 5: ['mei'], 6: ['juni'], 7: ['juli'], 8: ['augustus'], 10: ['oktober'],\n }\n\n } # }}}\n\n self.english_months = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']\n self.months = months.get(self.domain, {})\n\n self.pd_xpath = '''\n //h2[text()=\"Product Details\" or \\\n text()=\"Produktinformation\" or \\\n text()=\"Dettagli prodotto\" or \\\n text()=\"Product details\" or \\\n text()=\"Détails sur le produit\" or \\\n text()=\"Detalles del producto\" or \\\n text()=\"Detalhes do produto\" or \\\n text()=\"Productgegevens\" or \\\n text()=\"基本信æ¯\" or \\\n starts-with(text(), \"ç™»éŒ²æƒ…å ±\")]/../div[@class=\"content\"]\n '''\n # Editor: is for Spanish\n self.publisher_xpath = '''\n descendant::*[starts-with(text(), \"Publisher:\") or \\\n starts-with(text(), \"Verlag:\") or \\\n starts-with(text(), \"Editore:\") or \\\n starts-with(text(), \"Editeur\") or \\\n starts-with(text(), \"Editor:\") or \\\n starts-with(text(), \"Editora:\") or \\\n starts-with(text(), \"Uitgever:\") or \\\n starts-with(text(), \"Utgivare:\") or \\\n starts-with(text(), \"出版社:\")]\n '''\n self.pubdate_xpath = '''\n descendant::*[starts-with(text(), \"Publication Date:\") or \\\n starts-with(text(), \"Audible.com Release Date:\") or \\\n starts-with(text(), \"発売日:\")]\n '''\n self.publisher_names = {'Publisher', 'Uitgever', 'Verlag', 'Utgivare', 'Herausgeber',\n 'Editore', 'Editeur', 'Éditeur', 'Editor', 'Editora', '出版社'}\n\n self.language_xpath = '''\n descendant::*[\n starts-with(text(), \"Language:\") \\\n or text() = \"Language\" \\\n or text() = \"Sprache:\" \\\n or text() = \"Lingua:\" \\\n or text() = \"Idioma:\" \\\n or starts-with(text(), \"Langue\") \\\n or starts-with(text(), \"言語\") \\\n or starts-with(text(), \"SprÃ¥k\") \\\n or starts-with(text(), \"è¯ç§\")\n ]\n '''\n self.language_names = {'Language', 'Sprache', 'SprÃ¥k',\n 'Lingua', 'Idioma', 'Langue', '言語', 'Taal', 'è¯ç§'}\n\n self.tags_xpath = '''\n descendant::h2[\n text() = \"Look for Similar Items by Category\" or\n text() = \"Ähnliche Artikel finden\" or\n text() = \"Buscar productos similares por categorÃa\" or\n text() = \"Ricerca articoli simili per categoria\" or\n text() = \"Rechercher des articles similaires par rubrique\" or\n text() = \"Procure por items similares por categoria\" or\n text() = \"関連商å“を探ã™\"\n ]/../descendant::ul/li\n '''\n\n self.ratings_pat = re.compile(\n r'([0-9.,]+) ?(out of|von|van|su|étoiles sur|ã¤æ˜Ÿã®ã†ã¡|de un máximo de|de|av) '\n r'([\\d\\.]+)( (stars|Sternen|stelle|estrellas|estrelas|sterren|stjärnor)){0,1}'\n )\n self.ratings_pat_cn = re.compile(r'([0-9.]+) 颗星,最多 5 颗星')\n self.ratings_pat_jp = re.compile(r'\\d+ã¤æ˜Ÿã®ã†ã¡([\\d\\.]+)')\n\n lm = {\n 'eng': ('English', 'Englisch', 'Engels', 'Engelska'),\n 'fra': ('French', 'Français'),\n 'ita': ('Italian', 'Italiano'),\n 'deu': ('German', 'Deutsch'),\n 'spa': ('Spanish', 'Espa\\xf1ol', 'Espaniol'),\n 'jpn': ('Japanese', '日本語'),\n 'por': ('Portuguese', 'Português'),\n 'nld': ('Dutch', 'Nederlands',),\n 'chs': ('Chinese', '䏿–‡', 'ç®€ä½“ä¸æ–‡'),\n 'swe': ('Swedish', 'Svenska'),\n }\n self.lang_map = {}\n for code, names in lm.items():\n for name in names:\n self.lang_map[name] = code\n\n self.series_pat = re.compile(\n r'''\n \\|\\s* # Prefix\n (Series)\\s*:\\s* # Series declaration\n (?P<series>.+?)\\s+ # The series name\n \\((Book)\\s* # Book declaration\n (?P<index>[0-9.]+) # Series index\n \\s*\\)\n ''', re.X)\n\n def delocalize_datestr(self, raw):\n if self.domain == 'cn':\n return raw.replace('å¹´', '-').replace('月', '-').replace('æ—¥', '')\n if not self.months:\n return raw\n ans = raw.lower()\n for i, vals in self.months.items():\n for x in vals:\n ans = ans.replace(x, self.english_months[i])\n ans = ans.replace(' de ', ' ')\n return ans\n\n def run(self):\n try:\n self.get_details()\n except Exception:\n self.log.exception('get_details failed for url: %r' % self.url)\n\n def get_details(self):\n if self.preparsed_root is None:\n raw, root, selector = parse_details_page(\n self.url, self.log, self.timeout, self.browser, self.domain)\n else:\n raw, root, selector = self.preparsed_root\n\n from css_selectors import Select\n self.selector = Select(root)\n self.parse_details(raw, root)\n\n def parse_details(self, raw, root):\n asin = parse_asin(root, self.log, self.url)\n if not asin and root.xpath('//form[@action=\"/errors/validateCaptcha\"]'):\n raise CaptchaError(\n 'Amazon returned a CAPTCHA page, probably because you downloaded too many books. Wait for some time and try again.')\n if self.testing:\n import tempfile\n import uuid\n\n from calibre.ptempfile import get_default_tempdir\n with tempfile.NamedTemporaryFile(prefix=(asin or type('')(uuid.uuid4())) + '_',\n suffix='.html', delete=False, dir=get_default_tempdir()) as f:\n f.write(raw)\n print('Downloaded HTML for', asin, 'saved in', f.name)\n\n try:\n title = self.parse_title(root)\n except Exception:\n self.log.exception('Error parsing title for url: %r' % self.url)\n title = None\n\n try:\n authors = self.parse_authors(root)\n except Exception:\n self.log.exception('Error parsing authors for url: %r' % self.url)\n authors = []\n\n if not title or not authors or not asin:\n self.log.error(\n 'Could not find title/authors/asin for %r' % self.url)\n self.log.error('ASIN: %r Title: %r Authors: %r' % (asin, title,\n authors))\n return\n\n mi = Metadata(title, authors)\n idtype = 'amazon' if self.domain == 'com' else 'amazon_' + self.domain\n mi.set_identifier(idtype, asin)\n self.amazon_id = asin\n\n try:\n mi.rating = self.parse_rating(root)\n except Exception:\n self.log.exception('Error parsing ratings for url: %r' % self.url)\n\n try:\n mi.comments = self.parse_comments(root, raw)\n except Exception:\n self.log.exception('Error parsing comments for url: %r' % self.url)\n\n try:\n series, series_index = self.parse_series(root)\n if series:\n mi.series, mi.series_index = series, series_index\n elif self.testing:\n mi.series, mi.series_index = 'Dummy series for testing', 1\n except Exception:\n self.log.exception('Error parsing series for url: %r' % self.url)\n\n try:\n mi.tags = self.parse_tags(root)\n except Exception:\n self.log.exception('Error parsing tags for url: %r' % self.url)\n\n try:\n self.cover_url = self.parse_cover(root, raw)\n except Exception:\n self.log.exception('Error parsing cover for url: %r' % self.url)\n if self.cover_url_processor is not None and self.cover_url and self.cover_url.startswith('/'):\n self.cover_url = self.cover_url_processor(self.cover_url)\n mi.has_cover = bool(self.cover_url)\n\n detail_bullets = root.xpath('//*[@data-feature-name=\"detailBullets\"]')\n non_hero = tuple(self.selector(\n 'div#bookDetails_container_div div#nonHeroSection')) or tuple(self.selector(\n '#productDetails_techSpec_sections'))\n feature_and_detail_bullets = root.xpath('//*[@data-feature-name=\"featureBulletsAndDetailBullets\"]')\n if detail_bullets:\n self.parse_detail_bullets(root, mi, detail_bullets[0])\n elif non_hero:\n try:\n self.parse_new_details(root, mi, non_hero[0])\n except Exception:\n self.log.exception(\n 'Failed to parse new-style book details section')\n elif feature_and_detail_bullets:\n self.parse_detail_bullets(root, mi, feature_and_detail_bullets[0], ul_selector='ul')\n elif root.xpath('//*[@data-rpi-attribute-name=\"book_details-publication_date\"]'):\n # New Amazon \"rich_product_information\" carousel (RPI)\n node = root.xpath('//*[@data-rpi-attribute-name=\"book_details-publication_date\"]')[0]\n label = node.xpath('.//*[contains(@class,\"rpi-attribute-label\")]//span')\n value = node.xpath('.//*[contains(@class,\"rpi-attribute-value\")]//span')\n if label and value:\n self.parse_detail_cells(mi, label[0], value[0])\n else:\n pd = root.xpath(self.pd_xpath)\n if pd:\n pd = pd[0]\n\n try:\n isbn = self.parse_isbn(pd)\n if isbn:\n self.isbn = mi.isbn = isbn\n except Exception:\n self.log.exception(\n 'Error parsing ISBN for url: %r' % self.url)\n\n try:\n mi.publisher = self.parse_publisher(pd)\n except Exception:\n self.log.exception(\n 'Error parsing publisher for url: %r' % self.url)\n\n try:\n mi.pubdate = self.parse_pubdate(pd)\n except Exception:\n self.log.exception(\n 'Error parsing publish date for url: %r' % self.url)\n\n try:\n lang = self.parse_language(pd)\n if lang:\n mi.language = lang\n except Exception:\n self.log.exception(\n 'Error parsing language for url: %r' % self.url)\n\n else:\n self.log.warning(\n 'Failed to find product description for url: %r' % self.url)\n\n mi.source_relevance = self.relevance\n\n if self.amazon_id:\n if self.isbn:\n self.plugin.cache_isbn_to_identifier(self.isbn, self.amazon_id)\n if self.cover_url:\n self.plugin.cache_identifier_to_cover_url(self.amazon_id,\n self.cover_url)\n\n self.plugin.clean_downloaded_metadata(mi)\n\n if self.filter_result(mi, self.log):\n self.result_queue.put(mi)\n\n def totext(self, elem, only_printable=False):\n res = self.tostring(elem, encoding='unicode', method='text')\n if only_printable:\n try:\n filtered_characters = [s for s in res if s.isprintable()]\n except AttributeError:\n filtered_characters = [s for s in res if s in string.printable]\n res = ''.join(filtered_characters)\n return res.strip()\n\n def parse_title(self, root):\n\n def sanitize_title(title):\n ans = title.strip()\n if not ans.startswith('['):\n ans = re.sub(r'[(\\[].*[)\\]]', '', title).strip()\n return ans\n\n h1 = root.xpath('//h1[@id=\"title\"]')\n if h1:\n h1 = h1[0]\n for child in h1.xpath('./*[contains(@class, \"a-color-secondary\")]'):\n h1.remove(child)\n return sanitize_title(self.totext(h1))\n # audiobooks\n elem = root.xpath('//*[@id=\"productTitle\"]')\n if elem:\n return sanitize_title(self.totext(elem[0]))\n tdiv = root.xpath('//h1[contains(@class, \"parseasinTitle\")]')\n if not tdiv:\n span = root.xpath('//*[@id=\"ebooksTitle\"]')\n if span:\n return sanitize_title(self.totext(span[0]))\n h1 = root.xpath('//h1[@data-feature-name=\"title\"]')\n if h1:\n return sanitize_title(self.totext(h1[0]))\n raise ValueError('No title block found')\n tdiv = tdiv[0]\n actual_title = tdiv.xpath('descendant::*[@id=\"btAsinTitle\"]')\n if actual_title:\n title = self.tostring(actual_title[0], encoding='unicode',\n method='text').strip()\n else:\n title = self.tostring(tdiv, encoding='unicode',\n method='text').strip()\n return sanitize_title(title)\n\n def parse_authors(self, root):\n for sel in (\n '#byline .author .contributorNameID',\n '#byline .author a.a-link-normal',\n '#bylineInfo .author .contributorNameID',\n '#bylineInfo .author a.a-link-normal',\n '#bylineInfo #bylineContributor',\n '#bylineInfo #contributorLink',\n ):\n matches = tuple(self.selector(sel))\n if matches:\n authors = [self.totext(x) for x in matches]\n return [a for a in authors if a]\n\n x = '//h1[contains(@class, \"parseasinTitle\")]/following-sibling::span/*[(name()=\"a\" and @href) or (name()=\"span\" and @class=\"contributorNameTrigger\")]'\n aname = root.xpath(x)\n if not aname:\n aname = root.xpath('''\n //h1[contains(@class, \"parseasinTitle\")]/following-sibling::*[(name()=\"a\" and @href) or (name()=\"span\" and @class=\"contributorNameTrigger\")]\n ''')\n for x in aname:\n x.tail = ''\n authors = [self.tostring(x, encoding='unicode', method='text').strip() for x\n in aname]\n authors = [a for a in authors if a]\n return authors\n\n def parse_rating(self, root):\n for x in root.xpath('//div[@id=\"cpsims-feature\" or @id=\"purchase-sims-feature\" or @id=\"rhf\"]'):\n # Remove the similar books section as it can cause spurious\n # ratings matches\n x.getparent().remove(x)\n\n rating_paths = (\n '//div[@data-feature-name=\"averageCustomerReviews\" or @id=\"averageCustomerReviews\"]',\n '//div[@class=\"jumpBar\"]/descendant::span[contains(@class,\"asinReviewsSummary\")]',\n '//div[@class=\"buying\"]/descendant::span[contains(@class,\"asinReviewsSummary\")]',\n '//span[@class=\"crAvgStars\"]/descendant::span[contains(@class,\"asinReviewsSummary\")]'\n )\n ratings = None\n for p in rating_paths:\n ratings = root.xpath(p)\n if ratings:\n break\n\n def parse_ratings_text(text):\n try:\n m = self.ratings_pat.match(text)\n return float(m.group(1).replace(',', '.')) / float(m.group(3)) * 5\n except Exception:\n pass\n\n if ratings:\n ratings = ratings[0]\n for elem in ratings.xpath('descendant::*[@title]'):\n t = elem.get('title').strip()\n if self.domain == 'cn':\n m = self.ratings_pat_cn.match(t)\n if m is not None:\n return float(m.group(1))\n elif self.domain == 'jp':\n m = self.ratings_pat_jp.match(t)\n if m is not None:\n return float(m.group(1))\n else:\n ans = parse_ratings_text(t)\n if ans is not None:\n return ans\n for elem in ratings.xpath('descendant::span[@class=\"a-icon-alt\"]'):\n t = self.tostring(\n elem, encoding='unicode', method='text', with_tail=False).strip()\n ans = parse_ratings_text(t)\n if ans is not None:\n return ans\n else:\n # found in kindle book pages on amazon.com\n for x in root.xpath('//a[@id=\"acrCustomerReviewLink\"]'):\n spans = x.xpath('./span')\n if spans:\n txt = self.tostring(spans[0], method='text', encoding='unicode', with_tail=False).strip()\n try:\n return float(txt.replace(',', '.'))\n except Exception:\n pass\n\n def _render_comments(self, desc):\n from calibre.library.comments import sanitize_comments_html\n\n for c in desc.xpath('descendant::noscript'):\n c.getparent().remove(c)\n for c in desc.xpath('descendant::*[@class=\"seeAll\" or'\n ' @class=\"emptyClear\" or @id=\"collapsePS\" or'\n ' @id=\"expandPS\"]'):\n c.getparent().remove(c)\n for b in desc.xpath('descendant::b[@style]'):\n # Bing highlights search results\n s = b.get('style', '')\n if 'color' in s:\n b.tag = 'span'\n del b.attrib['style']\n\n for a in desc.xpath('descendant::a[@href]'):\n del a.attrib['href']\n a.tag = 'span'\n for a in desc.xpath('descendant::span[@class=\"a-text-italic\"]'):\n a.tag = 'i'\n for a in desc.xpath('descendant::span[@class=\"a-text-bold\"]'):\n a.tag = 'b'\n desc = self.tostring(desc, method='html', encoding='unicode').strip()\n desc = xml_replace_entities(desc, 'utf-8')\n\n # Encoding bug in Amazon data U+fffd (replacement char)\n # in some examples it is present in place of '\n desc = desc.replace('\\ufffd', \"'\")\n # remove all attributes from tags\n desc = re.sub(r'<([a-zA-Z0-9]+)\\s[^>]+>', r'<\\1>', desc)\n # Collapse whitespace\n # desc = re.sub(r'\\n+', '\\n', desc)\n # desc = re.sub(r' +', ' ', desc)\n # Remove the notice about text referring to out of print editions\n desc = re.sub(r'(?s)<em>--This text ref.*?</em>', '', desc)\n # Remove comments\n desc = re.sub(r'(?s)<!--.*?-->', '', desc)\n return sanitize_comments_html(desc)\n\n def parse_comments(self, root, raw):\n try:\n from urllib.parse import unquote\n except ImportError:\n from urllib import unquote\n ans = ''\n ovr = tuple(self.selector('#drengr_MobileTabbedDescriptionOverviewContent_feature_div')) or tuple(\n self.selector('#drengr_DesktopTabbedDescriptionOverviewContent_feature_div'))\n if ovr:\n ovr = ovr[0]\n ovr.tag = 'div'\n ans = self._render_comments(ovr)\n ovr = tuple(self.selector('#drengr_MobileTabbedDescriptionEditorialsContent_feature_div')) or tuple(\n self.selector('#drengr_DesktopTabbedDescriptionEditorialsContent_feature_div'))\n if ovr:\n ovr = ovr[0]\n ovr.tag = 'div'\n ans += self._render_comments(ovr)\n else:\n ns = tuple(self.selector('#bookDescription_feature_div noscript'))\n if ns:\n ns = ns[0]\n if len(ns) == 0 and ns.text:\n import html5lib\n\n # html5lib parsed noscript as CDATA\n ns = html5lib.parseFragment(\n '<div>%s</div>' % (ns.text), treebuilder='lxml', namespaceHTMLElements=False)[0]\n else:\n ns.tag = 'div'\n ans = self._render_comments(ns)\n else:\n desc = root.xpath('//div[@id=\"ps-content\"]/div[@class=\"content\"]')\n if desc:\n ans = self._render_comments(desc[0])\n else:\n ns = tuple(self.selector('#bookDescription_feature_div .a-expander-content'))\n if ns:\n ans = self._render_comments(ns[0])\n # audiobooks\n if not ans:\n elem = root.xpath('//*[@id=\"audible_desktopTabbedDescriptionOverviewContent_feature_div\"]')\n if elem:\n ans = self._render_comments(elem[0])\n desc = root.xpath(\n '//div[@id=\"productDescription\"]/*[@class=\"content\"]')\n if desc:\n ans += self._render_comments(desc[0])\n else:\n # Idiot chickens from amazon strike again. This data is now stored\n # in a JS variable inside a script tag URL encoded.\n m = re.search(br'var\\s+iframeContent\\s*=\\s*\"([^\"]+)\"', raw)\n if m is not None:\n try:\n text = unquote(m.group(1)).decode('utf-8')\n nr = parse_html(text)\n desc = nr.xpath(\n '//div[@id=\"productDescription\"]/*[@class=\"content\"]')\n if desc:\n ans += self._render_comments(desc[0])\n except Exception as e:\n self.log.warn(\n 'Parsing of obfuscated product description failed with error: %s' % as_unicode(e))\n else:\n desc = root.xpath('//div[@id=\"productDescription_fullView\"]')\n if desc:\n ans += self._render_comments(desc[0])\n\n return ans\n\n def parse_series(self, root):\n ans = (None, None)\n\n # This is found on kindle pages for books on amazon.* (including amazon.co.jp)\n series = root.xpath('//*[@id=\"rpi-attribute-book_details-series\"]')\n if series:\n spans = series[0].xpath('descendant::span')\n if spans:\n texts = [self.tostring(x, encoding='unicode', method='text', with_tail=False).strip()\n for x in spans]\n texts = list(filter(None, texts))\n if len(texts) == 2:\n idxinfo, series_name = texts\n idxinfo = idxinfo.strip()\n\n # Try Japanese pattern like: \"å…¨5å·»ä¸ç¬¬1å·»\", \"å…¨3冊ä¸ç¬¬2冊\"\n m = re.search(r'å…¨\\s*[0-9.]+\\s*(?:å·»|冊)ä¸ç¬¬\\s*([0-9.]+)\\s*(?:å·»|冊)', idxinfo)\n if m is not None:\n return (series_name, float(m.group(1)))\n\n # Newer JP pattern: \"å…¨24冊ä¸4ç•ªç›®ã®æœ¬\" (no 第 / no 冊 after index)\n m = re.search(r'å…¨\\s*[0-9.]+\\s*(?:å·»|冊)ä¸\\s*([0-9.]+)\\s*番目', idxinfo)\n if m is not None:\n return (series_name, float(m.group(1)))\n\n # Book X of Y\n m = re.search(r'([0-9.]+)\\s+of\\s+([0-9.]+)', idxinfo)\n if m is not None:\n return (series_name, float(m.group(1)))\n\n # Safer fallback: if there are multiple numbers, the FIRST one is usually the index\n nums = re.findall(r'[0-9.]+', idxinfo)\n if nums:\n ans = (series_name, float(nums[0]))\n return ans\n\n # This is found on the paperback/hardback pages for books on amazon.com\n series = root.xpath('//div[@data-feature-name=\"seriesTitle\"]')\n if series:\n series = series[0]\n spans = series.xpath('./span')\n if spans:\n raw = self.tostring(\n spans[0], encoding='unicode', method='text', with_tail=False).strip()\n m = re.search(r'\\s+([0-9.]+)$', raw.strip())\n if m is not None:\n series_index = float(m.group(1))\n s = series.xpath('./a[@id=\"series-page-link\"]')\n if s:\n series = self.tostring(\n s[0], encoding='unicode', method='text', with_tail=False).strip()\n if series:\n ans = (series, series_index)\n else:\n series = root.xpath('//div[@id=\"seriesBulletWidget_feature_div\"]')\n if series:\n a = series[0].xpath('descendant::a')\n if a:\n raw = self.tostring(a[0], encoding='unicode', method='text', with_tail=False)\n if self.domain == 'jp':\n m = re.search(r'å…¨\\s*[0-9.]+\\s*(?:å·»|冊)ä¸ç¬¬\\s*(?P<index>[0-9.]+)\\s*(?:å·»|冊)\\s*:\\s*(?P<series>.+)', raw.strip())\n else:\n m = re.search(r'(?:Book|Libro|Buch)\\s+(?P<index>[0-9.]+)\\s+(?:of|de|von)\\s+([0-9.]+)\\s*:\\s*(?P<series>.+)', raw.strip())\n if m is not None:\n ans = (m.group('series').strip(), float(m.group('index')))\n\n # This is found on Kindle edition pages on amazon.com\n if ans == (None, None):\n for span in root.xpath('//div[@id=\"aboutEbooksSection\"]//li/span'):\n text = (span.text or '').strip()\n m = re.match(r'Book\\s+([0-9.]+)', text)\n if m is not None:\n series_index = float(m.group(1))\n a = span.xpath('./a[@href]')\n if a:\n series = self.tostring(\n a[0], encoding='unicode', method='text', with_tail=False).strip()\n if series:\n ans = (series, series_index)\n # This is found on newer Kindle edition pages on amazon.com\n if ans == (None, None):\n for b in root.xpath('//div[@id=\"reviewFeatureGroup\"]/span/b'):\n text = (b.text or '').strip()\n m = re.match(r'Book\\s+([0-9.]+)', text)\n if m is not None:\n series_index = float(m.group(1))\n a = b.getparent().xpath('./a[@href]')\n if a:\n series = self.tostring(\n a[0], encoding='unicode', method='text', with_tail=False).partition('(')[0].strip()\n if series:\n ans = series, series_index\n\n if ans == (None, None):\n desc = root.xpath('//div[@id=\"ps-content\"]/div[@class=\"buying\"]')\n if desc:\n raw = self.tostring(desc[0], method='text', encoding='unicode')\n raw = re.sub(r'\\s+', ' ', raw)\n match = self.series_pat.search(raw)\n if match is not None:\n s, i = match.group('series'), float(match.group('index'))\n if s:\n ans = (s, i)\n if ans[0]:\n ans = (re.sub(r'\\s+Series$', '', ans[0]).strip(), ans[1])\n ans = (re.sub(r'\\(.+?\\s+Series\\)$', '', ans[0]).strip(), ans[1])\n return ans\n\n def parse_tags(self, root):\n ans = []\n exclude_tokens = {'kindle', 'a-z'}\n exclude = {'special features', 'by authors',\n 'authors & illustrators', 'books', 'new; used & rental textbooks'}\n seen = set()\n for li in root.xpath(self.tags_xpath):\n for i, a in enumerate(li.iterdescendants('a')):\n if i > 0:\n # we ignore the first category since it is almost always\n # too broad\n raw = (a.text or '').strip().replace(',', ';')\n lraw = icu_lower(raw)\n tokens = frozenset(lraw.split())\n if raw and lraw not in exclude and not tokens.intersection(exclude_tokens) and lraw not in seen:\n ans.append(raw)\n seen.add(lraw)\n return ans\n\n def parse_cover(self, root, raw=b''):\n # Look for the image URL in javascript, using the first image in the\n # image gallery as the cover\n import json\n imgpat = re.compile(r'\"hiRes\":\"(.+?)\",\"thumb\"')\n for script in root.xpath('//script'):\n m = imgpat.search(script.text or '')\n if m is not None:\n return m.group(1)\n imgpat = re.compile(r''''imageGalleryData'\\s*:\\s*(\\[\\s*{.+])''')\n for script in root.xpath('//script'):\n m = imgpat.search(script.text or '')\n if m is not None:\n try:\n return json.loads(m.group(1))[0]['mainUrl']\n except Exception:\n continue\n\n def clean_img_src(src):\n parts = src.split('/')\n if len(parts) > 3:\n bn = parts[-1]\n sparts = bn.split('_')\n if len(sparts) > 2:\n bn = re.sub(r'\\.\\.jpg$', '.jpg', (sparts[0] + sparts[-1]))\n return ('/'.join(parts[:-1])) + '/' + bn\n\n imgpat2 = re.compile(r'var imageSrc = \"([^\"]+)\"')\n for script in root.xpath('//script'):\n m = imgpat2.search(script.text or '')\n if m is not None:\n src = m.group(1)\n url = clean_img_src(src)\n if url:\n return url\n\n imgs = root.xpath(\n '//img[(@id=\"prodImage\" or @id=\"original-main-image\" or @id=\"main-image\" or @id=\"main-image-nonjs\") and @src]')\n if not imgs:\n imgs = (\n root.xpath('//div[@class=\"main-image-inner-wrapper\"]/img[@src]') or\n root.xpath('//div[@id=\"main-image-container\" or @id=\"ebooks-main-image-container\"]//img[@src]') or\n root.xpath(\n '//div[@id=\"mainImageContainer\"]//img[@data-a-dynamic-image]')\n )\n for img in imgs:\n try:\n idata = json.loads(img.get('data-a-dynamic-image'))\n except Exception:\n imgs = ()\n else:\n mwidth = 0\n try:\n url = None\n for iurl, (width, height) in idata.items():\n if width > mwidth:\n mwidth = width\n url = iurl\n\n return url\n except Exception:\n pass\n\n for img in imgs:\n src = img.get('src')\n if 'data:' in src:\n continue\n if 'loading-' in src:\n js_img = re.search(br'\"largeImage\":\"(https?://[^\"]+)\",', raw)\n if js_img:\n src = js_img.group(1).decode('utf-8')\n if ('/no-image-avail' not in src and 'loading-' not in src and '/no-img-sm' not in src):\n self.log('Found image: %s' % src)\n url = clean_img_src(src)\n if url:\n return url\n\n def parse_detail_bullets(self, root, mi, container, ul_selector='.detail-bullet-list'):\n try:\n ul = next(self.selector(ul_selector, root=container))\n except StopIteration:\n return\n for span in self.selector('.a-list-item', root=ul):\n cells = span.xpath('./span')\n if len(cells) >= 2:\n self.parse_detail_cells(mi, cells[0], cells[1])\n\n def parse_new_details(self, root, mi, non_hero):\n table = non_hero.xpath('descendant::table')[0]\n for tr in table.xpath('descendant::tr'):\n cells = tr.xpath('descendant::*[local-name()=\"td\" or local-name()=\"th\"]')\n if len(cells) == 2:\n self.parse_detail_cells(mi, cells[0], cells[1])\n\n def parse_detail_cells(self, mi, c1, c2):\n name = self.totext(c1, only_printable=True).strip().strip(':').strip()\n val = self.totext(c2)\n val = val.replace('\\u200e', '').replace('\\u200f', '')\n if not val:\n return\n if name in self.language_names:\n ans = self.lang_map.get(val)\n if not ans:\n ans = canonicalize_lang(val)\n if ans:\n mi.language = ans\n elif name in self.publisher_names:\n pub = val.partition(';')[0].partition('(')[0].strip()\n if pub:\n mi.publisher = pub\n if '(' in val:\n date = val.rpartition('(')[-1].replace(')', '').strip()\n try:\n from calibre.utils.date import parse_only_date\n date = self.delocalize_datestr(date)\n mi.pubdate = parse_only_date(date, assume_utc=True)\n except Exception:\n self.log.exception('Failed to parse pubdate: %s' % val)\n elif name in {'ISBN', 'ISBN-10', 'ISBN-13'}:\n ans = check_isbn(val)\n if ans:\n self.isbn = mi.isbn = ans\n elif name in {'Publication date', '発売日'}:\n from calibre.utils.date import parse_only_date\n date = self.delocalize_datestr(val)\n mi.pubdate = parse_only_date(date, assume_utc=True)\n\n def parse_isbn(self, pd):\n items = pd.xpath(\n 'descendant::*[starts-with(text(), \"ISBN\")]')\n if not items:\n items = pd.xpath(\n 'descendant::b[contains(text(), \"ISBN:\")]')\n for x in reversed(items):\n if x.tail:\n ans = check_isbn(x.tail.strip())\n if ans:\n return ans\n\n def parse_publisher(self, pd):\n for x in reversed(pd.xpath(self.publisher_xpath)):\n if x.tail:\n ans = x.tail.partition(';')[0]\n return ans.partition('(')[0].strip()\n\n def parse_pubdate(self, pd):\n from calibre.utils.date import parse_only_date\n for x in reversed(pd.xpath(self.pubdate_xpath)):\n if x.tail:\n date = x.tail.strip()\n date = self.delocalize_datestr(date)\n try:\n return parse_only_date(date, assume_utc=True)\n except Exception:\n pass\n for x in reversed(pd.xpath(self.publisher_xpath)):\n if x.tail:\n ans = x.tail\n date = ans.rpartition('(')[-1].replace(')', '').strip()\n date = self.delocalize_datestr(date)\n try:\n return parse_only_date(date, assume_utc=True)\n except Exception:\n pass\n\n def parse_language(self, pd):\n for x in reversed(pd.xpath(self.language_xpath)):\n if x.tail:\n raw = x.tail.strip().partition(',')[0].strip()\n ans = self.lang_map.get(raw, None)\n if ans:\n return ans\n ans = canonicalize_lang(ans)\n if ans:\n return ans\n# }}}\n\n\nclass Amazon(Source):\n\n name = 'Amazon.com'\n version = (1, 3, 18)\n minimum_calibre_version = (2, 82, 0)\n description = _('Downloads metadata and covers from Amazon')\n\n capabilities = frozenset(('identify', 'cover'))\n touched_fields = frozenset(('title', 'authors', 'identifier:amazon',\n 'rating', 'comments', 'publisher', 'pubdate',\n 'languages', 'series', 'tags'))\n has_html_comments = True\n supports_gzip_transfer_encoding = True\n prefer_results_with_isbn = False\n\n AMAZON_DOMAINS = {\n 'com': _('US'),\n 'fr': _('France'),\n 'de': _('Germany'),\n 'uk': _('UK'),\n 'au': _('Australia'),\n 'it': _('Italy'),\n 'jp': _('Japan'),\n 'es': _('Spain'),\n 'br': _('Brazil'),\n 'in': _('India'),\n 'nl': _('Netherlands'),\n 'cn': _('China'),\n 'ca': _('Canada'),\n 'se': _('Sweden'),\n }\n\n SERVERS = {\n 'auto': _('Choose server automatically'),\n 'amazon': _('Amazon servers'),\n 'bing': _('Bing search cache'),\n 'google': _('Google search cache'),\n 'wayback': _('Wayback machine cache (slow)'),\n 'ddg': _('DuckDuckGo search and Google cache'),\n }\n\n options = (\n Option('domain', 'choices', 'com', _('Amazon country website to use:'),\n _('Metadata from Amazon will be fetched using this '\n \"country's Amazon website.\"), choices=AMAZON_DOMAINS),\n Option('server', 'choices', 'auto', _('Server to get data from:'),\n _(\n 'Amazon has started blocking attempts to download'\n ' metadata from its servers. To get around this problem,'\n ' calibre can fetch the Amazon data from many different'\n ' places where it is cached. Choose the source you prefer.'\n ), choices=SERVERS),\n Option('use_mobi_asin', 'bool', False, _('Use the MOBI-ASIN for metadata search'),\n _(\n 'Enable this option to search for metadata with an'\n ' ASIN identifier from the MOBI file at the current country website,'\n ' unless any other amazon id is available. Note that if the'\n ' MOBI file came from a different Amazon country store, you could get'\n ' incorrect results.'\n )),\n Option('prefer_kindle_edition', 'bool', False, _('Prefer the Kindle edition, when available'),\n _(\n 'When searching for a book and the search engine returns both paper and Kindle editions,'\n ' always prefer the Kindle edition, instead of whatever the search engine returns at the'\n ' top.')\n ),\n )\n\n def __init__(self, *args, **kwargs):\n Source.__init__(self, *args, **kwargs)\n self.set_amazon_id_touched_fields()\n\n def id_from_url(self, url):\n from polyglot.urllib import urlparse\n purl = urlparse(url)\n if purl.netloc and purl.path and '/dp/' in purl.path:\n host_parts = tuple(x.lower() for x in purl.netloc.split('.'))\n if 'amazon' in host_parts:\n domain = host_parts[-1]\n parts = purl.path.split('/')\n idx = parts.index('dp')\n try:\n val = parts[idx+1]\n except IndexError:\n return\n aid = 'amazon' if domain == 'com' else ('amazon_' + domain)\n return aid, val\n\n def test_fields(self, mi):\n '''\n Return the first field from self.touched_fields that is null on the\n mi object\n '''\n for key in self.touched_fields:\n if key.startswith('identifier:'):\n key = key.partition(':')[-1]\n if key == 'amazon':\n if self.domain != 'com':\n key += '_' + self.domain\n if not mi.has_identifier(key):\n return 'identifier: ' + key\n elif mi.is_null(key):\n return key\n\n @property\n def browser(self):\n br = self._browser\n if br is None:\n ua = 'Mobile '\n while not user_agent_is_ok(ua):\n ua = random_user_agent(allow_ie=False)\n # ua = 'Mozilla/5.0 (Linux; Android 8.0.0; VTR-L29; rv:63.0) Gecko/20100101 Firefox/63.0'\n self._browser = br = browser(user_agent=ua)\n br.set_handle_gzip(True)\n if self.use_search_engine:\n br.addheaders += [\n ('Accept', accept_header_for_ua(ua)),\n ('Upgrade-insecure-requests', '1'),\n ]\n else:\n br.addheaders += [\n ('Accept', accept_header_for_ua(ua)),\n ('Upgrade-insecure-requests', '1'),\n ('Referer', self.referrer_for_domain()),\n ]\n return br\n\n def save_settings(self, *args, **kwargs):\n Source.save_settings(self, *args, **kwargs)\n self.set_amazon_id_touched_fields()\n\n def set_amazon_id_touched_fields(self):\n ident_name = 'identifier:amazon'\n if self.domain != 'com':\n ident_name += '_' + self.domain\n tf = [x for x in self.touched_fields if not\n x.startswith('identifier:amazon')] + [ident_name]\n self.touched_fields = frozenset(tf)\n\n def get_domain_and_asin(self, identifiers, extra_domains=()):\n identifiers = {k.lower(): v for k, v in identifiers.items()}\n for key, val in identifiers.items():\n if key in ('amazon', 'asin'):\n return 'com', val\n if key.startswith('amazon_'):\n domain = key.partition('_')[-1]\n if domain and (domain in self.AMAZON_DOMAINS or domain in extra_domains):\n return domain, val\n if self.prefs['use_mobi_asin']:\n val = identifiers.get('mobi-asin')\n if val is not None:\n return self.domain, val\n return None, None\n\n def referrer_for_domain(self, domain=None):\n domain = domain or self.domain\n return {\n 'uk': 'https://www.amazon.co.uk/',\n 'au': 'https://www.amazon.com.au/',\n 'br': 'https://www.amazon.com.br/',\n 'jp': 'https://www.amazon.co.jp/',\n 'mx': 'https://www.amazon.com.mx/',\n }.get(domain, 'https://www.amazon.%s/' % domain)\n\n def _get_book_url(self, identifiers): # {{{\n domain, asin = self.get_domain_and_asin(\n identifiers, extra_domains=('au', 'ca'))\n if domain and asin:\n url = None\n r = self.referrer_for_domain(domain)\n if r is not None:\n url = r + 'dp/' + asin\n if url:\n idtype = 'amazon' if domain == 'com' else 'amazon_' + domain\n return domain, idtype, asin, url\n\n def get_book_url(self, identifiers):\n ans = self._get_book_url(identifiers)\n if ans is not None:\n return ans[1:]\n\n def get_book_url_name(self, idtype, idval, url):\n if idtype == 'amazon':\n return self.name\n return 'A' + idtype.replace('_', '.')[1:]\n # }}}\n\n @property\n def domain(self):\n x = getattr(self, 'testing_domain', None)\n if x is not None:\n return x\n domain = self.prefs['domain']\n if domain not in self.AMAZON_DOMAINS:\n domain = 'com'\n\n return domain\n\n @property\n def server(self):\n x = getattr(self, 'testing_server', None)\n if x is not None:\n return x\n server = self.prefs['server']\n if server not in self.SERVERS:\n server = 'auto'\n return server\n\n @property\n def use_search_engine(self):\n return self.server != 'amazon'\n\n def clean_downloaded_metadata(self, mi):\n docase = (\n mi.language == 'eng' or\n (mi.is_null('language') and self.domain in {'com', 'uk', 'au'})\n )\n if mi.title and docase:\n # Remove series information from title\n m = re.search(r'\\S+\\s+(\\(.+?\\s+Book\\s+\\d+\\))$', mi.title)\n if m is not None:\n mi.title = mi.title.replace(m.group(1), '').strip()\n mi.title = fixcase(mi.title)\n mi.authors = fixauthors(mi.authors)\n if mi.tags and docase:\n mi.tags = list(map(fixcase, mi.tags))\n mi.isbn = check_isbn(mi.isbn)\n if mi.series and docase:\n mi.series = fixcase(mi.series)\n if mi.title and mi.series:\n for pat in (r':\\s*Book\\s+\\d+\\s+of\\s+%s$', r'\\(%s\\)$', r':\\s*%s\\s+Book\\s+\\d+$'):\n pat = pat % re.escape(mi.series)\n q = re.sub(pat, '', mi.title, flags=re.I).strip()\n if q and q != mi.title:\n mi.title = q\n break\n\n def get_website_domain(self, domain):\n return {'uk': 'co.uk', 'jp': 'co.jp', 'br': 'com.br', 'au': 'com.au'}.get(domain, domain)\n\n def create_query(self, log, title=None, authors=None, identifiers={}, # {{{\n domain=None, for_amazon=True):\n try:\n from urllib.parse import unquote_plus, urlencode\n except ImportError:\n from urllib import unquote_plus, urlencode\n if domain is None:\n domain = self.domain\n\n idomain, asin = self.get_domain_and_asin(identifiers)\n if idomain is not None:\n domain = idomain\n\n # See the amazon detailed search page to get all options\n terms = []\n q = {'search-alias': 'aps',\n 'unfiltered': '1',\n }\n\n if domain == 'com':\n q['sort'] = 'relevanceexprank'\n else:\n q['sort'] = 'relevancerank'\n\n isbn = check_isbn(identifiers.get('isbn', None))\n\n if asin is not None:\n q['field-keywords'] = asin\n terms.append(asin)\n elif isbn is not None:\n q['field-isbn'] = isbn\n if len(isbn) == 13:\n terms.extend('({} OR {}-{})'.format(isbn, isbn[:3], isbn[3:]).split())\n else:\n terms.append(isbn)\n else:\n # Only return book results\n q['search-alias'] = {'br': 'digital-text',\n 'nl': 'aps'}.get(domain, 'stripbooks')\n if title:\n title_tokens = list(self.get_title_tokens(title))\n if title_tokens:\n q['field-title'] = ' '.join(title_tokens)\n terms.extend(title_tokens)\n if authors:\n author_tokens = list(self.get_author_tokens(authors,\n only_first_author=True))\n if author_tokens:\n q['field-author'] = ' '.join(author_tokens)\n terms.extend(author_tokens)\n\n if not ('field-keywords' in q or 'field-isbn' in q or\n ('field-title' in q)):\n # Insufficient metadata to make an identify query\n log.error('Insufficient metadata to construct query, none of title, ISBN or ASIN supplied')\n raise SearchFailed()\n\n if not for_amazon:\n return terms, domain\n\n if domain == 'nl':\n q['__mk_nl_NL'] = 'Ã…MÅŽÕÑ'\n if 'field-keywords' not in q:\n q['field-keywords'] = ''\n for f in 'field-isbn field-title field-author'.split():\n q['field-keywords'] += ' ' + q.pop(f, '')\n q['field-keywords'] = q['field-keywords'].strip()\n\n encoded_q = {x.encode('utf-8', 'ignore'): y.encode('utf-8', 'ignore') for x, y in q.items()}\n url_query = urlencode(encoded_q)\n # amazon's servers want IRIs with unicode characters not percent esaped\n parts = []\n for x in url_query.split(b'&' if isinstance(url_query, bytes) else '&'):\n k, v = x.split(b'=' if isinstance(x, bytes) else '=', 1)\n parts.append('{}={}'.format(iri_quote_plus(unquote_plus(k)), iri_quote_plus(unquote_plus(v))))\n url_query = '&'.join(parts)\n url = 'https://www.amazon.%s/s/?' % self.get_website_domain(\n domain) + url_query\n return url, domain\n\n # }}}\n\n def get_cached_cover_url(self, identifiers): # {{{\n url = None\n domain, asin = self.get_domain_and_asin(identifiers)\n if asin is None:\n isbn = identifiers.get('isbn', None)\n if isbn is not None:\n asin = self.cached_isbn_to_identifier(isbn)\n if asin is not None:\n url = self.cached_identifier_to_cover_url(asin)\n\n return url\n # }}}\n\n def parse_results_page(self, root, domain): # {{{\n from lxml.html import tostring\n\n matches = []\n\n def title_ok(title):\n title = title.lower()\n bad = ['bulk pack', '[audiobook]', '[audio cd]',\n '(a book companion)', '( slipcase with door )', ': free sampler']\n if self.domain == 'com':\n bad.extend(['(%s edition)' % x for x in ('spanish', 'german')])\n for x in bad:\n if x in title:\n return False\n if title and title[0] in '[{' and re.search(r'\\(\\s*author\\s*\\)', title) is not None:\n # Bad entries in the catalog\n return False\n return True\n\n for query in (\n '//div[contains(@class, \"s-result-list\")]//h2/a[@href]',\n '//div[contains(@class, \"s-result-list\")]//div[@data-index]//h5//a[@href]',\n r'//li[starts-with(@id, \"result_\")]//a[@href and contains(@class, \"s-access-detail-page\")]',\n '//div[@data-cy=\"title-recipe\"]/a[@href]',\n ):\n result_links = root.xpath(query)\n if result_links:\n break\n for a in result_links:\n title = tostring(a, method='text', encoding='unicode')\n if title_ok(title):\n url = a.get('href')\n if url.startswith('/'):\n url = 'https://www.amazon.%s%s' % (\n self.get_website_domain(domain), url)\n matches.append(url)\n\n if not matches:\n # Previous generation of results page markup\n for div in root.xpath(r'//div[starts-with(@id, \"result_\")]'):\n links = div.xpath(r'descendant::a[@class=\"title\" and @href]')\n if not links:\n # New amazon markup\n links = div.xpath('descendant::h3/a[@href]')\n for a in links:\n title = tostring(a, method='text', encoding='unicode')\n if title_ok(title):\n url = a.get('href')\n if url.startswith('/'):\n url = 'https://www.amazon.%s%s' % (\n self.get_website_domain(domain), url)\n matches.append(url)\n break\n\n if not matches:\n # This can happen for some user agents that Amazon thinks are\n # mobile/less capable\n for td in root.xpath(\n r'//div[@id=\"Results\"]/descendant::td[starts-with(@id, \"search:Td:\")]'):\n for a in td.xpath(r'descendant::td[@class=\"dataColumn\"]/descendant::a[@href]/span[@class=\"srTitle\"]/..'):\n title = tostring(a, method='text', encoding='unicode')\n if title_ok(title):\n url = a.get('href')\n if url.startswith('/'):\n url = 'https://www.amazon.%s%s' % (\n self.get_website_domain(domain), url)\n matches.append(url)\n break\n if not matches and root.xpath('//form[@action=\"/errors/validateCaptcha\"]'):\n raise CaptchaError('Amazon returned a CAPTCHA page. Recently Amazon has begun using statistical'\n ' profiling to block access to its website. As such this metadata plugin is'\n ' unlikely to ever work reliably.')\n\n # Keep only the top 3 matches as the matches are sorted by relevance by\n # Amazon so lower matches are not likely to be very relevant\n return matches[:3]\n # }}}\n\n def search_amazon(self, br, testing, log, abort, title, authors, identifiers, timeout): # {{{\n from calibre.ebooks.chardet import xml_to_unicode\n from calibre.utils.cleantext import clean_ascii_chars\n matches = []\n query, domain = self.create_query(log, title=title, authors=authors,\n identifiers=identifiers)\n time.sleep(1)\n try:\n raw = br.open_novisit(query, timeout=timeout).read().strip()\n except Exception as e:\n if callable(getattr(e, 'getcode', None)) and \\\n e.getcode() == 404:\n log.error('Query malformed: %r' % query)\n raise SearchFailed()\n attr = getattr(e, 'args', [None])\n attr = attr or [None]\n if isinstance(attr[0], socket.timeout):\n msg = _('Amazon timed out. Try again later.')\n log.error(msg)\n else:\n msg = 'Failed to make identify query: %r' % query\n log.exception(msg)\n raise SearchFailed()\n\n raw = clean_ascii_chars(xml_to_unicode(raw,\n strip_encoding_pats=True, resolve_entities=True)[0])\n\n if testing:\n import tempfile\n with tempfile.NamedTemporaryFile(prefix='amazon_results_',\n suffix='.html', delete=False) as f:\n f.write(raw.encode('utf-8'))\n print('Downloaded html for results page saved in', f.name)\n\n matches = []\n found = '<title>404 - ' not in raw\n\n if found:\n try:\n root = parse_html(raw)\n except Exception:\n msg = 'Failed to parse amazon page for query: %r' % query\n log.exception(msg)\n raise SearchFailed()\n\n matches = self.parse_results_page(root, domain)\n\n return matches, query, domain, None\n # }}}\n\n def search_search_engine(self, br, testing, log, abort, title, authors, identifiers, timeout, override_server=None): # {{{\n from calibre.ebooks.metadata.sources.update import search_engines_module\n se = search_engines_module()\n terms, domain = self.create_query(log, title=title, authors=authors,\n identifiers=identifiers, for_amazon=False)\n site = self.referrer_for_domain(\n domain)[len('https://'):].partition('/')[0]\n matches = []\n server = override_server or self.server\n if server == 'bing':\n urlproc, sfunc = se.bing_url_processor, se.bing_search\n elif server == 'wayback':\n urlproc, sfunc = se.wayback_url_processor, se.ddg_search\n elif server == 'ddg':\n urlproc, sfunc = se.ddg_url_processor, se.ddg_search\n elif server == 'google':\n urlproc, sfunc = se.google_url_processor, se.google_search\n else: # auto or unknown\n urlproc, sfunc = se.google_url_processor, se.google_search\n # urlproc, sfunc = se.bing_url_processor, se.bing_search\n try:\n results, qurl = sfunc(terms, site, log=log, br=br, timeout=timeout)\n except HTTPError as err:\n if err.code == 429 and sfunc is se.google_search:\n log('Got too many requests error from Google, trying via DuckDuckGo')\n urlproc, sfunc = se.ddg_url_processor, se.ddg_search\n results, qurl = sfunc(terms, site, log=log, br=br, timeout=timeout)\n else:\n raise\n\n br.set_current_header('Referer', qurl)\n for result in results:\n if abort.is_set():\n return matches, terms, domain, None\n\n purl = urlparse(result.url)\n if '/dp/' in purl.path and site in purl.netloc:\n # We cannot use cached URL as wayback machine no longer caches\n # amazon and Google and Bing web caches are no longer\n # accessible.\n url = result.url\n if url not in matches:\n matches.append(url)\n if len(matches) >= 3:\n break\n else:\n log('Skipping non-book result:', result)\n if not matches:\n log('No search engine results for terms:', ' '.join(terms))\n if urlproc is se.google_url_processor:\n # Google does not cache adult titles\n log('Trying the bing search engine instead')\n return self.search_search_engine(br, testing, log, abort, title, authors, identifiers, timeout, 'bing')\n return matches, terms, domain, urlproc\n # }}}\n\n def identify(self, log, result_queue, abort, title=None, authors=None, # {{{\n identifiers={}, timeout=60):\n '''\n Note this method will retry without identifiers automatically if no\n match is found with identifiers.\n '''\n\n testing = getattr(self, 'running_a_test', False)\n\n udata = self._get_book_url(identifiers)\n br = self.browser\n log('User-agent:', br.current_user_agent())\n log('Server:', self.server)\n if testing:\n print('User-agent:', br.current_user_agent())\n if udata is not None and not self.use_search_engine:\n # Try to directly get details page instead of running a search\n # Cannot use search engine as the directly constructed URL is\n # usually redirected to a full URL by amazon, and is therefore\n # not cached\n domain, idtype, asin, durl = udata\n if durl is not None:\n preparsed_root = parse_details_page(\n durl, log, timeout, br, domain)\n if preparsed_root is not None:\n qasin = parse_asin(preparsed_root[1], log, durl)\n if qasin == asin:\n w = Worker(durl, result_queue, br, log, 0, domain,\n self, testing=testing, preparsed_root=preparsed_root, timeout=timeout)\n try:\n w.get_details()\n return\n except Exception:\n log.exception(\n 'get_details failed for url: %r' % durl)\n func = self.search_search_engine if self.use_search_engine else self.search_amazon\n try:\n matches, query, domain, cover_url_processor = func(\n br, testing, log, abort, title, authors, identifiers, timeout)\n except SearchFailed:\n return\n\n if abort.is_set():\n return\n\n if not matches:\n if identifiers and title and authors:\n log('No matches found with identifiers, retrying using only'\n ' title and authors. Query: %r' % query)\n time.sleep(1)\n return self.identify(log, result_queue, abort, title=title,\n authors=authors, timeout=timeout)\n log.error('No matches found with query: %r' % query)\n return\n\n if self.prefs['prefer_kindle_edition']:\n matches = sort_matches_preferring_kindle_editions(matches)\n\n workers = [Worker(\n url, result_queue, br, log, i, domain, self, testing=testing, timeout=timeout,\n cover_url_processor=cover_url_processor, filter_result=partial(\n self.filter_result, title, authors, identifiers)) for i, url in enumerate(matches)]\n\n for w in workers:\n # Don't send all requests at the same time\n time.sleep(1)\n w.start()\n if abort.is_set():\n return\n\n while not abort.is_set():\n a_worker_is_alive = False\n for w in workers:\n w.join(0.2)\n if abort.is_set():\n break\n if w.is_alive():\n a_worker_is_alive = True\n if not a_worker_is_alive:\n break\n\n return None\n # }}}\n\n def filter_result(self, title, authors, identifiers, mi, log): # {{{\n if not self.use_search_engine:\n return True\n if title is not None:\n import regex\n only_punctuation_pat = regex.compile(r'^\\p{P}+$')\n\n def tokenize_title(x):\n ans = icu_lower(x).replace(\"'\", '').replace('\"', '').rstrip(':')\n if only_punctuation_pat.match(ans) is not None:\n ans = ''\n return ans\n\n tokens = {tokenize_title(x) for x in title.split() if len(x) > 3}\n tokens.discard('')\n if tokens:\n result_tokens = {tokenize_title(x) for x in mi.title.split()}\n result_tokens.discard('')\n if not tokens.intersection(result_tokens):\n log('Ignoring result:', mi.title, 'as its title does not match')\n return False\n if authors:\n author_tokens = set()\n for author in authors:\n author_tokens |= {icu_lower(x) for x in author.split() if len(x) > 2}\n result_tokens = set()\n for author in mi.authors:\n result_tokens |= {icu_lower(x) for x in author.split() if len(x) > 2}\n if author_tokens and not author_tokens.intersection(result_tokens):\n log('Ignoring result:', mi.title, 'by', ' & '.join(mi.authors), 'as its author does not match')\n return False\n return True\n # }}}\n\n def download_cover(self, log, result_queue, abort, # {{{\n title=None, authors=None, identifiers={}, timeout=60, get_best_cover=False):\n cached_url = self.get_cached_cover_url(identifiers)\n if cached_url is None:\n log.info('No cached cover found, running identify')\n rq = Queue()\n self.identify(log, rq, abort, title=title, authors=authors,\n identifiers=identifiers)\n if abort.is_set():\n return\n results = []\n while True:\n try:\n results.append(rq.get_nowait())\n except Empty:\n break\n results.sort(key=self.identify_results_keygen(\n title=title, authors=authors, identifiers=identifiers))\n for mi in results:\n cached_url = self.get_cached_cover_url(mi.identifiers)\n if cached_url is not None:\n break\n if cached_url is None:\n log.info('No cover found')\n return\n\n if abort.is_set():\n return\n log('Downloading cover from:', cached_url)\n br = self.browser\n if self.use_search_engine:\n br = br.clone_browser()\n br.set_current_header('Referer', self.referrer_for_domain(self.domain))\n try:\n time.sleep(1)\n cdata = br.open_novisit(\n cached_url, timeout=timeout).read()\n result_queue.put((self, cdata))\n except Exception:\n log.exception('Failed to download cover from:', cached_url)\n # }}}\n\n\ndef manual_tests(domain, **kw): # {{{\n # To run these test use:\n # calibre-debug -c \"from calibre.ebooks.metadata.sources.amazon import *; manual_tests('com')\"\n from calibre.ebooks.metadata.sources.test import authors_test, comments_test, isbn_test, series_test, test_identify_plugin, title_test\n all_tests = {}\n all_tests['com'] = [ # {{{\n ( # Paperback with series\n {'identifiers': {'amazon': 'B082D76DHS'}},\n [title_test('Lost Coast: A Post-Apocalyptic Zombie Thriller', exact=False), series_test('Undead Ultra', 3)]\n ),\n\n ( # in title\n {'title': 'Expert C# 2008 Business Objects',\n 'authors': ['Lhotka']},\n [title_test('Expert C#'),\n authors_test(['Rockford Lhotka'])\n ]\n ),\n\n ( # Paperback with series\n {'identifiers': {'amazon': '1423146786'}},\n [title_test('Heroes of Olympus', exact=False), series_test('The Heroes of Olympus', 5)]\n ),\n\n ( # Kindle edition with series\n {'identifiers': {'amazon': 'B0085UEQDO'}},\n [title_test('Three Parts Dead', exact=True),\n series_test('Craft Sequence', 1)]\n ),\n\n ( # + in title and uses id=\"main-image\" for cover\n {'identifiers': {'amazon': '1933988770'}},\n [title_test(\n 'C++ Concurrency in Action: Practical Multithreading', exact=True)]\n ),\n\n\n ( # Different comments markup, using Book Description section\n {'identifiers': {'amazon': '0982514506'}},\n [title_test(\n \"Griffin's Destiny\",\n exact=True),\n comments_test('Jelena'), comments_test('Ashinji'),\n ]\n ),\n\n ( # New search results page markup (Dec 2024)\n {'title': 'Come si scrive un articolo medico-scientifico'},\n [title_test('Come si scrive un articolo medico-scientifico', exact=True)]\n ),\n\n ( # No specific problems\n {'identifiers': {'isbn': '0743273567'}},\n [title_test('the great gatsby'),\n authors_test(['f. Scott Fitzgerald'])]\n ),\n\n ]\n\n # }}}\n\n all_tests['de'] = [ # {{{\n # series\n (\n {'identifiers': {'isbn': '3499275120'}},\n [title_test('Vespasian: Das Schwert des Tribuns: Historischer Roman',\n exact=False), authors_test(['Robert Fabbri']), series_test('Die Vespasian-Reihe', 1)\n ]\n\n ),\n\n ( # umlaut in title/authors\n {'title': 'Flüsternde Wälder',\n 'authors': ['Nicola Förg']},\n [title_test('Flüsternde Wälder'),\n authors_test(['Nicola Förg'], subset=True)\n ]\n ),\n\n (\n {'identifiers': {'isbn': '9783453314979'}},\n [title_test('Die letzten Wächter: Roman',\n exact=False), authors_test(['Sergej Lukianenko'])\n ]\n\n ),\n\n (\n {'identifiers': {'isbn': '3548283519'}},\n [title_test('Wer Wind Sät: Der Fünfte Fall Für Bodenstein Und Kirchhoff',\n exact=False), authors_test(['Nele Neuhaus'])\n ]\n\n ),\n ] # }}}\n\n all_tests['it'] = [ # {{{\n (\n {'identifiers': {'isbn': '8838922195'}},\n [title_test('La briscola in cinque',\n exact=True), authors_test(['Marco Malvaldi'])\n ]\n\n ),\n ] # }}}\n\n all_tests['fr'] = [ # {{{\n (\n {'identifiers': {'amazon_fr': 'B07L7ST4RS'}},\n [title_test('Le secret de Lola', exact=True),\n authors_test(['Amélie BRIZIO'])\n ]\n ),\n (\n {'identifiers': {'isbn': '2221116798'}},\n [title_test(\"L'étrange voyage de Monsieur Daldry\",\n exact=True), authors_test(['Marc Levy'])\n ]\n\n ),\n ] # }}}\n\n all_tests['es'] = [ # {{{\n (\n {'identifiers': {'isbn': '8483460831'}},\n [title_test('Tiempos Interesantes',\n exact=False), authors_test(['Terry Pratchett'])\n ]\n\n ),\n ] # }}}\n\n all_tests['se'] = [ # {{{\n (\n {'identifiers': {'isbn': '9780552140287'}},\n [title_test('Men At Arms: A Discworld Novel: 14',\n exact=False), authors_test(['Terry Pratchett'])\n ]\n\n ),\n ] # }}}\n\n all_tests['jp'] = [ # {{{\n ( # Adult filtering test\n {'identifiers': {'isbn': '4799500066'}},\n [title_test('Bitch ï¼´ï½’ï½ï½'), ]\n ),\n\n ( # isbn -> title, authors\n {'identifiers': {'isbn': '9784101302720'}},\n [title_test('精霊ã®å®ˆã‚Šäºº',\n exact=True), authors_test(['上橋 èœç©‚å'])\n ]\n ),\n ( # title, authors -> isbn (will use Shift_JIS encoding in query.)\n {'title': '考ãˆãªã„ç·´ç¿’',\n 'authors': ['å°æ± é¾ä¹‹ä»‹']},\n [isbn_test('9784093881067'), ]\n ),\n ] # }}}\n\n all_tests['br'] = [ # {{{\n (\n {'title': 'A Ascensão da Sombra'},\n [title_test('A Ascensão da Sombra'), authors_test(['Robert Jordan'])]\n ),\n\n (\n {'title': 'Guerra dos Tronos'},\n [title_test('A Guerra dos Tronos. As Crônicas de Gelo e Fogo - Livro 1'), authors_test(['George R. R. Martin'])\n ]\n\n ),\n ] # }}}\n\n all_tests['nl'] = [ # {{{\n (\n {'title': 'Freakonomics'},\n [title_test('Freakonomics',\n exact=True), authors_test(['Steven Levitt & Stephen Dubner & R. Kuitenbrouwer & O. Brenninkmeijer & A. van Den Berg'])\n ]\n\n ),\n ] # }}}\n\n all_tests['cn'] = [ # {{{\n (\n {'identifiers': {'isbn': '9787115369512'}},\n [title_test('若为自由故 è‡ªç”±è½¯ä»¶ä¹‹çˆ¶ç†æŸ¥å¾·æ–¯æ‰˜æ›¼ä¼ ', exact=True),\n authors_test(['[美]sam Williams', 'é‚“æ¥ ï¼ŒæŽå‡¡å¸Œ'])]\n ),\n (\n {'title': '爱上Raspberry Pi'},\n [title_test('爱上Raspberry Pi',\n exact=True), authors_test(['Matt Richardson', 'Shawn Wallace', 'æŽå‡¡å¸Œ'])\n ]\n\n ),\n ] # }}}\n\n all_tests['ca'] = [ # {{{\n ( # Paperback with series\n {'identifiers': {'isbn': '9781623808747'}},\n [title_test('Parting Shot', exact=True),\n authors_test(['Mary Calmes'])]\n ),\n ( # in title\n {'title': 'Expert C# 2008 Business Objects',\n 'authors': ['Lhotka']},\n [title_test('Expert C# 2008 Business Objects'),\n authors_test(['Rockford Lhotka'])]\n ),\n ( # noscript description\n {'identifiers': {'amazon_ca': '162380874X'}},\n [title_test('Parting Shot', exact=True), authors_test(['Mary Calmes'])\n ]\n ),\n ] # }}}\n\n all_tests['in'] = [ # {{{\n ( # Paperback with series\n {'identifiers': {'amazon_in': '1423146786'}},\n [title_test('The Heroes of Olympus, Book Five The Blood of Olympus', exact=True)]\n ),\n ] # }}}\n\n def do_test(domain, start=0, stop=None, server='auto'):\n tests = all_tests[domain]\n if stop is None:\n stop = len(tests)\n tests = tests[start:stop]\n test_identify_plugin(Amazon.name, tests, modify_plugin=lambda p: (\n setattr(p, 'testing_domain', domain),\n setattr(p, 'touched_fields', p.touched_fields - {'tags'}),\n setattr(p, 'testing_server', server),\n ))\n\n do_test(domain, **kw)\n# }}}\n", - "edelweiss": "#!/usr/bin/env python\n# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\n__license__ = 'GPL v3'\n__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'\n__docformat__ = 'restructuredtext en'\n\nimport re\nimport time\nfrom threading import Thread\n\ntry:\n from queue import Empty, Queue\nexcept ImportError:\n from Queue import Empty, Queue\n\nfrom calibre import as_unicode, random_user_agent\nfrom calibre.ebooks.metadata import check_isbn\nfrom calibre.ebooks.metadata.sources.base import Source\n\n\ndef clean_html(raw):\n from calibre.ebooks.chardet import xml_to_unicode\n from calibre.utils.cleantext import clean_ascii_chars\n return clean_ascii_chars(xml_to_unicode(raw, strip_encoding_pats=True,\n resolve_entities=True, assume_utf8=True)[0])\n\n\ndef parse_html(raw):\n raw = clean_html(raw)\n from html5_parser import parse\n return parse(raw)\n\n\ndef astext(node):\n from lxml import etree\n return etree.tostring(node, method='text', encoding='unicode',\n with_tail=False).strip()\n\n\nclass Worker(Thread): # {{{\n\n def __init__(self, basic_data, relevance, result_queue, br, timeout, log, plugin):\n Thread.__init__(self)\n self.daemon = True\n self.basic_data = basic_data\n self.br, self.log, self.timeout = br, log, timeout\n self.result_queue, self.plugin, self.sku = result_queue, plugin, self.basic_data['sku']\n self.relevance = relevance\n\n def run(self):\n url = ('https://www.edelweiss.plus/GetTreelineControl.aspx?controlName=/uc/product/two_Enhanced.ascx&'\n 'sku={0}&idPrefix=content_1_{0}&mode=0'.format(self.sku))\n try:\n raw = self.br.open_novisit(url, timeout=self.timeout).read()\n except Exception:\n self.log.exception('Failed to load comments page: %r'%url)\n return\n\n try:\n mi = self.parse(raw)\n mi.source_relevance = self.relevance\n self.plugin.clean_downloaded_metadata(mi)\n self.result_queue.put(mi)\n except Exception:\n self.log.exception('Failed to parse details for sku: %s'%self.sku)\n\n def parse(self, raw):\n from calibre.ebooks.metadata.book.base import Metadata\n from calibre.utils.date import UNDEFINED_DATE\n root = parse_html(raw)\n mi = Metadata(self.basic_data['title'], self.basic_data['authors'])\n\n # Identifiers\n if self.basic_data['isbns']:\n mi.isbn = self.basic_data['isbns'][0]\n mi.set_identifier('edelweiss', self.sku)\n\n # Tags\n if self.basic_data['tags']:\n mi.tags = self.basic_data['tags']\n mi.tags = [t[1:].strip() if t.startswith('&') else t for t in mi.tags]\n\n # Publisher\n mi.publisher = self.basic_data['publisher']\n\n # Pubdate\n if self.basic_data['pubdate'] and self.basic_data['pubdate'].year != UNDEFINED_DATE:\n mi.pubdate = self.basic_data['pubdate']\n\n # Rating\n if self.basic_data['rating']:\n mi.rating = self.basic_data['rating']\n\n # Comments\n comments = ''\n for cid in ('summary', 'contributorbio', 'quotes_reviews'):\n cid = 'desc_{}{}-content'.format(cid, self.sku)\n div = root.xpath('//*[@id=\"{}\"]'.format(cid))\n if div:\n comments += self.render_comments(div[0])\n if comments:\n mi.comments = comments\n\n mi.has_cover = self.plugin.cached_identifier_to_cover_url(self.sku) is not None\n return mi\n\n def render_comments(self, desc):\n from lxml import etree\n\n from calibre.library.comments import sanitize_comments_html\n for c in desc.xpath('descendant::noscript'):\n c.getparent().remove(c)\n for a in desc.xpath('descendant::a[@href]'):\n del a.attrib['href']\n a.tag = 'span'\n desc = etree.tostring(desc, method='html', encoding='unicode').strip()\n\n # remove all attributes from tags\n desc = re.sub(r'<([a-zA-Z0-9]+)\\s[^>]+>', r'<\\1>', desc)\n # Collapse whitespace\n # desc = re.sub(r'\\n+', '\\n', desc)\n # desc = re.sub(r' +', ' ', desc)\n # Remove comments\n desc = re.sub(r'(?s)<!--.*?-->', '', desc)\n return sanitize_comments_html(desc)\n# }}}\n\n\ndef get_basic_data(browser, log, *skus):\n from mechanize import Request\n\n from calibre.utils.date import parse_only_date\n zeroes = ','.join('0' for sku in skus)\n data = {\n 'skus': ','.join(skus),\n 'drc': zeroes,\n 'startPosition': '0',\n 'sequence': '1',\n 'selected': zeroes,\n 'itemID': '0',\n 'orderID': '0',\n 'mailingID': '',\n 'tContentWidth': '926',\n 'originalOrder': ','.join(type('')(i) for i in range(len(skus))),\n 'selectedOrderID': '0',\n 'selectedSortColumn': '0',\n 'listType': '1',\n 'resultType': '32',\n 'blockView': '1',\n }\n items_data_url = 'https://www.edelweiss.plus/GetTreelineControl.aspx?controlName=/uc/listviews/ListView_Title_Multi.ascx'\n req = Request(items_data_url, data)\n response = browser.open_novisit(req)\n raw = response.read()\n root = parse_html(raw)\n for item in root.xpath('//div[@data-priority]'):\n row = item.getparent().getparent()\n sku = item.get('id').split('-')[-1]\n isbns = [x.strip() for x in row.xpath('descendant::*[contains(@class, \"pev_sku\")]/text()')[0].split(',') if check_isbn(x.strip())]\n isbns.sort(key=len, reverse=True)\n try:\n tags = [x.strip() for x in astext(row.xpath('descendant::*[contains(@class, \"pev_categories\")]')[0]).split('/')]\n except IndexError:\n tags = []\n rating = 0\n for bar in row.xpath('descendant::*[contains(@class, \"bgdColorCommunity\")]/@style'):\n m = re.search(r'width: (\\d+)px;.*max-width: (\\d+)px', bar)\n if m is not None:\n rating = float(m.group(1)) / float(m.group(2))\n break\n try:\n pubdate = parse_only_date(astext(row.xpath('descendant::*[contains(@class, \"pev_shipDate\")]')[0]\n ).split(':')[-1].split(u'\\xa0')[-1].strip(), assume_utc=True)\n except Exception:\n log.exception('Error parsing published date')\n pubdate = None\n authors = []\n for x in [x.strip() for x in row.xpath('descendant::*[contains(@class, \"pev_contributor\")]/@title')]:\n authors.extend(a.strip() for a in x.split(','))\n entry = {\n 'sku': sku,\n 'cover': row.xpath('descendant::img/@src')[0].split('?')[0],\n 'publisher': astext(row.xpath('descendant::*[contains(@class, \"headerPublisher\")]')[0]),\n 'title': astext(row.xpath('descendant::*[@id=\"title_{}\"]'.format(sku))[0]),\n 'authors': authors,\n 'isbns': isbns,\n 'tags': tags,\n 'pubdate': pubdate,\n 'format': ' '.join(row.xpath('descendant::*[contains(@class, \"pev_format\")]/text()')).strip(),\n 'rating': rating,\n }\n if entry['cover'].startswith('/'):\n entry['cover'] = None\n yield entry\n\n\nclass Edelweiss(Source):\n\n name = 'Edelweiss'\n version = (2, 0, 1)\n minimum_calibre_version = (3, 6, 0)\n description = _('Downloads metadata and covers from Edelweiss - A catalog updated by book publishers')\n\n capabilities = frozenset(['identify', 'cover'])\n touched_fields = frozenset([\n 'title', 'authors', 'tags', 'pubdate', 'comments', 'publisher',\n 'identifier:isbn', 'identifier:edelweiss', 'rating'])\n supports_gzip_transfer_encoding = True\n has_html_comments = True\n\n @property\n def user_agent(self):\n # Pass in an index to random_user_agent() to test with a particular\n # user agent\n return random_user_agent(allow_ie=False)\n\n def _get_book_url(self, sku):\n if sku:\n return 'https://www.edelweiss.plus/#sku={}&page=1'.format(sku)\n\n def get_book_url(self, identifiers): # {{{\n sku = identifiers.get('edelweiss', None)\n if sku:\n return 'edelweiss', sku, self._get_book_url(sku)\n\n # }}}\n\n def get_cached_cover_url(self, identifiers): # {{{\n sku = identifiers.get('edelweiss', None)\n if not sku:\n isbn = identifiers.get('isbn', None)\n if isbn is not None:\n sku = self.cached_isbn_to_identifier(isbn)\n return self.cached_identifier_to_cover_url(sku)\n # }}}\n\n def create_query(self, log, title=None, authors=None, identifiers={}):\n try:\n from urllib.parse import urlencode\n except ImportError:\n from urllib import urlencode\n import time\n BASE_URL = ('https://www.edelweiss.plus/GetTreelineControl.aspx?'\n 'controlName=/uc/listviews/controls/ListView_data.ascx&itemID=0&resultType=32&dashboardType=8&itemType=1&dataType=products&keywordSearch&')\n keywords = []\n isbn = check_isbn(identifiers.get('isbn', None))\n if isbn is not None:\n keywords.append(isbn)\n elif title:\n title_tokens = list(self.get_title_tokens(title))\n if title_tokens:\n keywords.extend(title_tokens)\n author_tokens = self.get_author_tokens(authors, only_first_author=True)\n if author_tokens:\n keywords.extend(author_tokens)\n if not keywords:\n return None\n params = {\n 'q': (' '.join(keywords)).encode('utf-8'),\n '_': type('')(int(time.time()))\n }\n return BASE_URL+urlencode(params)\n\n # }}}\n\n def identify(self, log, result_queue, abort, title=None, authors=None, # {{{\n identifiers={}, timeout=30):\n import json\n\n br = self.browser\n br.addheaders = [\n ('Referer', 'https://www.edelweiss.plus/'),\n ('X-Requested-With', 'XMLHttpRequest'),\n ('Cache-Control', 'no-cache'),\n ('Pragma', 'no-cache'),\n ]\n if 'edelweiss' in identifiers:\n items = [identifiers['edelweiss']]\n else:\n log.error('Currently Edelweiss returns random books for search queries')\n return\n query = self.create_query(log, title=title, authors=authors,\n identifiers=identifiers)\n if not query:\n log.error('Insufficient metadata to construct query')\n return\n log('Using query URL:', query)\n try:\n raw = br.open(query, timeout=timeout).read().decode('utf-8')\n except Exception as e:\n log.exception('Failed to make identify query: %r'%query)\n return as_unicode(e)\n items = re.search(r'window[.]items\\s*=\\s*(.+?);', raw)\n if items is None:\n log.error('Failed to get list of matching items')\n log.debug('Response text:')\n log.debug(raw)\n return\n items = json.loads(items.group(1))\n\n if (not items and identifiers and title and authors and\n not abort.is_set()):\n return self.identify(log, result_queue, abort, title=title,\n authors=authors, timeout=timeout)\n\n if not items:\n return\n\n workers = []\n items = items[:5]\n for i, item in enumerate(get_basic_data(self.browser, log, *items)):\n sku = item['sku']\n for isbn in item['isbns']:\n self.cache_isbn_to_identifier(isbn, sku)\n if item['cover']:\n self.cache_identifier_to_cover_url(sku, item['cover'])\n fmt = item['format'].lower()\n if 'audio' in fmt or 'mp3' in fmt:\n continue # Audio-book, ignore\n workers.append(Worker(item, i, result_queue, br.clone_browser(), timeout, log, self))\n\n if not workers:\n return\n\n for w in workers:\n w.start()\n # Don't send all requests at the same time\n time.sleep(0.1)\n\n while not abort.is_set():\n a_worker_is_alive = False\n for w in workers:\n w.join(0.2)\n if abort.is_set():\n break\n if w.is_alive():\n a_worker_is_alive = True\n if not a_worker_is_alive:\n break\n\n # }}}\n\n def download_cover(self, log, result_queue, abort, # {{{\n title=None, authors=None, identifiers={}, timeout=30, get_best_cover=False):\n cached_url = self.get_cached_cover_url(identifiers)\n if cached_url is None:\n log.info('No cached cover found, running identify')\n rq = Queue()\n self.identify(log, rq, abort, title=title, authors=authors,\n identifiers=identifiers)\n if abort.is_set():\n return\n results = []\n while True:\n try:\n results.append(rq.get_nowait())\n except Empty:\n break\n results.sort(key=self.identify_results_keygen(\n title=title, authors=authors, identifiers=identifiers))\n for mi in results:\n cached_url = self.get_cached_cover_url(mi.identifiers)\n if cached_url is not None:\n break\n if cached_url is None:\n log.info('No cover found')\n return\n\n if abort.is_set():\n return\n br = self.browser\n log('Downloading cover from:', cached_url)\n try:\n cdata = br.open_novisit(cached_url, timeout=timeout).read()\n result_queue.put((self, cdata))\n except Exception:\n log.exception('Failed to download cover from:', cached_url)\n # }}}\n\n\nif __name__ == '__main__':\n from calibre.ebooks.metadata.sources.test import authors_test, comments_test, pubdate_test, test_identify_plugin, title_test\n tests = [\n ( # A title and author search\n {'title': \"The Husband's Secret\", 'authors':['Liane Moriarty']},\n [title_test(\"The Husband's Secret\", exact=True),\n authors_test(['Liane Moriarty'])]\n ),\n\n ( # An isbn present in edelweiss\n {'identifiers':{'isbn': '9780312621360'}, },\n [title_test('Flame: A Sky Chasers Novel', exact=True),\n authors_test(['Amy Kathleen Ryan'])]\n ),\n\n # Multiple authors and two part title and no general description\n ({'identifiers':{'edelweiss':'0321180607'}},\n [title_test('XQuery From the Experts: A Guide to the W3C XML Query Language', exact=True),\n authors_test([\n 'Howard Katz', 'Don Chamberlin', 'Denise Draper', 'Mary Fernandez',\n 'Michael Kay', 'Jonathan Robie', 'Michael Rys', 'Jerome Simeon',\n 'Jim Tivy', 'Philip Wadler']),\n pubdate_test(2003, 8, 22),\n comments_test('Jérôme Siméon'), lambda mi: bool(mi.comments and 'No title summary' not in mi.comments)\n ]),\n ]\n start, stop = 0, len(tests)\n\n tests = tests[start:stop]\n test_identify_plugin(Edelweiss.name, tests)\n", - "google": "#!/usr/bin/env python\n# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai\n# License: GPLv3 Copyright: 2011, Kovid Goyal <kovid at kovidgoyal.net>\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport hashlib\nimport os\nimport re\nimport sys\nimport tempfile\nimport time\n\nimport regex\n\ntry:\n from queue import Empty, Queue\nexcept ImportError:\n from Queue import Empty, Queue\n\nfrom calibre import as_unicode, prepare_string_for_xml, replace_entities\nfrom calibre.ebooks.chardet import xml_to_unicode\nfrom calibre.ebooks.metadata import authors_to_string, check_isbn\nfrom calibre.ebooks.metadata.book.base import Metadata\nfrom calibre.ebooks.metadata.sources.base import Source\nfrom calibre.utils.cleantext import clean_ascii_chars\nfrom calibre.utils.localization import canonicalize_lang\n\nNAMESPACES = {\n 'openSearch': 'http://a9.com/-/spec/opensearchrss/1.0/',\n 'atom': 'http://www.w3.org/2005/Atom',\n 'dc': 'http://purl.org/dc/terms',\n 'gd': 'http://schemas.google.com/g/2005'\n}\n\n\ndef pretty_google_books_comments(raw):\n raw = replace_entities(raw)\n # Paragraphs in the comments are removed but whatever software googl uses\n # to do this does not insert a space so we often find the pattern\n # word.Capital in the comments which can be used to find paragraph markers.\n parts = []\n for x in re.split(r'([a-z)\"â€])(\\.)([A-Z(\"“])', raw):\n if x == '.':\n parts.append('.</p>\\n\\n<p>')\n else:\n parts.append(prepare_string_for_xml(x))\n raw = '<p>' + ''.join(parts) + '</p>'\n return raw\n\n\ndef get_details(browser, url, timeout): # {{{\n try:\n raw = browser.open_novisit(url, timeout=timeout).read()\n except Exception as e:\n gc = getattr(e, 'getcode', lambda: -1)\n if gc() != 403:\n raise\n # Google is throttling us, wait a little\n time.sleep(2)\n raw = browser.open_novisit(url, timeout=timeout).read()\n\n return raw\n# }}}\n\n\nxpath_cache = {}\n\n\ndef XPath(x):\n ans = xpath_cache.get(x)\n if ans is None:\n from lxml import etree\n ans = xpath_cache[x] = etree.XPath(x, namespaces=NAMESPACES)\n return ans\n\n\ndef to_metadata(browser, log, entry_, timeout, running_a_test=False): # {{{\n from calibre.utils.xml_parse import safe_xml_fromstring\n # total_results = XPath('//openSearch:totalResults')\n # start_index = XPath('//openSearch:startIndex')\n # items_per_page = XPath('//openSearch:itemsPerPage')\n entry = XPath('//atom:entry')\n entry_id = XPath('descendant::atom:id')\n url = XPath('descendant::atom:link[@rel=\"self\"]/@href')\n creator = XPath('descendant::dc:creator')\n identifier = XPath('descendant::dc:identifier')\n title = XPath('descendant::dc:title')\n date = XPath('descendant::dc:date')\n publisher = XPath('descendant::dc:publisher')\n subject = XPath('descendant::dc:subject')\n description = XPath('descendant::dc:description')\n language = XPath('descendant::dc:language')\n\n # print(etree.tostring(entry_, pretty_print=True))\n\n def get_text(extra, x):\n try:\n ans = x(extra)\n if ans:\n ans = ans[0].text\n if ans and ans.strip():\n return ans.strip()\n except Exception:\n log.exception('Programming error:')\n return None\n\n def get_extra_details():\n raw = get_details(browser, details_url, timeout)\n if running_a_test:\n with open(os.path.join(tempfile.gettempdir(), 'Google-' + details_url.split('/')[-1] + '.xml'), 'wb') as f:\n f.write(raw)\n print('Book details saved to:', f.name, file=sys.stderr)\n feed = safe_xml_fromstring(xml_to_unicode(clean_ascii_chars(raw), strip_encoding_pats=True)[0])\n return entry(feed)[0]\n\n if isinstance(entry_, str):\n google_id = entry_\n details_url = 'https://www.google.com/books/feeds/volumes/' + google_id\n extra = get_extra_details()\n title_ = ': '.join([x.text for x in title(extra)]).strip()\n authors = [x.text.strip() for x in creator(extra) if x.text]\n else:\n id_url = entry_id(entry_)[0].text\n google_id = id_url.split('/')[-1]\n details_url = url(entry_)[0]\n title_ = ': '.join([x.text for x in title(entry_)]).strip()\n authors = [x.text.strip() for x in creator(entry_) if x.text]\n if not id_url or not title:\n # Silently discard this entry\n return None\n extra = None\n\n if not authors:\n authors = [_('Unknown')]\n if not title:\n return None\n if extra is None:\n extra = get_extra_details()\n mi = Metadata(title_, authors)\n mi.identifiers = {'google': google_id}\n mi.comments = get_text(extra, description)\n lang = canonicalize_lang(get_text(extra, language))\n if lang:\n mi.language = lang\n mi.publisher = get_text(extra, publisher)\n\n # ISBN\n isbns = []\n for x in identifier(extra):\n t = type('')(x.text).strip()\n if t[:5].upper() in ('ISBN:', 'LCCN:', 'OCLC:'):\n if t[:5].upper() == 'ISBN:':\n t = check_isbn(t[5:])\n if t:\n isbns.append(t)\n if isbns:\n mi.isbn = sorted(isbns, key=len)[-1]\n mi.all_isbns = isbns\n\n # Tags\n try:\n btags = [x.text for x in subject(extra) if x.text]\n tags = []\n for t in btags:\n atags = [y.strip() for y in t.split('/')]\n for tag in atags:\n if tag not in tags:\n tags.append(tag)\n except Exception:\n log.exception('Failed to parse tags:')\n tags = []\n if tags:\n mi.tags = [x.replace(',', ';') for x in tags]\n\n # pubdate\n pubdate = get_text(extra, date)\n if pubdate:\n from calibre.utils.date import parse_date, utcnow\n try:\n default = utcnow().replace(day=15)\n mi.pubdate = parse_date(pubdate, assume_utc=True, default=default)\n except Exception:\n log.error('Failed to parse pubdate %r' % pubdate)\n\n # Cover\n mi.has_google_cover = None\n for x in extra.xpath(\n '//*[@href and @rel=\"http://schemas.google.com/books/2008/thumbnail\"]'\n ):\n mi.has_google_cover = x.get('href')\n break\n\n return mi\n\n# }}}\n\n\nclass GoogleBooks(Source):\n\n name = 'Google'\n version = (1, 1, 3)\n minimum_calibre_version = (2, 80, 0)\n description = _('Downloads metadata and covers from Google Books')\n\n capabilities = frozenset({'identify'})\n touched_fields = frozenset({\n 'title', 'authors', 'tags', 'pubdate', 'comments', 'publisher',\n 'identifier:isbn', 'identifier:google', 'languages'\n })\n supports_gzip_transfer_encoding = True\n cached_cover_url_is_reliable = False\n\n GOOGLE_COVER = 'https://books.google.com/books?id=%s&printsec=frontcover&img=1'\n\n DUMMY_IMAGE_MD5 = frozenset(\n ('0de4383ebad0adad5eeb8975cd796657', 'a64fa89d7ebc97075c1d363fc5fea71f')\n )\n\n def get_book_url(self, identifiers): # {{{\n goog = identifiers.get('google', None)\n if goog is not None:\n return ('google', goog, 'https://books.google.com/books?id=%s' % goog)\n # }}}\n\n def id_from_url(self, url): # {{{\n from polyglot.urllib import parse_qs, urlparse\n purl = urlparse(url)\n if purl.netloc == 'books.google.com':\n q = parse_qs(purl.query)\n gid = q.get('id')\n if gid:\n return 'google', gid[0]\n # }}}\n\n def create_query(self, title=None, authors=None, identifiers={}, capitalize_isbn=False): # {{{\n try:\n from urllib.parse import urlencode\n except ImportError:\n from urllib import urlencode\n BASE_URL = 'https://books.google.com/books/feeds/volumes?'\n isbn = check_isbn(identifiers.get('isbn', None))\n q = ''\n if isbn is not None:\n q += ('ISBN:' if capitalize_isbn else 'isbn:') + isbn\n elif title or authors:\n\n def build_term(prefix, parts):\n return ' '.join('in' + prefix + ':' + x for x in parts)\n\n title_tokens = list(self.get_title_tokens(title))\n if title_tokens:\n q += build_term('title', title_tokens)\n author_tokens = list(self.get_author_tokens(authors, only_first_author=True))\n if author_tokens:\n q += ('+' if q else '') + build_term('author', author_tokens)\n\n if not q:\n return None\n if not isinstance(q, bytes):\n q = q.encode('utf-8')\n return BASE_URL + urlencode({\n 'q': q,\n 'max-results': 20,\n 'start-index': 1,\n 'min-viewability': 'none',\n })\n\n # }}}\n\n def download_cover( # {{{\n self,\n log,\n result_queue,\n abort,\n title=None,\n authors=None,\n identifiers={},\n timeout=30,\n get_best_cover=False\n ):\n cached_url = self.get_cached_cover_url(identifiers)\n if cached_url is None:\n log.info('No cached cover found, running identify')\n rq = Queue()\n self.identify(\n log,\n rq,\n abort,\n title=title,\n authors=authors,\n identifiers=identifiers\n )\n if abort.is_set():\n return\n results = []\n while True:\n try:\n results.append(rq.get_nowait())\n except Empty:\n break\n results.sort(\n key=self.identify_results_keygen(\n title=title, authors=authors, identifiers=identifiers\n )\n )\n for mi in results:\n cached_url = self.get_cached_cover_url(mi.identifiers)\n if cached_url is not None:\n break\n if cached_url is None:\n log.info('No cover found')\n return\n\n br = self.browser\n for candidate in (0, 1):\n if abort.is_set():\n return\n url = cached_url + '&zoom={}'.format(candidate)\n log('Downloading cover from:', cached_url)\n try:\n cdata = br.open_novisit(url, timeout=timeout).read()\n if cdata:\n if hashlib.md5(cdata).hexdigest() in self.DUMMY_IMAGE_MD5:\n log.warning('Google returned a dummy image, ignoring')\n else:\n result_queue.put((self, cdata))\n break\n except Exception:\n log.exception('Failed to download cover from:', cached_url)\n\n # }}}\n\n def get_cached_cover_url(self, identifiers): # {{{\n url = None\n goog = identifiers.get('google', None)\n if goog is None:\n isbn = identifiers.get('isbn', None)\n if isbn is not None:\n goog = self.cached_isbn_to_identifier(isbn)\n if goog is not None:\n url = self.cached_identifier_to_cover_url(goog)\n\n return url\n\n # }}}\n\n def postprocess_downloaded_google_metadata(self, ans, relevance=0): # {{{\n if not isinstance(ans, Metadata):\n return ans\n ans.source_relevance = relevance\n goog = ans.identifiers['google']\n for isbn in getattr(ans, 'all_isbns', []):\n self.cache_isbn_to_identifier(isbn, goog)\n if getattr(ans, 'has_google_cover', False):\n self.cache_identifier_to_cover_url(goog, self.GOOGLE_COVER % goog)\n if ans.comments:\n ans.comments = pretty_google_books_comments(ans.comments)\n self.clean_downloaded_metadata(ans)\n return ans\n # }}}\n\n def get_all_details( # {{{\n self,\n br,\n log,\n entries,\n abort,\n result_queue,\n timeout\n ):\n from lxml import etree\n for relevance, i in enumerate(entries):\n try:\n ans = self.postprocess_downloaded_google_metadata(to_metadata(br, log, i, timeout, self.running_a_test), relevance)\n if isinstance(ans, Metadata):\n result_queue.put(ans)\n except Exception:\n log.exception(\n 'Failed to get metadata for identify entry:', etree.tostring(i)\n )\n if abort.is_set():\n break\n\n # }}}\n\n def identify_via_web_search( # {{{\n self,\n log,\n result_queue,\n abort,\n title=None,\n authors=None,\n identifiers={},\n timeout=30\n ):\n from calibre.utils.filenames import ascii_text\n from polyglot.urllib import urlparse\n isbn = check_isbn(identifiers.get('isbn', None))\n q = []\n strip_punc_pat = regex.compile(r'[\\p{C}|\\p{M}|\\p{P}|\\p{S}|\\p{Z}]+', regex.UNICODE)\n google_ids = []\n check_tokens = set()\n has_google_id = 'google' in identifiers\n\n def to_check_tokens(*tokens):\n for t in tokens:\n if len(t) < 3:\n continue\n t = t.lower()\n if t in ('and', 'not', 'the'):\n continue\n yield ascii_text(strip_punc_pat.sub('', t))\n\n if has_google_id:\n google_ids.append(identifiers['google'])\n elif isbn is not None:\n q.append(isbn)\n elif title or authors:\n title_tokens = list(self.get_title_tokens(title))\n if title_tokens:\n q += title_tokens\n check_tokens |= set(to_check_tokens(*title_tokens))\n author_tokens = list(self.get_author_tokens(authors, only_first_author=True))\n if author_tokens:\n q += author_tokens\n check_tokens |= set(to_check_tokens(*author_tokens))\n if not q and not google_ids:\n return None\n from calibre.ebooks.metadata.sources.update import search_engines_module\n se = search_engines_module()\n br = se.google_specialize_browser(se.browser())\n if not has_google_id:\n url = se.google_format_query(q, site='books.google.com')\n log('Making query:', url)\n r = []\n root = se.query(br, url, 'google', timeout=timeout, save_raw=r.append)\n pat = re.compile(r'id=([^&]+)')\n for q in se.google_parse_results(root, r[0], log=log, ignore_uncached=False):\n m = pat.search(q.url)\n if m is None or not q.url:\n continue\n try:\n purl = urlparse(q.url)\n except Exception:\n continue\n if not purl.hostname.startswith('books.google'):\n continue\n google_ids.append(m.group(1))\n\n if not google_ids and isbn and (title or authors):\n return self.identify_via_web_search(log, result_queue, abort, title, authors, {}, timeout)\n found = False\n seen = set()\n for relevance, gid in enumerate(google_ids):\n if gid in seen:\n continue\n seen.add(gid)\n try:\n ans = to_metadata(br, log, gid, timeout, self.running_a_test)\n if isinstance(ans, Metadata):\n if isbn:\n if isbn not in ans.all_isbns:\n log('Excluding', ans.title, 'by', authors_to_string(ans.authors), 'as it does not match the ISBN:', isbn,\n 'not in', ' '.join(ans.all_isbns))\n continue\n elif check_tokens:\n candidate = set(to_check_tokens(*self.get_title_tokens(ans.title)))\n candidate |= set(to_check_tokens(*self.get_author_tokens(ans.authors)))\n if candidate.intersection(check_tokens) != check_tokens:\n log('Excluding', ans.title, 'by', authors_to_string(ans.authors), 'as it does not match the query')\n continue\n ans = self.postprocess_downloaded_google_metadata(ans, relevance)\n result_queue.put(ans)\n found = True\n except Exception:\n log.exception('Failed to get metadata for google books id:', gid)\n if abort.is_set():\n break\n if not found and isbn and (title or authors):\n return self.identify_via_web_search(log, result_queue, abort, title, authors, {}, timeout)\n # }}}\n\n def identify( # {{{\n self,\n log,\n result_queue,\n abort,\n title=None,\n authors=None,\n identifiers={},\n timeout=30\n ):\n from calibre.utils.xml_parse import safe_xml_fromstring\n entry = XPath('//atom:entry')\n identifiers = identifiers.copy()\n br = self.browser\n if 'google' in identifiers:\n try:\n ans = to_metadata(br, log, identifiers['google'], timeout, self.running_a_test)\n if isinstance(ans, Metadata):\n self.postprocess_downloaded_google_metadata(ans)\n result_queue.put(ans)\n return\n except Exception:\n log.exception('Failed to get metadata for Google identifier:', identifiers['google'])\n del identifiers['google']\n\n query = self.create_query(\n title=title, authors=authors, identifiers=identifiers\n )\n if not query:\n log.error('Insufficient metadata to construct query')\n return\n\n def make_query(query):\n log('Making query:', query)\n try:\n raw = br.open_novisit(query, timeout=timeout).read()\n except Exception as e:\n log.exception('Failed to make identify query: %r' % query)\n return False, as_unicode(e)\n\n try:\n feed = safe_xml_fromstring(xml_to_unicode(clean_ascii_chars(raw), strip_encoding_pats=True)[0])\n return True, entry(feed)\n except Exception as e:\n log.exception('Failed to parse identify results')\n return False, as_unicode(e)\n ok, entries = make_query(query)\n if not ok:\n return entries\n if not entries and not abort.is_set():\n log('No results found, doing a web search instead')\n return self.identify_via_web_search(log, result_queue, abort, title, authors, identifiers, timeout)\n\n # There is no point running these queries in threads as google\n # throttles requests returning 403 Forbidden errors\n self.get_all_details(br, log, entries, abort, result_queue, timeout)\n\n # }}}\n\n\nif __name__ == '__main__': # tests {{{\n # To run these test use:\n # calibre-debug src/calibre/ebooks/metadata/sources/google.py\n from calibre.ebooks.metadata.sources.test import authors_test, test_identify_plugin, title_test\n tests = [\n ({\n 'identifiers': {'google': 's7NIrgEACAAJ'},\n }, [title_test('Ride Every Stride', exact=False)]),\n\n ({\n 'identifiers': {'isbn': '0743273567'},\n 'title': 'Great Gatsby',\n 'authors': ['Fitzgerald']\n }, [\n title_test('The great gatsby', exact=True),\n authors_test(['F. Scott Fitzgerald'])\n ]),\n\n ({\n 'title': 'Flatland',\n 'authors': ['Abbott']\n }, [title_test('Flatland', exact=False)]),\n\n ({\n 'title': 'The Blood Red Indian Summer: A Berger and Mitry Mystery',\n 'authors': ['David Handler'],\n }, [title_test('The Blood Red Indian Summer: A Berger and Mitry Mystery')\n ]),\n\n ({\n # requires using web search to find the book, but web search is broken currently\n 'title': 'Dragon Done It',\n 'authors': ['Eric Flint'],\n }, [\n title_test('The dragon done it', exact=True),\n authors_test(['Eric Flint', 'Mike Resnick'])\n ]),\n\n ]\n test_identify_plugin(GoogleBooks.name, tests)\n\n# }}}\n", - "google_images": "#!/usr/bin/env python\n# vim:fileencoding=UTF-8\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\n__license__ = 'GPL v3'\n__copyright__ = '2013, Kovid Goyal <kovid@kovidgoyal.net>'\n__docformat__ = 'restructuredtext en'\n\nfrom collections import OrderedDict\n\nfrom calibre import random_user_agent\nfrom calibre.ebooks.metadata.sources.base import Option, Source\n\n\ndef parse_html(raw):\n try:\n from html5_parser import parse\n except ImportError:\n # Old versions of calibre\n import html5lib\n return html5lib.parse(raw, treebuilder='lxml', namespaceHTMLElements=False)\n else:\n return parse(raw)\n\n\ndef imgurl_from_id(raw, tbnid):\n from json import JSONDecoder\n q = '\"{}\",['.format(tbnid)\n start_pos = raw.index(q)\n if start_pos < 100:\n return\n jd = JSONDecoder()\n data = jd.raw_decode('[' + raw[start_pos:])[0]\n # from pprint import pprint\n # pprint(data)\n url_num = 0\n for x in data:\n if isinstance(x, list) and len(x) == 3:\n q = x[0]\n if hasattr(q, 'lower') and q.lower().startswith('http'):\n url_num += 1\n if url_num > 1:\n return q\n\n\ndef parse_google_markup(raw):\n root = parse_html(raw)\n # newer markup pages use data-docid not data-tbnid\n results = root.xpath('//div/@data-tbnid') or root.xpath('//div/@data-docid')\n ans = OrderedDict()\n for tbnid in results:\n try:\n imgurl = imgurl_from_id(raw, tbnid)\n except Exception:\n continue\n if imgurl:\n ans[imgurl] = True\n return list(ans)\n\n\nclass GoogleImages(Source):\n\n name = 'Google Images'\n version = (1, 0, 6)\n minimum_calibre_version = (2, 80, 0)\n description = _('Downloads covers from a Google Image search. Useful to find larger/alternate covers.')\n capabilities = frozenset(['cover'])\n can_get_multiple_covers = True\n supports_gzip_transfer_encoding = True\n options = (Option('max_covers', 'number', 5, _('Maximum number of covers to get'),\n _('The maximum number of covers to process from the Google search result')),\n Option('size', 'choices', 'svga', _('Cover size'),\n _('Search for covers larger than the specified size'),\n choices=OrderedDict((\n ('any', _('Any size'),),\n ('l', _('Large'),),\n ('qsvga', _('Larger than %s')%'400x300',),\n ('vga', _('Larger than %s')%'640x480',),\n ('svga', _('Larger than %s')%'600x800',),\n ('xga', _('Larger than %s')%'1024x768',),\n ('2mp', _('Larger than %s')%'2 MP',),\n ('4mp', _('Larger than %s')%'4 MP',),\n ))),\n )\n\n def download_cover(self, log, result_queue, abort,\n title=None, authors=None, identifiers={}, timeout=30, get_best_cover=False):\n if not title:\n return\n timeout = max(60, timeout) # Needs at least a minute\n title = ' '.join(self.get_title_tokens(title))\n author = ' '.join(self.get_author_tokens(authors))\n urls = self.get_image_urls(title, author, log, abort, timeout)\n self.download_multiple_covers(title, authors, urls, get_best_cover, timeout, result_queue, abort, log)\n\n @property\n def user_agent(self):\n return random_user_agent(allow_ie=False)\n\n def get_image_urls(self, title, author, log, abort, timeout):\n from calibre.utils.cleantext import clean_ascii_chars\n try:\n from urllib.parse import urlencode\n except ImportError:\n from urllib import urlencode\n br = self.browser\n q = urlencode({'as_q': ('%s %s'%(title, author)).encode('utf-8')})\n if isinstance(q, bytes):\n q = q.decode('utf-8')\n sz = self.prefs['size']\n if sz == 'any':\n sz = ''\n elif sz == 'l':\n sz = 'isz:l,'\n else:\n sz = 'isz:lt,islt:%s,' % sz\n # See https://www.google.com/advanced_image_search to understand this\n # URL scheme\n url = 'https://www.google.com/search?as_st=y&tbm=isch&{}&as_epq=&as_oq=&as_eq=&cr=&as_sitesearch=&safe=images&tbs={}iar:t,ift:jpg'.format(q, sz)\n log('Search URL: ' + url)\n # See https://github.com/benbusby/whoogle-search/pull/1054 for cookies\n br.set_simple_cookie('CONSENT', 'PENDING+987', '.google.com', path='/')\n template = b'\\x08\\x01\\x128\\x08\\x14\\x12+boq_identityfrontenduiserver_20231107.05_p0\\x1a\\x05en-US \\x03\\x1a\\x06\\x08\\x80\\xf1\\xca\\xaa\\x06'\n from base64 import standard_b64encode\n from datetime import date\n template.replace(b'20231107', date.today().strftime('%Y%m%d').encode('ascii'))\n br.set_simple_cookie('SOCS', standard_b64encode(template).decode('ascii').rstrip('='), '.google.com', path='/')\n # br.set_debug_http(True)\n raw = clean_ascii_chars(br.open(url).read().decode('utf-8'))\n # with open('/t/raw.html', 'w') as f:\n # f.write(raw)\n return parse_google_markup(raw)\n\n\ndef test_raw():\n import sys\n raw = open(sys.argv[-1]).read()\n for x in parse_google_markup(raw):\n print(x)\n\n\ndef test(title='Star Trek: Section 31: Control', authors=('David Mack',)):\n try:\n from queue import Queue\n except ImportError:\n from Queue import Queue\n from threading import Event\n\n from calibre.utils.logging import default_log\n p = GoogleImages(None)\n p.log = default_log\n rq = Queue()\n p.download_cover(default_log, rq, Event(), title=title, authors=authors)\n print('Downloaded', rq.qsize(), 'covers')\n\n\nif __name__ == '__main__':\n test()\n", - "hashes": { - "amazon": "f71f4a6da6ef1fd98d76f0135f5360fb019b76ef", - "edelweiss": "640a39d0926dfdaa72f54160a1db5323b4d7c164", - "google": "e71e2242c2dababa10f73d9c2aee9b2864d4f80a", - "google_images": "4244dd8267cb6215c7dfd2da166c6e02b1db31ea", - "openlibrary": "239077a692701cbf0281e7a2e64306cd00217410", - "search_engines": "65c9081d21ecf31abf31ceb5f0d87870fcd943db" - }, - "openlibrary": "#!/usr/bin/env python\n# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\n__license__ = 'GPL v3'\n__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'\n__docformat__ = 'restructuredtext en'\n\nfrom calibre.ebooks.metadata.sources.base import Source\n\n\nclass OpenLibrary(Source):\n\n name = 'Open Library'\n version = (1, 0, 2)\n minimum_calibre_version = (2, 80, 0)\n description = _('Downloads covers from The Open Library')\n\n capabilities = frozenset(['cover'])\n\n OPENLIBRARY = 'https://covers.openlibrary.org/b/isbn/%s-L.jpg?default=false'\n\n def download_cover(self, log, result_queue, abort,\n title=None, authors=None, identifiers={}, timeout=30, get_best_cover=False):\n if 'isbn' not in identifiers:\n return\n isbn = identifiers['isbn']\n br = self.browser\n try:\n ans = br.open_novisit(self.OPENLIBRARY%isbn, timeout=timeout).read()\n result_queue.put((self, ans))\n except Exception as e:\n if callable(getattr(e, 'getcode', None)) and e.getcode() == 404:\n log.error('No cover for ISBN: %r found'%isbn)\n else:\n log.exception('Failed to download cover for ISBN:', isbn)\n", - "search_engines": "#!/usr/bin/env python\n# vim:fileencoding=utf-8\n# License: GPLv3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>\n\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport json\nimport os\nimport re\nimport sys\nimport time\nfrom collections import namedtuple\nfrom contextlib import contextmanager\nfrom functools import partial\nfrom threading import Lock\n\ntry:\n from urllib.parse import parse_qs, quote, quote_plus, urlencode, urlparse\nexcept ImportError:\n from urllib import quote, quote_plus, urlencode\n\n from urlparse import parse_qs, urlparse\n\nfrom lxml import etree\n\nfrom calibre import browser as _browser\nfrom calibre import prints as safe_print\nfrom calibre import random_user_agent\nfrom calibre.constants import cache_dir\nfrom calibre.ebooks.chardet import xml_to_unicode\nfrom calibre.utils.lock import ExclusiveFile\nfrom calibre.utils.random_ua import accept_header_for_ua\n\ncurrent_version = (1, 2, 15)\nminimum_calibre_version = (2, 80, 0)\nwebcache = {}\nwebcache_lock = Lock()\nprints = partial(safe_print, file=sys.stderr)\n\n\nResult = namedtuple('Result', 'url title cached_url')\n\n\n@contextmanager\ndef rate_limit(name='test', time_between_visits=2, max_wait_seconds=5 * 60, sleep_time=0.2):\n lock_file = os.path.join(cache_dir(), 'search-engine.' + name + '.lock')\n with ExclusiveFile(lock_file, timeout=max_wait_seconds, sleep_time=sleep_time) as f:\n try:\n lv = float(f.read().decode('utf-8').strip())\n except Exception:\n lv = 0\n # we cannot use monotonic() as this is cross process and historical\n # data as well\n delta = time.time() - lv\n if delta < time_between_visits:\n time.sleep(time_between_visits - delta)\n try:\n yield\n finally:\n f.seek(0)\n f.truncate()\n f.write(repr(time.time()).encode('utf-8'))\n\n\ndef tostring(elem):\n return etree.tostring(elem, encoding='unicode', method='text', with_tail=False)\n\n\ndef browser():\n ua = random_user_agent(allow_ie=False)\n # ua = 'Mozilla/5.0 (Linux; Android 8.0.0; VTR-L29; rv:63.0) Gecko/20100101 Firefox/63.0'\n br = _browser(user_agent=ua)\n br.set_handle_gzip(True)\n br.addheaders += [\n ('Accept', accept_header_for_ua(ua)),\n ('Upgrade-insecure-requests', '1'),\n ]\n return br\n\n\ndef encode_query(**query):\n q = {k.encode('utf-8'): v.encode('utf-8') for k, v in query.items()}\n return urlencode(q).decode('utf-8')\n\n\ndef parse_html(raw):\n try:\n from html5_parser import parse\n except ImportError:\n # Old versions of calibre\n import html5lib\n return html5lib.parse(raw, treebuilder='lxml', namespaceHTMLElements=False)\n else:\n return parse(raw)\n\n\ndef query(br, url, key, dump_raw=None, limit=1, parser=parse_html, timeout=60, save_raw=None, simple_scraper=None):\n with rate_limit(key):\n if simple_scraper is None:\n raw = br.open_novisit(url, timeout=timeout).read()\n raw = xml_to_unicode(raw, strip_encoding_pats=True)[0]\n else:\n raw = simple_scraper(url, timeout=timeout)\n if dump_raw is not None:\n with open(dump_raw, 'w') as f:\n f.write(raw)\n if save_raw is not None:\n save_raw(raw)\n return parser(raw)\n\n\ndef quote_term(x):\n ans = quote_plus(x.encode('utf-8'))\n if isinstance(ans, bytes):\n ans = ans.decode('utf-8')\n return ans\n\n\n# DDG + Wayback machine DDG does a captcha after 2-3 requests {{{\n\ndef ddg_url_processor(url):\n return url\n\n\ndef ddg_term(t):\n t = t.replace('\"', '')\n if t.lower() in {'map', 'news'}:\n t = '\"' + t + '\"'\n if t in {'OR', 'AND', 'NOT'}:\n t = t.lower()\n return t\n\n\ndef ddg_href(url):\n if url.startswith('/'):\n q = url.partition('?')[2]\n url = parse_qs(q.encode('utf-8'))[b'uddg'][0].decode('utf-8')\n return url\n\n\ndef wayback_machine_cached_url(url, br=None, log=prints, timeout=60):\n q = quote_term(url)\n br = br or browser()\n try:\n data = query(br, 'https://archive.org/wayback/available?url=' +\n q, 'wayback', parser=json.loads, limit=0.25, timeout=timeout)\n except Exception as e:\n log('Wayback machine query failed for url: ' + url + ' with error: ' + str(e))\n return None\n try:\n closest = data['archived_snapshots']['closest']\n if closest['available']:\n ans = closest['url'].replace('http:', 'https:', 1)\n # get unmodified HTML\n ans = ans.replace(closest['timestamp'], closest['timestamp'] + 'id_', 1)\n return ans\n except Exception:\n pass\n from pprint import pformat\n log('Response from wayback machine:', pformat(data))\n\n\ndef wayback_url_processor(url):\n if url.startswith('/'):\n # Use original URL instead of absolutizing to wayback URL as wayback is\n # slow\n m = re.search(r'https?:', url)\n if m is None:\n url = 'https://web.archive.org' + url\n else:\n url = url[m.start():]\n return url\n\n\nddg_scraper_storage = []\n\n\ndef ddg_search(terms, site=None, br=None, log=prints, safe_search=False, dump_raw=None, timeout=60):\n # https://duck.co/help/results/syntax\n terms = [quote_term(ddg_term(t)) for t in terms]\n if site is not None:\n terms.append(quote_term(('site:' + site)))\n q = '+'.join(terms)\n url = 'https://duckduckgo.com/html/?q={q}&kp={kp}'.format(\n q=q, kp=1 if safe_search else -1)\n log('Making ddg query: ' + url)\n from calibre.scraper.simple import read_url\n br = br or browser()\n root = query(br, url, 'ddg', dump_raw, timeout=timeout, simple_scraper=partial(read_url, ddg_scraper_storage))\n ans = []\n for a in root.xpath('//*[@class=\"results\"]//*[@class=\"result__title\"]/a[@href and @class=\"result__a\"]'):\n try:\n ans.append(Result(ddg_href(a.get('href')), tostring(a), None))\n except KeyError:\n log('Failed to find ddg href in:', a.get('href'))\n return ans, url\n\n\ndef ddg_develop():\n br = browser()\n for result in ddg_search('heroes abercrombie'.split(), 'www.amazon.com', dump_raw='/t/raw.html', br=br)[0]:\n if '/dp/' in result.url:\n print(result.title)\n print(' ', result.url)\n print(' ', get_cached_url(result.url, br))\n print()\n# }}}\n\n\n# Bing uses a CAPTCHA {{{\n\ndef bing_term(t):\n t = t.replace('\"', '')\n if t in {'OR', 'AND', 'NOT'}:\n t = t.lower()\n return t\n\n\ndef bing_url_processor(url):\n return url\n\n\ndef resolve_bing_wrapper_page(url, br, log):\n raw = br.open_novisit(url).read().decode('utf-8', 'replace')\n m = re.search(r'var u = \"(.+)\"', raw)\n if m is None:\n log('Failed to resolve bing wrapper page for url: ' + url)\n return url\n log('Resolved bing wrapped URL: ' + url + ' to ' + m.group(1))\n return m.group(1)\n\n\nbing_scraper_storage = []\n\n\ndef bing_search(\n terms, site=None, br=None, log=prints, safe_search=False, dump_raw=None, timeout=60,\n show_user_agent=False, result_url_is_ok=lambda x: True\n):\n # http://vlaurie.com/computers2/Articles/bing_advanced_search.htm\n terms = [quote_term(bing_term(t)) for t in terms]\n if site is not None:\n terms.append(quote_term(('site:' + site)))\n q = '+'.join(terms)\n url = 'https://www.bing.com/search?q={q}'.format(q=q)\n log('Making bing query: ' + url)\n from calibre.scraper.simple import read_url\n root = query(br, url, 'bing', dump_raw, timeout=timeout, simple_scraper=partial(read_url, bing_scraper_storage))\n ans = []\n result_items = root.xpath('//*[@id=\"b_results\"]/li[@class=\"b_algo\"]')\n if not result_items:\n log('Bing returned no results')\n return ans, url\n for li in result_items:\n a = li.xpath('descendant::h2/a[@href]') or li.xpath('descendant::div[@class=\"b_algoheader\"]/a[@href]')\n a = a[0]\n title = tostring(a)\n ans_url = a.get('href')\n if ans_url.startswith('https://www.bing.com/'):\n ans_url = resolve_bing_wrapper_page(ans_url, br, log)\n if result_url_is_ok(ans_url):\n ans.append(Result(ans_url, title, None))\n if not ans:\n title = ' '.join(root.xpath('//title/text()'))\n log('Failed to find any results on results page, with title:', title)\n return ans, url\n\n\ndef bing_develop(terms='heroes abercrombie'):\n if isinstance(terms, str):\n terms = terms.split()\n for result in bing_search(terms, 'www.amazon.com', dump_raw='/t/raw.html', show_user_agent=True)[0]:\n if '/dp/' in result.url:\n print(result.title)\n print(' ', result.url)\n print(' ', result.cached_url)\n print()\n# }}}\n\n\n# Google only serves JS enabled search pages as of Sep 11, 2025 {{{\n\ndef google_term(t):\n t = t.replace('\"', '')\n if t in {'OR', 'AND', 'NOT'}:\n t = t.lower()\n return t\n\n\ndef google_url_processor(url):\n return url\n\n\ndef google_cache_url_for_url(url):\n if not isinstance(url, bytes):\n url = url.encode('utf-8')\n cu = quote(url, safe='')\n if isinstance(cu, bytes):\n cu = cu.decode('utf-8')\n return 'https://webcache.googleusercontent.com/search?q=cache:' + cu\n\n\ndef google_get_cached_url(url, br=None, log=prints, timeout=60):\n # Google's webcache was discontinued in september 2024\n cached_url = google_cache_url_for_url(url)\n br = google_specialize_browser(br or browser())\n try:\n raw = query(br, cached_url, 'google-cache', parser=lambda x: x.encode('utf-8'), timeout=timeout)\n except Exception as err:\n log('Failed to get cached URL from google for URL: {} with error: {}'.format(url, err))\n else:\n with webcache_lock:\n webcache[cached_url] = raw\n return cached_url\n\n\ndef canonicalize_url_for_cache_map(url):\n try:\n purl = urlparse(url)\n except Exception:\n return url\n if '.amazon.' in purl.netloc:\n url = url.split('&', 1)[0]\n return url\n\n\ndef google_parse_results(root, raw, log=prints, ignore_uncached=True):\n ans = []\n seen = set()\n for a in root.xpath('//a[@href]'):\n href = a.get('href')\n if not href.startswith('/url?q=http'):\n continue\n try:\n url = parse_qs(urlparse(href).query)['q'][0]\n purl = urlparse(url)\n except Exception:\n continue\n if purl.hostname.endswith('google.com'):\n continue\n try:\n title = tostring(next(a.iterchildren('span')))\n except StopIteration:\n continue\n curl = canonicalize_url_for_cache_map(url)\n if curl in seen:\n continue\n seen.add(curl)\n ans.append(Result(curl, title, None))\n if not ans:\n title = ' '.join(root.xpath('//title/text()'))\n log('Failed to find any results on results page, with title:', title)\n return ans\n\n\ndef google_consent_cookies():\n # See https://github.com/benbusby/whoogle-search/pull/1054 for cookies\n from base64 import standard_b64encode\n from datetime import date\n base = {'domain': '.google.com', 'path': '/'}\n b = base.copy()\n b['name'], b['value'] = 'CONSENT', 'PENDING+987'\n yield b\n template = b'\\x08\\x01\\x128\\x08\\x14\\x12+boq_identityfrontenduiserver_20231107.05_p0\\x1a\\x05en-US \\x03\\x1a\\x06\\x08\\x80\\xf1\\xca\\xaa\\x06'\n template.replace(b'20231107', date.today().strftime('%Y%m%d').encode('ascii'))\n b = base.copy()\n b['name'], b['value'] = 'SOCS', standard_b64encode(template).decode('ascii').rstrip('=')\n yield b\n\n\ndef google_specialize_browser(br):\n with webcache_lock:\n if not hasattr(br, 'google_consent_cookie_added'):\n for c in google_consent_cookies():\n br.set_simple_cookie(c['name'], c['value'], c['domain'], path=c['path'])\n br.google_consent_cookie_added = True\n # google serves JS based pages without the right user agent\n br.set_user_agent('L''y''nx''/2.''8.''6rel''.5 lib''ww''w-F''M/2.''1''4') # noqa\n return br\n\n\ndef is_probably_book_asin(t):\n return t and len(t) == 10 and t.startswith('B') and t.upper() == t\n\n\ndef is_asin_or_isbn(t):\n from calibre.ebooks.metadata import check_isbn\n return bool(check_isbn(t) or is_probably_book_asin(t))\n\n\ndef google_format_query(terms, site=None, tbm=None):\n prevent_spelling_correction = False\n for t in terms:\n if is_asin_or_isbn(t):\n prevent_spelling_correction = True\n break\n terms = [quote_term(google_term(t)) for t in terms]\n if site is not None:\n terms.append(quote_term(('site:' + site)))\n q = '+'.join(terms)\n url = 'https://www.google.com/search?q={q}'.format(q=q)\n # tbm causes 403 forbidden errors\n # if tbm:\n # url += '&tbm=' + tbm\n if prevent_spelling_correction:\n url += '&nfpr=1'\n return url\n\n\ndef google_search(terms, site=None, br=None, log=prints, safe_search=False, dump_raw=None, timeout=60):\n url = google_format_query(terms, site)\n log('Making google query: ' + url)\n br = google_specialize_browser(br or browser())\n r = []\n root = query(br, url, 'google', dump_raw, timeout=timeout, save_raw=r.append)\n return google_parse_results(root, r[0], log=log), url\n\n\ndef google_develop(search_terms='1423146786', raw_from=''):\n if raw_from:\n with open(raw_from, 'rb') as f:\n raw = f.read()\n results = google_parse_results(parse_html(raw), raw)\n else:\n br = browser()\n results = google_search(search_terms.split(), 'www.amazon.com', dump_raw='/t/raw.html', br=br)[0]\n for result in results:\n if '/dp/' in result.url:\n print(result.title)\n print(' ', result.url)\n print(' ', result.cached_url)\n print()\n# }}}\n\n\n# Yandex uses a CAPTCHA {{{\ndef yandex_term(t):\n t = t.replace('\"', '')\n if t in {'OR', 'AND', 'NOT'}:\n t = t.lower()\n return t\n\n\ndef yandex_format_query(terms, site=None):\n terms = [quote_term(yandex_term(t)) for t in terms]\n if site is not None:\n terms.append(quote_term(('site:' + site)))\n q = '+'.join(terms)\n url = 'https://yandex.com/search?text={q}'.format(q=q)\n return url\n\n\ndef yandex_parse_results(root, raw, log=prints, ignore_uncached=True):\n pass\n\n\nyandex_scraper_storage = []\n\n\ndef yandex_search(terms, site=None, br=None, dump_raw=None, log=prints, timeout=60):\n # Sadly yandex uses CAPTCHAs aggresively\n url = yandex_format_query(terms, site)\n br = browser()\n r = []\n from calibre.scraper.simple import read_url\n root = query(br, url, 'yandex', dump_raw, timeout=timeout, save_raw=r.append, simple_scraper=partial(read_url, yandex_scraper_storage))\n return yandex_parse_results(root, r[0], log=log), url\n\n\ndef yandex_develop(search_terms='1423146786', raw_from=''):\n if raw_from:\n with open(raw_from, 'rb') as f:\n raw = f.read()\n results = yandex_parse_results(parse_html(raw), raw)\n else:\n results = yandex_search(search_terms.split(), 'www.amazon.com', dump_raw='/t/raw.html')[0]\n for result in results:\n if '/dp/' in result.url:\n print(result.title)\n print(' ', result.url)\n print(' ', result.cached_url)\n print()\n\n# }}}\n\n\ndef get_cached_url(url, br=None, log=prints, timeout=60):\n from threading import Lock, Thread\n\n from polyglot.queue import Queue\n print_lock = Lock()\n q = Queue()\n\n def safe_print(*a):\n with print_lock:\n log(*a)\n\n def doit(func):\n try:\n q.put(func(url, br, safe_print, timeout))\n except Exception as e:\n safe_print(e)\n q.put(None)\n\n threads = []\n threads.append(Thread(target=doit, args=(wayback_machine_cached_url,), daemon=True).start())\n while threads:\n x = q.get()\n if x is not None:\n return x\n threads.pop()\n\n\ndef get_data_for_cached_url(url):\n with webcache_lock:\n return webcache.get(url)\n\n\ndef resolve_url(url):\n prefix, rest = url.partition(':')[::2]\n if prefix == 'bing':\n return bing_url_processor(rest)\n if prefix == 'wayback':\n return wayback_url_processor(rest)\n return url\n\n\n# if __name__ == '__main__':\n# import sys\n# func = sys.argv[-1]\n# globals()[func]()\n" -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/metadata_sources/global.json b/dotfiles/common/.config/calibre/metadata_sources/global.json deleted file mode 100644 index fb03e0a..0000000 --- a/dotfiles/common/.config/calibre/metadata_sources/global.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "ignore_fields": [ - "rating", - "series" - ], - "tag_map_rules": [ - { - "action": "remove", - "match_type": "not_one_of", - "query": "Anthropology, Art, Biography & Autobiography, Business, Chess, Comedy, Comics, Computers, Cooking, Critical Theory, Design, DIY, Economics, Finance, Games, Gardening, Health, History, Language Learning, Law, Linguistics, Literary Critique, Literature, Magic, Mathematics, Music, Mythology, news, Non-Fiction, Philosophy, Poetry, Political Science, Politics, Psychology, Religion, Science, Social Critique, Sociology, Travel, Zen", - "replace": "" - } - ] -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/mtp_devices.json b/dotfiles/common/.config/calibre/mtp_devices.json deleted file mode 100644 index 274f3de..0000000 --- a/dotfiles/common/.config/calibre/mtp_devices.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "blacklist": [], - "history": { - "G0W19E040464033L": [ - "Fire", - "2021-01-28T21:54:04.815072+00:00" - ] - } -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/plugins/Apple Books covers.zip b/dotfiles/common/.config/calibre/plugins/Apple Books covers.zip Binary files differdeleted file mode 100644 index 722e2c7..0000000 --- a/dotfiles/common/.config/calibre/plugins/Apple Books covers.zip +++ /dev/null diff --git a/dotfiles/common/.config/calibre/plugins/Comments Cleaner.zip b/dotfiles/common/.config/calibre/plugins/Comments Cleaner.zip Binary files differdeleted file mode 100644 index 5a9b03c..0000000 --- a/dotfiles/common/.config/calibre/plugins/Comments Cleaner.zip +++ /dev/null diff --git a/dotfiles/common/.config/calibre/plugins/Extract ISBN.json b/dotfiles/common/.config/calibre/plugins/Extract ISBN.json deleted file mode 100644 index 29e3135..0000000 --- a/dotfiles/common/.config/calibre/plugins/Extract ISBN.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "Options": { - "askForConfirmation": true, - "batchSize": 100, - "displayFailures": true, - "postTask": "none", - "validISBN13Prefix": [ - "977", - "978", - "979" - ], - "workerThreshold": 1 - } -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/plugins/Extract ISBN.zip b/dotfiles/common/.config/calibre/plugins/Extract ISBN.zip Binary files differdeleted file mode 100644 index aa3628c..0000000 --- a/dotfiles/common/.config/calibre/plugins/Extract ISBN.zip +++ /dev/null diff --git a/dotfiles/common/.config/calibre/plugins/Favourites Menu.json b/dotfiles/common/.config/calibre/plugins/Favourites Menu.json deleted file mode 100644 index 27e1779..0000000 --- a/dotfiles/common/.config/calibre/plugins/Favourites Menu.json +++ /dev/null @@ -1,140 +0,0 @@ -{ - "menus": [ - { - "display": "Start Content server", - "path": [ - "Connect Share", - "Start Content server" - ] - }, - { - "display": "Extract ISBN", - "path": [ - "Extract ISBN" - ] - }, - null, - { - "display": "Add to default list", - "path": [ - "Reading List", - "Add to default list" - ] - }, - { - "display": "Remove from default list", - "path": [ - "Reading List", - "Remove from default list" - ] - }, - { - "display": "View default list", - "path": [ - "Reading List", - "View default list" - ] - }, - { - "display": "Edit default list", - "path": [ - "Reading List", - "Edit default list" - ] - }, - null, - { - "display": "Find book duplicates...", - "path": [ - "Find Duplicates", - "Find book duplicates..." - ] - }, - { - "display": "Clean comments", - "path": [ - "Clean Comments", - "Clean comments" - ] - }, - { - "display": "Check library", - "path": [ - "Choose Library", - "Library maintenance", - "Check library" - ] - }, - { - "display": "Plugin updater", - "path": [ - "Plugin Updater" - ] - }, - null, - { - "display": "Email to...", - "path": [ - "Connect Share", - "Email to..." - ] - }, - { - "display": "email to Christine's Kindle", - "path": [ - "Connect Share", - "Email to...", - "Christine's Kindle" - ] - }, - { - "display": "email to Craig's Paperwhite", - "path": [ - "Connect Share", - "Email to...", - "Paperwhite" - ] - }, - { - "display": "email to Craig's Pixel6", - "path": [ - "Connect Share", - "Email to...", - "Pixel6" - ] - }, - null, - { - "display": "Start wireless device connection", - "path": [ - "Connect Share", - "Start wireless device connection" - ] - }, - { - "display": "Stop wireless device connection [192.168.86.26 or 172.17.0.1, port 9090]", - "path": [ - "Connect Share", - "Stop wireless device connection [192.168.86.26 or 172.17.0.1, port 9090]" - ] - }, - { - "display": "Manage collections", - "path": [ - "Edit Collections" - ] - }, - { - "display": "Get books", - "path": [ - "Store" - ] - }, - { - "display": "Kindle Collections", - "path": [ - "Kindle Collections" - ] - } - ] -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/plugins/Favourites Menu.zip b/dotfiles/common/.config/calibre/plugins/Favourites Menu.zip Binary files differdeleted file mode 100644 index 569574c..0000000 --- a/dotfiles/common/.config/calibre/plugins/Favourites Menu.zip +++ /dev/null diff --git a/dotfiles/common/.config/calibre/plugins/Find Duplicates.json b/dotfiles/common/.config/calibre/plugins/Find Duplicates.json deleted file mode 100644 index 9daa2b7..0000000 --- a/dotfiles/common/.config/calibre/plugins/Find Duplicates.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "authorMatch": "identical", - "authorSoundexLength": 8, - "autoDeleteBinaryDups": false, - "identifierType": "isbn", - "includeLanguages": false, - "searchType": "binary", - "showAllGroups": true, - "showTagAuthor": true, - "sortGroupsByTitle": true, - "titleMatch": "identical", - "titleSoundexLength": 6 -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/plugins/Find Duplicates.zip b/dotfiles/common/.config/calibre/plugins/Find Duplicates.zip Binary files differdeleted file mode 100644 index 73112d9..0000000 --- a/dotfiles/common/.config/calibre/plugins/Find Duplicates.zip +++ /dev/null diff --git a/dotfiles/common/.config/calibre/plugins/Kindle hi-res covers.zip b/dotfiles/common/.config/calibre/plugins/Kindle hi-res covers.zip Binary files differdeleted file mode 100644 index 40106fe..0000000 --- a/dotfiles/common/.config/calibre/plugins/Kindle hi-res covers.zip +++ /dev/null diff --git a/dotfiles/common/.config/calibre/plugins/Kobo Utilities.json b/dotfiles/common/.config/calibre/plugins/Kobo Utilities.json deleted file mode 100644 index 4d9121a..0000000 --- a/dotfiles/common/.config/calibre/plugins/Kobo Utilities.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "BookmarkOptions": { - "backgroundJob": false, - "clearIfUnread": false, - "doNotStoreIfReopened": false, - "rating": true, - "readingStatus": true, - "setDateToNow": true, - "storeBookmarks": true, - "storeIfMoreRecent": false - }, - "Devices": { - "N4181C1037466": { - "active": true, - "backupOptionsStore": { - "backupCopiesToKeepSpin": 10, - "backupDestDirectory": "/home/cjennings/documents/kobo", - "backupEachCOnnection": true, - "backupZipDatabase": true, - "doDailyBackp": false - }, - "location_code": "main", - "name": "Kobo Libra 2", - "serial_no": "N4181C1037466", - "type": "Kobo Libra 2", - "updateOptionsStore": { - "doEarlyFirmwareUpdate": false, - "doFirmwareUpdateCheck": true, - "firmwareUpdateCheckLastTime": 0 - }, - "uuid": "8de75c8a-f9b6-405c-86a3-515afd1e71fa" - } - }, - "MetadataOptions": { - "author": false, - "authourSort": false, - "description": false, - "descriptionTemplate": "", - "descriptionUseTemplate": false, - "isbn": false, - "language": false, - "published_date": false, - "publisher": false, - "rating": false, - "readingStatus": -1, - "reading_direction": "Default", - "resetPosition": false, - "series": false, - "setRreadingStatus": false, - "set_reading_direction": false, - "set_sync_date": false, - "subtitle": false, - "subtitleTemplate": "", - "sync_date_library_date": "timestamp", - "title": false, - "titleSort": false, - "update_KoboEpubs": false, - "usePlugboard": false - }, - "ReadingOptions": { - "doNotUpdateIfSet": false, - "lockMargins": false, - "readingAlignment": "Off", - "readingFontFamily": "Georgia", - "readingFontSize": 22, - "readingLeftMargin": 3, - "readingLineHeight": 1.3, - "readingRightMargin": 3, - "updateConfigFile": false - }, - "_version": 2, - "backupAnnotations": { - "dest_directory": "" - }, - "backupOptionsStore": { - "backupCopiesToKeepSpin": 5, - "backupDestDirectory": "", - "backupEachCOnnection": false, - "backupZipDatabase": true, - "doDailyBackp": false - }, - "cleanImagesDir": { - "delete_extra_covers": false - }, - "commonOptionsStore": { - "buttonActionDevice": "", - "buttonActionLibrary": "", - "individualDeviceOptions": true - }, - "coverUpload": { - "blackandwhite": false, - "dithered_covers": false, - "keep_cover_aspect": false, - "kepub_covers": false, - "letterbox": false, - "letterbox_color": "#000000", - "png_covers": false - }, - "fixDuplicatesOptionsStore": { - "keepNewestShelf": true, - "purgeShelves": false - }, - "getShelvesOptionStore": { - "allBooks": true, - "replaceShelves": true - }, - "removeAnnotations": { - "removeAnnotAction": 0 - }, - "removeCovers": { - "kepub_covers": false, - "remove_fullsize_covers": false - }, - "setRelatedBooksOptionsStore": { - "relatedBooksType": 0 - }, - "updateOptionsStore": { - "doEarlyFirmwareUpdate": false, - "doFirmwareUpdateCheck": false, - "firmwareUpdateCheckLastTime": 1656213583 - } -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/plugins/Open With.json b/dotfiles/common/.config/calibre/plugins/Open With.json deleted file mode 100644 index 81eaeb8..0000000 --- a/dotfiles/common/.config/calibre/plugins/Open With.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "OpenWithMenus": { - "Menus": [ - { - "active": false, - "appArgs": "", - "appPath": "firefox", - "format": "EPUB", - "image": "owp_firefox.png", - "menuText": "EPUBReader (EPUB)", - "subMenu": "" - }, - { - "active": false, - "appArgs": "-c", - "appPath": "/usr/bin/emacsclient", - "format": "PDF", - "image": "reader.png", - "menuText": "Emacsclient", - "subMenu": "" - }, - { - "active": true, - "appArgs": "", - "appPath": "/usr/bin/zathura", - "format": "EPUB", - "image": "edit_book.png", - "menuText": "Zathura (EPUB)", - "subMenu": "" - }, - { - "active": true, - "appArgs": "", - "appPath": "/usr/bin/zathura", - "format": "PDF", - "image": "PDF.png", - "menuText": "Zathura (PDF)", - "subMenu": "" - }, - { - "active": false, - "appArgs": "-c", - "appPath": "/usr/bin/emacsclient", - "format": "EPUB", - "image": "PDF.png", - "menuText": "Emacsclient", - "subMenu": "" - }, - { - "active": false, - "appArgs": "", - "appPath": "gimp", - "format": "COVER", - "image": "owp_gimp.png", - "menuText": "Gimp (Cover)", - "subMenu": "" - } - ], - "UrlColWidth": 202 - } -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/plugins/Reading List.json b/dotfiles/common/.config/calibre/plugins/Reading List.json deleted file mode 100644 index a348407..0000000 --- a/dotfiles/common/.config/calibre/plugins/Reading List.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Devices": {}, - "Options": { - "quickAccess": false, - "removeDialog": true - }, - "SchemaVersion": 1.65 -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/plugins/Reading List.zip b/dotfiles/common/.config/calibre/plugins/Reading List.zip Binary files differdeleted file mode 100644 index 8592b6c..0000000 --- a/dotfiles/common/.config/calibre/plugins/Reading List.zip +++ /dev/null diff --git a/dotfiles/common/.config/calibre/plugins/Search The Internet.json b/dotfiles/common/.config/calibre/plugins/Search The Internet.json deleted file mode 100644 index 21f6786..0000000 --- a/dotfiles/common/.config/calibre/plugins/Search The Internet.json +++ /dev/null @@ -1,1097 +0,0 @@ -{ - "SearchMenus": { - "Menus": [ - { - "active": false, - "encoding": "utf-8", - "image": "stip_audible.png", - "menuText": "Audible for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.audible.com/search?keywords={author}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_audible.png", - "menuText": "Audible for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.audible.com/search?keywords={author}+{title}" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": true, - "encoding": "latin-1", - "image": "stip_amazon.png", - "menuText": "Amazon.com for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Dstripbooks&field-keywords={author}+{title}" - }, - { - "active": false, - "encoding": "latin-1", - "image": "stip_amazon.png", - "menuText": "Amazon.co.uk for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.amazon.co.uk/s/ref=nb_sb_noss?url=search-alias%3Dstripbooks&field-keywords={author}+{title}" - }, - { - "active": false, - "encoding": "latin-1", - "image": "stip_amazon.png", - "menuText": "Amazon.com.au for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.amazon.com.au/s/ref=nb_sb_noss?url=search-alias%3Dstripbooks&field-keywords={author}+{title}" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_bn.png", - "menuText": "Barnes and Noble for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.barnesandnoble.com/s/{author}/_/M-8q8" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_bn.png", - "menuText": "Barnes and Noble for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.barnesandnoble.com/s/{author}+{title}/_/M-8q8" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_bn.png", - "menuText": "Barnes and Noble for ISBN", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.barnesandnoble.com/s/{isbn}/_/M-8q8" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_bn.png", - "menuText": "Barnes and Noble for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.barnesandnoble.com/s/{title}/_/M-8q8" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_ebay.png", - "menuText": "EBay US", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.ebay.com/sch/i.html?_nkw={author}+{title}&_sacat=267" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_ebay.png", - "menuText": "EBay UK", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.ebay.co.uk/sch/i.html?_nkw={author}+{title}&_sacat=267" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_ebooks.png", - "menuText": "EBooks for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.ebooks.com/SearchApp/SearchResults.net?term={author}&RestrictBy=author" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_ebooks.png", - "menuText": "EBooks for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.ebooks.com/SearchApp/SearchResults.net?term={author}+{title}" - }, - { - "active": true, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": true, - "encoding": "utf-8", - "image": "stip_ff.png", - "menuText": "FantasticFiction for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.fantasticfiction.com/search/?searchfor=author&keywords={author}" - }, - { - "active": true, - "encoding": "utf-8", - "image": "stip_ff.png", - "menuText": "FantasticFiction for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.fantasticfiction.com/search/?searchfor=book&keywords={title}" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_fictiondb.png", - "menuText": "FictionDB for Author", - "method": "POST", - "openGroup": false, - "subMenu": "", - "url": "https://www.fictiondb.com/search/searchresults.htm?styp=5&srchtxt={author}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_fictiondb.png", - "menuText": "FictionDB for Book", - "method": "POST", - "openGroup": false, - "subMenu": "", - "url": "https://www.fictiondb.com/search/searchresults.htm?styp=5&author={author}&title={title}&srchtxt=multi&sgcode=0&tpcode=0&imprint=0&pubgroup=0&genretype=--&rating=-&myrating=-&status=-" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_fictiondb.png", - "menuText": "FictionDB for ISBN", - "method": "POST", - "openGroup": false, - "subMenu": "", - "url": "https://www.fictiondb.com/search/searchresults.htm?styp=5&srchtxt={isbn}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_fictiondb.png", - "menuText": "FictionDB for Title", - "method": "POST", - "openGroup": false, - "subMenu": "", - "url": "https://www.fictiondb.com/search/searchresults.htm?styp=5&srchtxt={title}" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_goodreads.png", - "menuText": "Goodreads for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.goodreads.com/search/search?q={author}&search_type=books" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_goodreads.png", - "menuText": "Goodreads for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.goodreads.com/search/search?q={author}+{title}&search_type=books" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_goodreads.png", - "menuText": "Goodreads for ISBN", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.goodreads.com/search/search?q={isbn}&search_type=books" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_goodreads.png", - "menuText": "Goodreads for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.goodreads.com/search/search?q={title}&search_type=books" - }, - { - "active": true, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": true, - "encoding": "utf-8", - "image": "stip_google.png", - "menuText": "Google images for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.google.com/images?q=%22{author}%22+%22{title}%22" - }, - { - "active": true, - "encoding": "utf-8", - "image": "stip_google.png", - "menuText": "Google images 400x300", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.google.com/images?as_q={author}+%22{title}%22&tbs=isch:1,isz:lt,islt:qsvga,imgo:1&safe=off" - }, - { - "active": true, - "encoding": "utf-8", - "image": "stip_google.png", - "menuText": "Google.com for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.google.com/#sclient=psy&q=%22{author}%22+%22{title}%22" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": false, - "encoding": "latin-1", - "image": "stip_isfdb.png", - "menuText": "isfdb for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.isfdb.org/cgi-bin/se.cgi?type=Name&arg={author}" - }, - { - "active": false, - "encoding": "latin-1", - "image": "stip_isfdb.png", - "menuText": "isfdb for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.isfdb.org/cgi-bin/edit/tp_search.cgi?TERM_1={title}&USE_1=title&OPERATOR_1=AND&TERM_2={author}&USE_2=author&OPERATOR_2=AND" - }, - { - "active": false, - "encoding": "latin-1", - "image": "stip_isfdb.png", - "menuText": "isfdb for ISBN", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.isfdb.org/cgi-bin/se.cgi?type=ISBN&arg={isbn}" - }, - { - "active": false, - "encoding": "latin-1", - "image": "stip_isfdb.png", - "menuText": "isfdb for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.isfdb.org/cgi-bin/se.cgi?type=Fiction+Titles&arg={title}" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_kobo.png", - "menuText": "Kobo for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.kobobooks.com/search/search.html?q={author}&f=author" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_kobo.png", - "menuText": "Kobo for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.kobobooks.com/search/search.html?q={author}+{title}&f=author" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_kobo.png", - "menuText": "Kobo for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.kobobooks.com/search/search.html?q={title}" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_loc.png", - "menuText": "Library of Congress for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://catalog.loc.gov/cgi-bin/Pwebrecon.cgi?DB=local&Search_Arg={author}&Search_Code=NAME%40&CNT=100&hist=1&type=quick" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_loc.png", - "menuText": "Library of Congress for ISBN", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://catalog.loc.gov/cgi-bin/Pwebrecon.cgi?DB=local&Search_Arg={isbn}&Search_Code=STNO^*&CNT=100&hist=1&type=quick" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_loc.png", - "menuText": "Library of Congress for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://catalog.loc.gov/cgi-bin/Pwebrecon.cgi?DB=local&Search_Arg={title}&Search_Code=TKEY^*&CNT=100&hist=1&type=quick" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_lthing.png", - "menuText": "LibraryThing for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.librarything.com/search.php?search={author}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_lthing.png", - "menuText": "LibraryThing for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.librarything.com/search.php?search={title}+{author}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_lthing.png", - "menuText": "LibraryThing for ISBN", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.librarything.com/search.php?search={isbn}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_lthing.png", - "menuText": "LibraryThing for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.librarything.com/search.php?search={title}" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_litmap.png", - "menuText": "Literature-Map like Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.literature-map.com/{author}" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_loveread.png", - "menuText": "Lovereading for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.lovereading.co.uk/search?s={author}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_loveread.png", - "menuText": "Lovereading for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.lovereading.co.uk/search?s={author}+{title}&view=book-results" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_loveread.png", - "menuText": "Lovereading for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.lovereading.co.uk/search?s={title}&view=book-results" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_manybooks.png", - "menuText": "ManyBooks for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://manybooks.net/search-book?search={author}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_manybooks.png", - "menuText": "ManyBooks for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://manybooks.net/search-book?search={title}" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": false, - "encoding": "", - "image": "stip_nyt.png", - "menuText": "NYTimes for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://nytimes.com/search?query={author}" - }, - { - "active": false, - "encoding": "", - "image": "stip_nyt.png", - "menuText": "NYTimes for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://nytimes.com/search?query={author}+{title}" - }, - { - "active": false, - "encoding": "", - "image": "stip_nyt.png", - "menuText": "NYTimes for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://nytimes.com/search?query={title}" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wstones.png", - "menuText": "Waterstones for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.waterstones.com/books/search/term/{author}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wstones.png", - "menuText": "Waterstones for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.waterstones.com/books/search/term/{title}+{author}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wstones.png", - "menuText": "Waterstones for ISBN", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.waterstones.com/books/search/term/{isbn}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wstones.png", - "menuText": "Waterstones for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.waterstones.com/books/search/term/{title}" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wsirn.png", - "menuText": "WhatShouldIReadNext for ISBN", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.whatshouldireadnext.com/isbn/{isbn}" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wikipedia.png", - "menuText": "Wikipedia for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://en.wikipedia.org/w/index.php?title=Special%3ASearch&search={author}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wikipedia.png", - "menuText": "Wikipedia for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://en.wikipedia.org/w/index.php?title=Special%3ASearch&search={author}+{title}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wikipedia.png", - "menuText": "Wikipedia for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://en.wikipedia.org/w/index.php?title=Special%3ASearch&search={title}" - }, - { - "active": false, - "encoding": "", - "image": "", - "menuText": "", - "method": "", - "openGroup": false, - "subMenu": "", - "url": "" - }, - { - "active": false, - "encoding": "latin-1", - "image": "stip_amazon.png", - "menuText": "Amazon.ca for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.amazon.ca/s/ref=nb_sb_noss?url=search-alias%3Dstripbooks&field-keywords={author}+{title}" - }, - { - "active": false, - "encoding": "latin-1", - "image": "stip_amazon.png", - "menuText": "Amazon.cn for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.amazon.cn/s/ref=nb_sb_noss?url=search-alias%3Dstripbooks&field-keywords={author}+{title}" - }, - { - "active": false, - "encoding": "latin-1", - "image": "stip_amazon.png", - "menuText": "Amazon.co.jp for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.amazon.co.jp/s/ref=nb_sb_noss?url=search-alias%3Dstripbooks&field-keywords={author}+{title}" - }, - { - "active": false, - "encoding": "latin-1", - "image": "stip_amazon.png", - "menuText": "Amazon.de for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.amazon.de/s/ref=nb_sb_noss?url=search-alias%3Dstripbooks&field-keywords={author}+{title}" - }, - { - "active": false, - "encoding": "latin-1", - "image": "stip_amazon.png", - "menuText": "Amazon.it for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.amazon.it/s/ref=nb_sb_noss?url=search-alias%3Dstripbooks&field-keywords={author}+{title}" - }, - { - "active": false, - "encoding": "latin-1", - "image": "stip_amazon.png", - "menuText": "Amazon.fr for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.amazon.fr/s/ref=nb_sb_noss?url=search-alias%3Dstripbooks&field-keywords={author}+{title}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_chapters.png", - "menuText": "Chapters.ca for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://indigo.ca/en-ca/search/?q={author}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_chapters.png", - "menuText": "Chapters.ca for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://indigo.ca/en-ca/search/?q={author}+{title}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_chapters.png", - "menuText": "Chapters.ca for ISBN", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://indigo.ca/en-ca/search/?q={isbn}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_chapters.png", - "menuText": "Chapters.ca for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://indigo.ca/en-ca/search/?q={title}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_fnac.png", - "menuText": "Fnac for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://recherche.fnac.com/Search/SearchResult.aspx?SCat=2&Search={author}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_fnac.png", - "menuText": "Fnac for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://recherche.fnac.com/Search/SearchResult.aspx?SCat=2&Search={author}+{title}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_fnac.png", - "menuText": "Fnac for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://recherche.fnac.com/Search/SearchResult.aspx?SCat=2&Search={title}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_google.png", - "menuText": "Google.de for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.google.de/#sclient=psy&q=%22{author}%22+%22{title}%22" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_google.png", - "menuText": "Google.es for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.google.es/#sclient=psy&q=%22{author}%22+%22{title}%22" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_google.png", - "menuText": "Google.fr for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.google.fr/#sclient=psy&q=%22{author}%22+%22{title}%22" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_google.png", - "menuText": "Google.it for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.google.it/#sclient=psy&q=%22{author}%22+%22{title}%22" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_libri.png", - "menuText": "libri.de for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.libri.de/shop/action/advancedSearch?action=search&nodeId=-1&binderType=Alle&languageCode=DE&person={author}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_libri.png", - "menuText": "libri.de for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.libri.de/shop/action/advancedSearch?action=search&nodeId=-1&binderType=Alle&languageCode=DE&title={title}&person={author}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_libri.png", - "menuText": "libri.de for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://www.libri.de/shop/action/advancedSearch?action=search&nodeId=-1&binderType=Alle&languageCode=DE&title={title}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wikipedia.png", - "menuText": "Wikipedia.de for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://de.wikipedia.org/w/index.php?title=Special%3ASearch&search={author}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wikipedia.png", - "menuText": "Wikipedia.de for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://de.wikipedia.org/w/index.php?title=Special%3ASearch&search={author}+{title}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wikipedia.png", - "menuText": "Wikipedia.de for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://de.wikipedia.org/w/index.php?title=Special%3ASearch&search={title}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wikipedia.png", - "menuText": "Wikipedia.es for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://es.wikipedia.org/w/index.php?title=Special%3ASearch&search={author}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wikipedia.png", - "menuText": "Wikipedia.es for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://es.wikipedia.org/w/index.php?title=Special%3ASearch&search={author}+{title}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wikipedia.png", - "menuText": "Wikipedia.es for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://es.wikipedia.org/w/index.php?title=Special%3ASearch&search={title}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wikipedia.png", - "menuText": "Wikipedia.fr for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://fr.wikipedia.org/w/index.php?title=Sp%E9cial%3ARecherche&search={author}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wikipedia.png", - "menuText": "Wikipedia.fr for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://fr.wikipedia.org/w/index.php?title=Special%3ASearch&search={author}+{title}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wikipedia.png", - "menuText": "Wikipedia.fr for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://fr.wikipedia.org/w/index.php?title=Sp%E9cial%3ARecherche&search={title}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wikipedia.png", - "menuText": "Wikipedia.it for Author", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://it.wikipedia.org/w/index.php?title=Special%3ASearch&search={author}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wikipedia.png", - "menuText": "Wikipedia.it for Book", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://it.wikipedia.org/w/index.php?title=Special%3ASearch&search={author}+{title}" - }, - { - "active": false, - "encoding": "utf-8", - "image": "stip_wikipedia.png", - "menuText": "Wikipedia.it for Title", - "method": "GET", - "openGroup": false, - "subMenu": "", - "url": "https://it.wikipedia.org/w/index.php?title=Special%3ASearch&search={title}" - } - ], - "UrlColWidth": 1543 - } -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/save_to_disk.py.json b/dotfiles/common/.config/calibre/save_to_disk.py.json deleted file mode 100644 index bdf4e57..0000000 --- a/dotfiles/common/.config/calibre/save_to_disk.py.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "asciiize": false, - "formats": "all", - "replace_whitespace": false, - "save_cover": true, - "save_extra_files": false, - "send_template": "{author_sort}/{title} - {authors}", - "send_timefmt": "%b, %Y", - "single_dir": false, - "template": "{author_sort}/{title}/{title} - {authors}", - "timefmt": "%b, %Y", - "to_lowercase": false, - "update_metadata": false, - "write_opf": true -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/scheduler.xml b/dotfiles/common/.config/calibre/scheduler.xml deleted file mode 100644 index c946210..0000000 --- a/dotfiles/common/.config/calibre/scheduler.xml +++ /dev/null @@ -1,166 +0,0 @@ -<?xml version='1.0' encoding='utf-8'?> -<recipe_collection xmlns="http://calibre-ebook.com/recipe_collection"> - - <recipe_customization keep_issues="0" id="builtin:cracked_com" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:economist_free" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:economist_news" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:howtogeek" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:instapaper" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:nejm" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="7" id="builtin:nytimes" add_title_tag="yes" custom_tags="news" recipe_specific_options="{"web": "yes", "days": "1", "comp": "yes"}"/> - - <recipe_customization keep_issues="0" id="builtin:seattle_times" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:wsj_free" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:wsj" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:wash_post" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:wash_post_print" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:san_fran_chronicle" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:nytfeeds" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:nyt_magazine" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:nytimes_cooking" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:nytimesbook" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:lex_fridman_podcast" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:thenewcriterion" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:the_register" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:bbc_fast" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:american_thinker" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:barrons" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:bloomberg" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:nautilus" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:nymag" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:nypost" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:new_york_review_of_books" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:substack" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:cato" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <scheduled_recipe id="builtin:new_yorker" title="The New Yorker Magazine" last_downloaded="2026-03-29T11:00:40.291926+00:00"><schedule type="days_of_week">6:6:0</schedule></scheduled_recipe> - - <recipe_customization keep_issues="0" id="builtin:new_yorker" add_title_tag="yes" custom_tags="news" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:the_week_magazine_free" add_title_tag="yes" custom_tags="news" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:atlantic_com" add_title_tag="yes" custom_tags="news" recipe_specific_options="{}"/> - - <scheduled_recipe id="builtin:atlantic" title="The Atlantic" last_downloaded="2025-10-02T21:22:05.499182+00:00"><schedule type="days_of_month">24:6:0</schedule></scheduled_recipe> - - <recipe_customization keep_issues="0" id="builtin:atlantic" add_title_tag="yes" custom_tags="news" recipe_specific_options="{}"/> - - <scheduled_recipe id="builtin:harpers" title="Harper’s Magazine" last_downloaded="2025-10-02T21:21:56.402441+00:00"><schedule type="days_of_month">24:6:0</schedule></scheduled_recipe> - - <recipe_customization keep_issues="0" id="builtin:harpers" add_title_tag="yes" custom_tags="news" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:rushisaband" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:prospectmaguk_free" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:oxford_mail" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:lrb" add_title_tag="yes" custom_tags="news" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:interfax" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:ukrinform_en" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:nv_en" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:kyivpost_en" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:gagadget_en" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:google_news" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:moscow_times" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:novaya_gazeta_europe_en" add_title_tag="yes" custom_tags="news" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:coda" add_title_tag="yes" custom_tags="news" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:moscowtimes_en" add_title_tag="yes" custom_tags="news" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:t_invariant_en" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:sputnik" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:russiafeed" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:spiegel_int" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:haaretz_en" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:foreignaffairs" add_title_tag="yes" custom_tags="news" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:new_york_review_of_books_no_sub" add_title_tag="yes" custom_tags="news" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:phillosophy_now" add_title_tag="yes" custom_tags="news" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:scientific_american" add_title_tag="yes" custom_tags="news" recipe_specific_options="{"res": "500"}"/> - - <recipe_customization keep_issues="0" id="builtin:daily_writing_tips" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:economist_world_ahead" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:tls_mag" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:ars_technica" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:motherjones" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:time_magazine" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:wired" add_title_tag="yes" custom_tags="news" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:the_federalist" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:le_monde_en" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:deutsche_welle_en" add_title_tag="yes" custom_tags="" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="7" id="custom:1000" add_title_tag="yes" custom_tags="news" recipe_specific_options="{"web": "yes", "days": "1", "res": "mediumThreeByTwo440", "comp": "yes"}"/> - - <scheduled_recipe id="builtin:newrepublicmag" title="The New Republic Magazine" last_downloaded="2026-01-01T06:00:38.301817+00:00"><schedule type="days_of_month">1:0:0</schedule></scheduled_recipe> - - <recipe_customization keep_issues="0" id="builtin:newrepublicmag" add_title_tag="yes" custom_tags="news" recipe_specific_options="{}"/> - - <recipe_customization keep_issues="0" id="builtin:jacobinmag" add_title_tag="yes" custom_tags="news" recipe_specific_options="{}"/> - - <account_info id="builtin:lemonde_dip" username="craigmartinjennings@gmail.com" password="cmjDase1n"/> - - <scheduled_recipe id="builtin:lemonde_dip" title="Le Monde diplomatique - English edition" last_downloaded="2026-03-16T19:26:46.271595+00:00"><schedule type="interval">30.000000</schedule></scheduled_recipe> - - <recipe_customization keep_issues="0" id="builtin:lemonde_dip" add_title_tag="yes" custom_tags="news" recipe_specific_options="{}"/> - - <scheduled_recipe id="builtin:economist" title="The Economist" last_downloaded="2026-03-28T21:55:19.686930+00:00"><schedule type="days_of_week">5:6:0</schedule></scheduled_recipe> - - <recipe_customization keep_issues="7" id="builtin:economist" add_title_tag="yes" custom_tags="news" recipe_specific_options="{"res": "834"}"/> - - <recipe_customization keep_issues="5" id="builtin:nytimes_sub" add_title_tag="yes" custom_tags="news" recipe_specific_options="{"days": "1", "res": "mediumThreeByTwo440", "comp": "yes"}"/> - -</recipe_collection>
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/server-config.txt b/dotfiles/common/.config/calibre/server-config.txt deleted file mode 100644 index e69de29..0000000 --- a/dotfiles/common/.config/calibre/server-config.txt +++ /dev/null diff --git a/dotfiles/common/.config/calibre/server-custom-list-template.json b/dotfiles/common/.config/calibre/server-custom-list-template.json deleted file mode 100644 index f5cad2c..0000000 --- a/dotfiles/common/.config/calibre/server-custom-list-template.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "comments_fields": [ - "comments" - ], - "height": "auto", - "lines": [ - "<b>{title}</b> by {authors}", - "{series_index} of <i>{series}</i>|||{rating}", - "{tags}", - "Date: {timestamp}|||Published: {pubdate}|||Publisher: {publisher}" - ], - "thumbnail": true, - "thumbnail_height": 140 -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/server-search-the-net.json b/dotfiles/common/.config/calibre/server-search-the-net.json deleted file mode 100644 index 89dbf5a..0000000 --- a/dotfiles/common/.config/calibre/server-search-the-net.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - { - "name": "Z-Library", - "url": "https://z-library.sk/s/?q=Title%3A{title}%20author%3A{author}&languages[]=english&extensions[]=EPUB&extensions[]=PDF&selected_content_types[]=book&order=bestmatch", - "type": "book" - } -]
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/server-users.sqlite b/dotfiles/common/.config/calibre/server-users.sqlite Binary files differdeleted file mode 100644 index c191559..0000000 --- a/dotfiles/common/.config/calibre/server-users.sqlite +++ /dev/null diff --git a/dotfiles/common/.config/calibre/shortcuts/main.json b/dotfiles/common/.config/calibre/shortcuts/main.json deleted file mode 100644 index 3af458c..0000000 --- a/dotfiles/common/.config/calibre/shortcuts/main.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "map": { - "Interface Action: Edit Collections (Edit Collections) - qaction": [ - "Ctrl+Shift+C" - ], - "Interface Action: Extract ISBN (Extract ISBN) - qaction": [ - "Ctrl+I" - ], - "Interface Action: Quickview (Quickview) - qaction": [], - "quit calibre": [ - "Q" - ] - }, - "options_map": {} -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/smtp.py.json b/dotfiles/common/.config/calibre/smtp.py.json deleted file mode 100644 index 7258a9b..0000000 --- a/dotfiles/common/.config/calibre/smtp.py.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "accounts": { - "c@cjennings.net": [ - "EPUB,PDF", - false, - false - ], - "cciarmello@gmail.com": [ - "PDF", - false, - false - ], - "cjennings_oasis@kindle.com": [ - "EPUB", - false, - false - ], - "cjennings_paperwhite@kindle.com": [ - "EPUB", - false, - true - ], - "cjennings_pixel6@kindle.com": [ - "EPUB", - false, - false - ], - "laurajsmetanick@gmail.com": [ - "PDF", - false, - false - ], - "laurajsmetanick@kindle.com": [ - "EPUB, TPZ", - false, - false - ], - "lugrad2012_VxFH2q@kindle.com": [ - "EPUB, TPZ", - false, - false - ], - "rubyblu@kindle.com": [ - "EPUB,AZW3,MOBI", - false, - false - ] - }, - "aliases": { - "c@cjennings.net": "c@cjennings.net", - "cjennings_oasis@kindle.com": "Oasis", - "cjennings_paperwhite@kindle.com": "Paperwhite", - "cjennings_pixel6@kindle.com": "Pixel6", - "laurajsmetanick@gmail.com": "Laura's Direct Email", - "laurajsmetanick@kindle.com": "Laura's Kindle", - "lugrad2012_VxFH2q@kindle.com": "Laura's iPad Kindle", - "rubyblu@kindle.com": "Christine's Kindle" - }, - "encryption": "TLS", - "from_": "c@cjennings.net", - "relay_host": "127.0.0.1", - "relay_password": "35766e516c4b54364e4856725966522d4e4549553667", - "relay_port": 1025, - "relay_username": "c@cjennings.net", - "subjects": { - "c@cjennings.net": "Book: {title} - {author}", - "cciarmello@gmail.com": "{title}", - "cjennings_oasis@kindle.com": "{title}", - "cjennings_paperwhite@kindle.com": "{title}", - "cjennings_pixel6@kindle.com": "{title}", - "laurajsmetanick@gmail.com": "{title}", - "laurajsmetanick@kindle.com": "Book: {title} - {author}", - "lugrad2012_VxFH2q@kindle.com": "Book: {title} - {author}", - "rubyblu@kindle.com": "Book: {title} - {author}" - }, - "tags": {} -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/tag-map-rules.json b/dotfiles/common/.config/calibre/tag-map-rules.json deleted file mode 100644 index fa0deaf..0000000 --- a/dotfiles/common/.config/calibre/tag-map-rules.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "default": [ - { - "action": "remove", - "match_type": "not_one_of", - "query": "Art, Biography & Autobiography, Business, Chess, Comics, Computer, Cooking, Design, Economics, Fiction, Finance, Fitness, Games, Gardening, History, Latin, Law, Linguistics, Literary Critique, Literature, Magic, Mathematics, Music, Mythology, Non-Fiction, Philosophy, Poetry, Political Science, Politics, Psychology, Religion, Science, Social Critique, Sociology, Travel, Zen", - "replace": "" - } - ], - "default tags": [ - { - "action": "remove", - "match_type": "not_one_of", - "query": "Anthropology, Art, Biography & Autobiography, Business, Chess, Comedy, Comics, Computers, Cooking, Critical Theory, Design, DIY, Economics, Finance, Games, Gardening, Health, History, Language Learning, Law, Linguistics, Literary Critique, Literature, Magic, Mathematics, Music, Mythology, news, Non-Fiction, Philosophy, Poetry, Political Science, Politics, Psychology, Religion, Science, Social Critique, Sociology, Travel, Zen", - "replace": "" - } - ] -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/viewer-webengine.json b/dotfiles/common/.config/calibre/viewer-webengine.json deleted file mode 100644 index de97884..0000000 --- a/dotfiles/common/.config/calibre/viewer-webengine.json +++ /dev/null @@ -1,324 +0,0 @@ -{ - "geometry-of-main_window_geometry": { - "frame_geometry": { - "height": 1314, - "width": 1828, - "x": 0, - "y": 0 - }, - "full_screened": false, - "geometry": { - "height": 1314, - "width": 1828, - "x": 0, - "y": 0 - }, - "maximized": true, - "normal_geometry": { - "height": 1314, - "width": 1828, - "x": 0, - "y": 0 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAAAAAByMAAAUhAAAAAAAAAAAAAAcjAAAFIQAAAAACAAAADXAAAAAAAAAAAAAAByMAAAUh" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "local_storage": { - "search-bar-history-search-for-sc": [ - "black", - "dark", - "reverse", - "invert", - "quit" - ] - }, - "lookup_location": "Google dictionary", - "main_window_geometry": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAAAAAAAEwAABd8AAAPqAAAAAAAAABMAAAXfAAAD6gAAAAAAAAAABeAAAAAAAAAAEwAABd8AAAPq" - }, - "main_window_state": { - "__class__": "bytearray", - "__value__": "AAAA/wAAAAH9AAAAAgAAAAAAAAAAAAAAAPwCAAAAAvsAAAAQAHQAbwBjAC0AZABvAGMAawAAAAAA/////wAAAIoA////+wAAABYAcwBlAGEAcgBjAGgALQBkAG8AYwBrAAAAAAD/////AAAAmgD///8AAAABAAABeAAAA9X8AgAAAAT7AAAAFgBsAG8AbwBrAHUAcAAtAGQAbwBjAGsAAAAAAAAAA9UAAACDAP////sAAAAcAGIAbwBvAGsAbQBhAHIAawBzAC0AZABvAGMAawAAAAAAAAAD1QAAAPIA////+wAAABwAaQBuAHMAcABlAGMAdABvAHIALQBkAG8AYwBrAAAAAAD/////AAAAFAD////7AAAAHgBoAGkAZwBoAGwAaQBnAGgAdABzAC0AZABvAGMAawAAAAAA/////wAAANcA////AAAHJAAABSIAAAAEAAAABAAAAAgAAAAI/AAAAAEAAAAAAAAAAQAAAB4AYQBjAHQAaQBvAG4AcwBfAHQAbwBvAGwAYgBhAHICAAAAAP////8AAAAAAAAAAA==" - }, - "old_prefs_migrated": true, - "session_data": { - "base_font_size": 28, - "columns_per_screen": { - "landscape": 1, - "portrait": 1 - }, - "controls_help_shown_count": 2, - "current_color_scheme": "black", - "keyboard_shortcuts": { - "quit": [ - { - "altKey": false, - "ctrlKey": false, - "key": "q", - "metaKey": false, - "shiftKey": false - } - ] - }, - "margin_bottom": 100, - "margin_left": 100, - "margin_right": 100, - "margin_top": 100, - "read_mode": "flow", - "standalone_font_settings": { - "minimum_font_size": 12, - "mono_family": "Berkeley Mono", - "sans_family": "Verdana", - "serif_family": "Merriweather" - }, - "standalone_misc_settings": { - "remember_last_read": true, - "remember_window_geometry": false, - "save_annotations_in_ebook": true, - "singleinstance": false - }, - "standalone_recently_opened": [ - { - "key": "/home/cjennings/projects/career/2026-02-27-socom-scope-planning-summary.docx", - "pathtoebook": "/home/cjennings/projects/career/2026-02-27-socom-scope-planning-summary.docx", - "timestamp": "2026-02-28T07:06:07.816Z", - "title": "U.S. Special Operations Command (SOCOM) Technical Experimentation TE26-2" - }, - { - "authors": [ - "Admin" - ], - "key": "/home/cjennings/documents/google-drive/danneel/Ltr- Owen (02.06.26) v.5.docx", - "pathtoebook": "/home/cjennings/documents/google-drive/danneel/Ltr- Owen (02.06.26) v.5.docx", - "timestamp": "2026-02-09T19:36:19.376Z", - "title": "Ltr- Owen (02.06.26) v.5" - }, - { - "authors": [ - "Craig Jennings" - ], - "key": "/home/cjennings/projects/career/tooling/tooling-proposal.docx", - "pathtoebook": "/home/cjennings/projects/career/tooling/tooling-proposal.docx", - "timestamp": "2026-02-09T16:06:27.024Z", - "title": "DeepSat SDLC Tooling Proposal" - }, - { - "authors": [ - "Richard Polt" - ], - "key": "/home/cjennings/sync/books/Richard Polt/Heidegger_ An Introduction (44728)/Heidegger_ An Introduction - Richard Polt.azw3", - "pathtoebook": "/home/cjennings/sync/books/Richard Polt/Heidegger_ An Introduction (44728)/Heidegger_ An Introduction - Richard Polt.azw3", - "timestamp": "2025-09-10T01:03:46.282Z", - "title": "Heidegger: An Introduction" - }, - { - "authors": [ - "David Harvey" - ], - "key": "/home/cjennings/sync/books/David Harvey/A Companion to Marx's Capital_ Volume 1 (44747)/A Companion to Marx's Capital_ Volume 1 - David Harvey.mobi", - "pathtoebook": "/home/cjennings/sync/books/David Harvey/A Companion to Marx's Capital_ Volume 1 (44747)/A Companion to Marx's Capital_ Volume 1 - David Harvey.mobi", - "timestamp": "2025-09-10T01:01:01.569Z", - "title": "A Companion to Marx's Capital: Volume 1" - }, - { - "authors": [ - "Quentin Skinner" - ], - "key": "/home/cjennings/sync/books/Quentin Skinner/Liberty before Liberalism (44222)/Liberty before Liberalism - Quentin Skinner.mobi", - "pathtoebook": "/home/cjennings/sync/books/Quentin Skinner/Liberty before Liberalism (44222)/Liberty before Liberalism - Quentin Skinner.mobi", - "timestamp": "2025-08-03T23:22:50.318Z", - "title": "Liberty before Liberalism" - }, - { - "authors": [ - "Saint Thomas Aquinas" - ], - "key": "/home/cjennings/sync/books/Thomas Aquinas/Commentary on Aristotle's Metaphysics (43871)/Commentary on Aristotle's Metaphysics - Thomas Aquinas.epub", - "pathtoebook": "/home/cjennings/sync/books/Thomas Aquinas/Commentary on Aristotle's Metaphysics (43871)/Commentary on Aristotle's Metaphysics - Thomas Aquinas.epub", - "timestamp": "2025-07-30T21:29:13.047Z", - "title": "Commentary on Aristotle's Metaphysics" - }, - { - "authors": [ - "Saint Thomas Aquinas" - ], - "key": "/home/cjennings/sync/books/Saint Thomas Aquinas/Commentary on Aristotle's Metaphysics (43871)/Commentary on Aristotle's Metaphysics - Saint Thomas Aquinas.epub", - "pathtoebook": "/home/cjennings/sync/books/Saint Thomas Aquinas/Commentary on Aristotle's Metaphysics (43871)/Commentary on Aristotle's Metaphysics - Saint Thomas Aquinas.epub", - "timestamp": "2025-07-30T08:21:23.202Z", - "title": "Commentary on Aristotle's Metaphysics" - }, - { - "authors": [ - "Desconocido" - ], - "key": "/home/cjennings/sync/books/Jill Vance Buroker/Kant's 'Critique of Pure Reason'_ An Introduction (43864)/Kant's 'Critique of Pure Reason'_ An Intro - Jill Vance Buroker.mobi", - "pathtoebook": "/home/cjennings/sync/books/Jill Vance Buroker/Kant's 'Critique of Pure Reason'_ An Introduction (43864)/Kant's 'Critique of Pure Reason'_ An Intro - Jill Vance Buroker.mobi", - "timestamp": "2025-07-18T04:13:33.770Z", - "title": "Kants Critique of Pure Reason An Introduction Cambridge Introductions to Key Philosophical Texts Cambridge" - }, - { - "authors": [ - "Leszek Kolakowski" - ], - "key": "/home/cjennings/sync/books/Leszek Kolakowski/Is God Happy__ Selected Essays (43040)/Is God Happy__ Selected Essays - Leszek Kolakowski.azw3", - "pathtoebook": "/home/cjennings/sync/books/Leszek Kolakowski/Is God Happy__ Selected Essays (43040)/Is God Happy__ Selected Essays - Leszek Kolakowski.azw3", - "timestamp": "2025-07-13T16:19:13.806Z", - "title": "Is God Happy?: Selected Essays (Penguin Modern Classics)" - }, - { - "authors": [ - "Desconocido" - ], - "key": "/home/cjennings/sync/books/Desconocido/Routledge Aristotle And The Metaphysics (43652)/Routledge Aristotle And The Metaphysics - Desconocido.azw3", - "pathtoebook": "/home/cjennings/sync/books/Desconocido/Routledge Aristotle And The Metaphysics (43652)/Routledge Aristotle And The Metaphysics - Desconocido.azw3", - "timestamp": "2025-07-06T21:55:18.416Z", - "title": "Routledge Aristotle And The Metaphysics" - }, - { - "authors": [ - "Habermas, Jürgen" - ], - "key": "/home/cjennings/sync/books/Habermas, Jurgen/The Philosophical Discourse of Modernity (40589)/The Philosophical Discourse of Modernity - Habermas, Jurgen.epub", - "pathtoebook": "/home/cjennings/sync/books/Habermas, Jurgen/The Philosophical Discourse of Modernity (40589)/The Philosophical Discourse of Modernity - Habermas, Jurgen.epub", - "timestamp": "2024-12-13T02:38:28.792Z", - "title": "The Philosophical Discourse of Modernity" - }, - { - "authors": [ - "Tamsyn Muir" - ], - "key": "/home/cjennings/sync/books/Tamsyn Muir/Gideon the Ninth (40289)/Gideon the Ninth - Tamsyn Muir.epub", - "pathtoebook": "/home/cjennings/sync/books/Tamsyn Muir/Gideon the Ninth (40289)/Gideon the Ninth - Tamsyn Muir.epub", - "timestamp": "2024-11-15T19:06:33.047Z", - "title": "Gideon the Ninth" - }, - { - "key": "/home/cjennings/.local/opt/tor-browser/app/Browser/downloads/Love and Rockets #1 (1981) [Pyramid].cbz", - "pathtoebook": "/home/cjennings/.local/opt/tor-browser/app/Browser/downloads/Love and Rockets #1 (1981) [Pyramid].cbz", - "timestamp": "2022-08-23T16:40:22.898Z", - "title": "Love and Rockets #1 (1981) [Pyramid]" - }, - { - "key": "/home/cjennings/.local/opt/tor-browser/app/Browser/downloads/Love & Rockets v1 #05 (March 1984) [Cclay].cbr", - "pathtoebook": "/home/cjennings/.local/opt/tor-browser/app/Browser/downloads/Love & Rockets v1 #05 (March 1984) [Cclay].cbr", - "timestamp": "2022-08-23T16:40:04.599Z", - "title": "Love & Rockets v1 #05 (March 1984) [Cclay]" - }, - { - "key": "/tmp/mozilla_cjennings0/Love & Rockets v1 #05 (March 1984) [Cclay].cbr", - "pathtoebook": "/tmp/mozilla_cjennings0/Love & Rockets v1 #05 (March 1984) [Cclay].cbr", - "timestamp": "2022-08-23T16:31:27.722Z", - "title": "Love & Rockets v1 #05 (March 1984) [Cclay]" - }, - { - "authors": [ - "George Grätzer" - ], - "key": "/home/cjennings/Library/George Gratzer/More Math Into LaTeX (27737)/More Math Into LaTeX - George Gratzer.mobi", - "pathtoebook": "/home/cjennings/Library/George Gratzer/More Math Into LaTeX (27737)/More Math Into LaTeX - George Gratzer.mobi", - "timestamp": "2022-01-14T10:36:05.803Z", - "title": "More Math Into LaTeX" - }, - { - "authors": [ - "Simenon Georges" - ], - "key": "/home/cjennings/Library/Simenon Georges/050 Maigret's Little Joke (27730)/050 Maigret's Little Joke - Simenon Georges.mobi", - "pathtoebook": "/home/cjennings/Library/Simenon Georges/050 Maigret's Little Joke (27730)/050 Maigret's Little Joke - Simenon Georges.mobi", - "timestamp": "2022-01-10T12:32:52.530Z", - "title": "050 Maigret's Little Joke" - }, - { - "authors": [ - "Will Durant" - ], - "key": "/home/cjennings/Library/Will Durant/Story of Philosophy (3224)/Story of Philosophy - Will Durant.azw3", - "pathtoebook": "/home/cjennings/Library/Will Durant/Story of Philosophy (3224)/Story of Philosophy - Will Durant.azw3", - "timestamp": "2022-01-05T19:33:13.710Z", - "title": "Story of Philosophy" - }, - { - "authors": [ - "P G Wodehouse" - ], - "key": "/home/cjennings/Library/P. G. Wodehouse/Laughing Gas (24469)/Laughing Gas - P. G. Wodehouse.mobi", - "pathtoebook": "/home/cjennings/Library/P. G. Wodehouse/Laughing Gas (24469)/Laughing Gas - P. G. Wodehouse.mobi", - "timestamp": "2022-01-03T00:51:21.126Z", - "title": "Laughing Gas" - }, - { - "authors": [ - "Peter Seibel" - ], - "key": "/home/cjennings/Library/Peter Seibel/Coders at Work_ Reflections on the Craft of Programming (316)/Coders at Work_ Reflections on the Craft o - Peter Seibel.htmlz", - "pathtoebook": "/home/cjennings/Library/Peter Seibel/Coders at Work_ Reflections on the Craft of Programming (316)/Coders at Work_ Reflections on the Craft o - Peter Seibel.htmlz", - "timestamp": "2022-01-03T00:38:17.903Z", - "title": "Coders at Work" - }, - { - "authors": [ - "by Mike Gancarz" - ], - "key": "/home/cjennings/Downloads/torrents/files/Linux and the Unix Philosophy by Mike Gancarz (z-lib.org).epub", - "pathtoebook": "/home/cjennings/Downloads/torrents/files/Linux and the Unix Philosophy by Mike Gancarz (z-lib.org).epub", - "timestamp": "2022-01-02T23:44:59.829Z", - "title": "4362" - }, - { - "authors": [ - "Margaret Dauler Wilson" - ], - "key": "/home/cjennings/Library/Margaret Dauler Wilson/Descartes (86)/Descartes - Margaret Dauler Wilson.mobi", - "pathtoebook": "/home/cjennings/Library/Margaret Dauler Wilson/Descartes (86)/Descartes - Margaret Dauler Wilson.mobi", - "timestamp": "2022-01-02T14:20:51.792Z", - "title": "Descartes (Arguments of the Philosophers)" - }, - { - "authors": [ - "Alexander Tarlinder" - ], - "key": "/home/cjennings/Library/Alexander Tarlinder/Developer Testing_ Building Quality Into Software (26)/Developer Testing_ Building Quality Into S - Alexander Tarlinder.azw3", - "pathtoebook": "/home/cjennings/Library/Alexander Tarlinder/Developer Testing_ Building Quality Into Software (26)/Developer Testing_ Building Quality Into S - Alexander Tarlinder.azw3", - "timestamp": "2022-01-02T03:53:52.454Z", - "title": "Developer Testing: Building Quality into Software (Addison-Wesley Signature Series (Cohn))" - }, - { - "authors": [ - "Dieter Lohmar, Jagna Brudzinska" - ], - "key": "/home/cjennings/Library/Dieter Lohmar/Founding Psychoanalysis Phenomenologically_ Phenomenological Theory of Subjectivity and the Ps (17064)/Founding Psychoanalysis Phenomenologically - Dieter Lohmar.pdf", - "pathtoebook": "/home/cjennings/Library/Dieter Lohmar/Founding Psychoanalysis Phenomenologically_ Phenomenological Theory of Subjectivity and the Ps (17064)/Founding Psychoanalysis Phenomenologically - Dieter Lohmar.pdf", - "timestamp": "2022-01-01T22:55:44.420Z", - "title": "Founding Psychoanalysis Phenomenologically: Phenomenological Theory of Subjectivity and the Psychoanalytic Experience (Phaenomenologica, 199)" - } - ] - } -}
\ No newline at end of file diff --git a/dotfiles/common/.config/calibre/viewer.json b/dotfiles/common/.config/calibre/viewer.json deleted file mode 100644 index 7a41659..0000000 --- a/dotfiles/common/.config/calibre/viewer.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "geometry-of-print-to-pdf-geometry": { - "frame_geometry": { - "height": 285, - "width": 510, - "x": 649, - "y": 475 - }, - "full_screened": false, - "geometry": { - "height": 285, - "width": 510, - "x": 649, - "y": 475 - }, - "maximized": true, - "normal_geometry": { - "height": 285, - "width": 510, - "x": 649, - "y": 475 - }, - "qt": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAKJAAAB2wAABIYAAAL3AAACiQAAAdsAAASGAAAC9wAAAAACAAAADXAAAAKJAAAB2wAABIYAAAL3" - }, - "screen": { - "depth": 32, - "device_pixel_ratio": 1.0, - "geometry_in_logical_pixels": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - }, - "index_in_screens_list": 0, - "manufacturer": "Dell Inc.", - "model": "DELL U3419W", - "name": "DP-4", - "serial": "", - "size_in_logical_pixels": { - "height": 1440, - "width": 3440 - }, - "virtual_geometry": { - "height": 1440, - "width": 3440, - "x": 0, - "y": 0 - } - } - }, - "print-to-pdf-bottom-margin": 1.0, - "print-to-pdf-geometry": { - "__class__": "bytearray", - "__value__": "AdnQywADAAAAAAEjAAAA7AAAAyQAAAIpAAABJQAAAO4AAAMiAAACJwAAAAAAAAAABVYAAAElAAAA7gAAAyIAAAIn" - }, - "print-to-pdf-left-margin": 1.0, - "print-to-pdf-page-numbers": false, - "print-to-pdf-page-size": "letter", - "print-to-pdf-right-margin": 1.0, - "print-to-pdf-show-file": true, - "print-to-pdf-top-margin": 1.0 -}
\ No newline at end of file diff --git a/dotfiles/common/.config/dunst/dunstrc b/dotfiles/common/.config/dunst/dunstrc deleted file mode 100644 index bcfa533..0000000 --- a/dotfiles/common/.config/dunst/dunstrc +++ /dev/null @@ -1,74 +0,0 @@ -# Dunst config - Dupre theme -# Warm, earthy colors from dupre-theme.el - -[global] -# Display -follow = mouse -width = 350 -height = (0, 300) -origin = top-right -offset = (35, 35) -indicate_hidden = yes -notification_limit = 5 -gap_size = 12 -padding = 12 -horizontal_padding = 20 -frame_width = 0 -sort = no -corner_radius = 10 - -# Progress bar -progress_bar_frame_width = 0 -progress_bar_corner_radius = 3 - -# Colors - Dupre theme -foreground = "#f0fef0" -frame_color = "#d7af5f" -highlight = "#d7af5f, #ccc768" - -# Text (size in points, Pango syntax) -font = BerkeleyMono Nerd Font 10 -markup = full -format = "<small>%a</small>\n<b>%s</b>\n%b" -alignment = left -vertical_alignment = center -show_age_threshold = -1 -hide_duplicate_count = false - -# Icon -icon_position = left -min_icon_size = 54 -max_icon_size = 80 -icon_corner_radius = 4 -icon_theme = "Papirus-Dark" -enable_recursive_icon_lookup = true - -# Misc/Advanced -dmenu = fuzzel --dmenu --prompt 'Open with' - -# Mouse -mouse_left_click = close_current -mouse_middle_click = do_action, close_current -mouse_right_click = close_all - -[urgency_low] -background = "#252321f0" -timeout = 3 - -[urgency_normal] -background = "#252321f0" -timeout = 8 - -[urgency_critical] -background = "#7c2a09f0" -frame_color = "#d47c59" -highlight = "#ff2a00" -foreground = "#f0fef0" -timeout = 0 - -[fullscreen_delay_everything] -fullscreen = delay - -[fullscreen_show_critical] -msg_urgency = critical -fullscreen = show diff --git a/dotfiles/common/.config/environment.d/envvars.conf b/dotfiles/common/.config/environment.d/envvars.conf deleted file mode 100644 index f937aab..0000000 --- a/dotfiles/common/.config/environment.d/envvars.conf +++ /dev/null @@ -1 +0,0 @@ -PATH=/usr/local/bin:/usr/bin:/bin:$HOME/.local/bin:/usr/sbin:$HOME/.config/rofi/scripts diff --git a/dotfiles/common/.config/feh/keys b/dotfiles/common/.config/feh/keys deleted file mode 100644 index 3cdea0b..0000000 --- a/dotfiles/common/.config/feh/keys +++ /dev/null @@ -1,6 +0,0 @@ -# feh key bindings -# Format: action key1 [key2 [key3]] - -# Zoom: add +/- to existing Up/Down bindings -zoom_in plus equal Up -zoom_out minus Down KP_Subtract diff --git a/dotfiles/common/.config/fontconfig/fonts.conf b/dotfiles/common/.config/fontconfig/fonts.conf deleted file mode 100644 index 6a27675..0000000 --- a/dotfiles/common/.config/fontconfig/fonts.conf +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE fontconfig SYSTEM "fonts.dtd"> -<fontconfig> - <match target="font"> - <edit name="antialias" mode="assign"> - <bool>true</bool> - </edit> - <edit name="hinting" mode="assign"> - <bool>true</bool> - </edit> - <edit name="hintstyle" mode="assign"> - <const>hintslight</const> - </edit> - <edit name="rgba" mode="assign"> - <const>none</const> - </edit> - <edit name="autohint" mode="assign"> - <bool>true</bool> - </edit> - <edit name="lcdfilter" mode="assign"> - <const>lcdnone</const> - </edit> - <edit name="dpi" mode="assign"> - <double>144</double> - </edit> - </match> -</fontconfig> diff --git a/dotfiles/common/.config/gtk-3.0/gtk.css b/dotfiles/common/.config/gtk-3.0/gtk.css deleted file mode 100644 index cffaf78..0000000 --- a/dotfiles/common/.config/gtk-3.0/gtk.css +++ /dev/null @@ -1,6 +0,0 @@ -.window-frame, .window-frame:backdrop { - box-shadow: 0 0 0 black; /* removes shadow completely */ - border-style: none; - margin: 1px; /* this retains the ability to resize with the mouse, if 1px is too narrow, set some higher values */ - border-radius: 0; -} diff --git a/dotfiles/common/.config/gtk-3.0/settings.ini b/dotfiles/common/.config/gtk-3.0/settings.ini deleted file mode 100644 index 5b2ba74..0000000 --- a/dotfiles/common/.config/gtk-3.0/settings.ini +++ /dev/null @@ -1,18 +0,0 @@ -[Settings] -gtk-print-backends=file,cups,pdf -gtk-theme-name=Adwaita-dark -gtk-icon-theme-name=Papirus-Dark -gtk-font-name=Cantarell 11 -gtk-cursor-theme-name=Bibata-Modern-Ice -gtk-cursor-theme-size=24 -gtk-toolbar-style=GTK_TOOLBAR_BOTH -gtk-toolbar-icon-size=GTK_ICON_SIZE_LARGE_TOOLBAR -gtk-button-images=1 -gtk-menu-images=1 -gtk-enable-event-sounds=1 -gtk-enable-input-feedback-sounds=1 -gtk-xft-antialias=1 -gtk-xft-hinting=1 -gtk-xft-hintstyle=hintfull -gtk-xft-rgba=rgb -gtk-application-prefer-dark-theme=1
\ No newline at end of file diff --git a/dotfiles/common/.config/htop/htoprc b/dotfiles/common/.config/htop/htoprc deleted file mode 100644 index 06b580d..0000000 --- a/dotfiles/common/.config/htop/htoprc +++ /dev/null @@ -1,63 +0,0 @@ -# Beware! This file is rewritten by htop when settings are changed in the interface. -# The parser is also very primitive, and not human-friendly. -htop_version=3.2.2 -config_reader_min_version=3 -fields=0 48 17 18 38 39 40 2 46 47 49 1 -hide_kernel_threads=1 -hide_userland_threads=0 -hide_running_in_container=0 -shadow_other_users=0 -show_thread_names=0 -show_program_path=1 -highlight_base_name=0 -highlight_deleted_exe=1 -shadow_distribution_path_prefix=0 -highlight_megabytes=1 -highlight_threads=1 -highlight_changes=0 -highlight_changes_delay_secs=5 -find_comm_in_cmdline=1 -strip_exe_from_cmdline=1 -show_merged_command=0 -header_margin=1 -screen_tabs=1 -detailed_cpu_time=0 -cpu_count_from_one=0 -show_cpu_usage=1 -show_cpu_frequency=0 -show_cpu_temperature=0 -degree_fahrenheit=0 -update_process_names=0 -account_guest_in_cpu_meter=0 -color_scheme=0 -enable_mouse=1 -delay=15 -hide_function_bar=0 -header_layout=two_50_50 -column_meters_0=AllCPUs Memory Swap -column_meter_modes_0=1 1 1 -column_meters_1=Tasks LoadAverage Uptime -column_meter_modes_1=2 2 2 -tree_view=0 -sort_key=46 -tree_sort_key=0 -sort_direction=-1 -tree_sort_direction=1 -tree_view_always_by_pid=0 -all_branches_collapsed=0 -screen:Main=PID USER PRIORITY NICE M_VIRT M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command -.sort_key=PERCENT_CPU -.tree_sort_key=PID -.tree_view=0 -.tree_view_always_by_pid=0 -.sort_direction=-1 -.tree_sort_direction=1 -.all_branches_collapsed=0 -screen:I/O=PID USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE PERCENT_SWAP_DELAY PERCENT_IO_DELAY Command -.sort_key=IO_RATE -.tree_sort_key=PID -.tree_view=0 -.tree_view_always_by_pid=0 -.sort_direction=-1 -.tree_sort_direction=1 -.all_branches_collapsed=0 diff --git a/dotfiles/common/.config/mc/panels.ini b/dotfiles/common/.config/mc/panels.ini deleted file mode 100644 index e69de29..0000000 --- a/dotfiles/common/.config/mc/panels.ini +++ /dev/null diff --git a/dotfiles/common/.config/mimeapps.list b/dotfiles/common/.config/mimeapps.list deleted file mode 100644 index 6a310c5..0000000 --- a/dotfiles/common/.config/mimeapps.list +++ /dev/null @@ -1,215 +0,0 @@ - -[Default Applications] - -# Web browser -text/html=firefox.desktop -x-scheme-handler/http=firefox.desktop -x-scheme-handler/https=firefox.desktop -x-scheme-handler/about=firefox.desktop -x-scheme-handler/unknown=firefox.desktop -application/rdf+xml=firefox.desktop -application/rss+xml=firefox.desktop -application/xhtml+xml=firefox.desktop -application/xhtml_xml=firefox.desktop - -# File manager -inode/directory=org.gnome.Nautilus.desktop - -# PDF, ePub, comics, documents (zathura) -application/pdf=org.pwmt.zathura.desktop -application/epub+zip=org.pwmt.zathura.desktop -application/x-mobipocket-ebook=org.pwmt.zathura.desktop -application/vnd.comicbook+zip=org.pwmt.zathura.desktop -application/vnd.comicbook-rar=org.pwmt.zathura.desktop -application/eps=org.pwmt.zathura.desktop -application/postscript=org.pwmt.zathura.desktop -application/oxps=org.pwmt.zathura.desktop -application/x-fictionbook=org.pwmt.zathura.desktop -image/eps=org.pwmt.zathura.desktop -image/vnd.djvu=org.pwmt.zathura.desktop -image/vnd.djvu+multipage=org.pwmt.zathura.desktop - -# eBooks (calibre) -application/x-mobi8-ebook=calibre-ebook-viewer.desktop - -# Images (feh) -image/bmp=feh.desktop -image/gif=feh.desktop -image/jpeg=feh.desktop -image/jpg=feh.desktop -image/pjpeg=feh.desktop -image/png=feh.desktop -image/tiff=feh.desktop -image/webp=feh.desktop -image/avif=feh.desktop -image/heic=feh.desktop -image/heif=feh.desktop -image/svg+xml=feh.desktop -image/x-bmp=feh.desktop -image/x-eps=feh.desktop -image/x-ico=feh.desktop -image/x-icon=feh.desktop -image/x-pcx=feh.desktop -image/x-png=feh.desktop -image/x-portable-anymap=feh.desktop -image/x-portable-bitmap=feh.desktop -image/x-portable-graymap=feh.desktop -image/x-portable-pixmap=feh.desktop -image/x-tga=feh.desktop -image/x-xbitmap=feh.desktop -image/x-xpixmap=feh.desktop - -# Video (mpv) -video/mp4=mpv.desktop -video/mpeg=mpv.desktop -video/ogg=mpv.desktop -video/webm=mpv.desktop -video/x-matroska=mpv.desktop -video/x-msvideo=mpv.desktop -video/quicktime=mpv.desktop -video/x-flv=mpv.desktop -video/3gpp=mpv.desktop -video/x-ms-wmv=mpv.desktop -video/avi=mpv.desktop -video/divx=mpv.desktop -video/flv=mpv.desktop -video/mp4v-es=mpv.desktop -video/mpeg-system=mpv.desktop -video/msvideo=mpv.desktop -video/vnd.divx=mpv.desktop -video/vnd.mpegurl=mpv.desktop -video/vnd.rn-realvideo=mpv.desktop -video/x-avi=mpv.desktop -video/x-m4v=mpv.desktop -video/x-mpeg=mpv.desktop -video/x-mpeg-system=mpv.desktop -video/x-mpeg2=mpv.desktop -video/x-theora=mpv.desktop -video/x-theora+ogg=mpv.desktop -x-content/video-dvd=mpv.desktop -x-content/video-svcd=mpv.desktop -x-content/video-vcd=mpv.desktop -application/ogg=mpv.desktop -application/vnd.apple.mpegurl=mpv.desktop - -# Audio (mpv) -audio/mpeg=mpv.desktop -audio/ogg=mpv.desktop -audio/flac=mpv.desktop -audio/x-wav=mpv.desktop -audio/mp4=mpv.desktop -audio/basic=mpv.desktop -audio/opus=mpv.desktop -audio/vnd.rn-realaudio=mpv.desktop -audio/vnd.wav=mpv.desktop -audio/vorbis=mpv.desktop -audio/x-aiff=mpv.desktop -audio/x-mpegurl=mpv.desktop - -# Text and source code (emacsclient) -text/plain=emacsclient.desktop -text/english=emacsclient.desktop -text/x-readme=emacsclient.desktop -text/x-csrc=emacsclient.desktop -text/x-chdr=emacsclient.desktop -text/x-c=emacsclient.desktop -text/x-c++=emacsclient.desktop -text/x-c++src=emacsclient.desktop -text/x-c++hdr=emacsclient.desktop -text/x-java=emacsclient.desktop -text/x-python=emacsclient.desktop -text/x-ruby=emacsclient.desktop -text/x-rust=emacsclient.desktop -text/x-go=emacsclient.desktop -text/x-lua=emacsclient.desktop -text/x-perl=emacsclient.desktop -text/x-haskell=emacsclient.desktop -text/x-moc=emacsclient.desktop -text/x-pascal=emacsclient.desktop -text/x-tcl=emacsclient.desktop -text/javascript=emacsclient.desktop -application/javascript=emacsclient.desktop -application/x-typescript=emacsclient.desktop -text/vnd.trolltech.linguist=emacsclient.desktop -text/css=emacsclient.desktop -application/json=emacsclient.desktop -application/xml=emacsclient.desktop -text/xml=emacsclient.desktop -text/x-scss=emacsclient.desktop -text/x-sass=emacsclient.desktop -application/x-shellscript=emacsclient.desktop -text/x-makefile=emacsclient.desktop -text/x-cmake=emacsclient.desktop -application/toml=emacsclient.desktop -application/x-yaml=emacsclient.desktop -text/x-ini=emacsclient.desktop -text/x-systemd-unit=emacsclient.desktop -text/markdown=emacsclient.desktop -text/org=emacsclient.desktop -text/x-tex=emacsclient.desktop -text/x-latex=emacsclient.desktop -application/x-desktop=emacsclient.desktop -text/x-diff=emacsclient.desktop -text/x-patch=emacsclient.desktop -text/x-sql=emacsclient.desktop -text/x-lisp=emacsclient.desktop -text/x-emacs-lisp=emacsclient.desktop -text/x-scheme=emacsclient.desktop -text/x-log=emacsclient.desktop -text/x-nfo=emacsclient.desktop -application/x-docbook+xml=emacsclient.desktop -text/x-dockerfile=emacsclient.desktop -application/x-wine-extension-ini=emacsclient.desktop - -# Spreadsheets (libreoffice calc) -application/vnd.ms-excel=libreoffice-calc.desktop -application/vnd.openxmlformats-officedocument.spreadsheetml.sheet=libreoffice-calc.desktop -application/vnd.oasis.opendocument.spreadsheet=libreoffice-calc.desktop -application/vnd.oasis.opendocument.spreadsheet-template=libreoffice-calc.desktop -text/csv=libreoffice-calc.desktop -text/tab-separated-values=libreoffice-calc.desktop - -# Presentations (libreoffice impress) -application/vnd.ms-powerpoint=libreoffice-impress.desktop -application/vnd.openxmlformats-officedocument.presentationml.presentation=libreoffice-impress.desktop -application/vnd.oasis.opendocument.presentation=libreoffice-impress.desktop -application/vnd.oasis.opendocument.presentation-template=libreoffice-impress.desktop - -# Word processing (libreoffice writer) -application/msword=libreoffice-writer.desktop -application/vnd.ms-word=libreoffice-writer.desktop -application/vnd.openxmlformats-officedocument.wordprocessingml.document=libreoffice-writer.desktop -application/vnd.oasis.opendocument.text=libreoffice-writer.desktop -application/vnd.oasis.opendocument.text-template=libreoffice-writer.desktop -application/rtf=libreoffice-writer.desktop -text/rtf=libreoffice-writer.desktop - -# Archives (file-roller) -application/zip=org.gnome.FileRoller.desktop -application/x-tar=org.gnome.FileRoller.desktop -application/gzip=org.gnome.FileRoller.desktop -application/x-gzip=org.gnome.FileRoller.desktop -application/x-compressed-tar=org.gnome.FileRoller.desktop -application/x-bzip2=org.gnome.FileRoller.desktop -application/x-bzip2-compressed-tar=org.gnome.FileRoller.desktop -application/x-xz=org.gnome.FileRoller.desktop -application/x-xz-compressed-tar=org.gnome.FileRoller.desktop -application/x-7z-compressed=org.gnome.FileRoller.desktop -application/x-rar=org.gnome.FileRoller.desktop -application/vnd.rar=org.gnome.FileRoller.desktop -application/x-zstd-compressed-tar=org.gnome.FileRoller.desktop - -# Torrents (transmission) -application/x-bittorrent=io.github.TransmissionRemoteGtk.desktop -x-scheme-handler/magnet=io.github.TransmissionRemoteGtk.desktop - -# FTP (filezilla) -x-scheme-handler/ftp=filezilla.desktop - -# Org-protocol (emacsclient) -x-scheme-handler/org-protocol=org-protocol.desktop - -# Signal -x-scheme-handler/sgnl=signal.desktop -x-scheme-handler/signalcaptcha=signal.desktop -x-scheme-handler/claude-cli=claude-code-url-handler.desktop diff --git a/dotfiles/common/.config/mpd/mpd.conf b/dotfiles/common/.config/mpd/mpd.conf deleted file mode 100644 index 89a5d76..0000000 --- a/dotfiles/common/.config/mpd/mpd.conf +++ /dev/null @@ -1,437 +0,0 @@ -# An example configuration file for MPD. -# Read the user manual for documentation: http://www.musicpd.org/doc/user/ -# or /usr/share/doc/mpd/user-manual.html - - -# Files and directories ####################################################### -# -# This setting controls the top directory which MPD will search to discover the -# available audio files and add them to the daemon's online database. This -# setting defaults to the XDG directory, otherwise the music directory will be -# be disabled and audio files will only be accepted over ipc socket (using -# file:// protocol) or streaming files over an accepted protocol. -# -music_directory "/home/cjennings/music" -# -# This setting sets the MPD internal playlist directory. The purpose of this -# directory is storage for playlists created by MPD. The server will use -# playlist files not created by the server but only if they are in the MPD -# format. This setting defaults to playlist saving being disabled. -# -playlist_directory "/home/cjennings/music" -# -# This setting sets the location of the MPD database. This file is used to -# load the database at server start up and store the database while the -# server is not up. This setting defaults to disabled which will allow -# MPD to accept files over ipc socket (using file:// protocol) or streaming -# files over an accepted protocol. -# -db_file "/home/cjennings/.config/mpd/database" -# -# These settings are the locations for the daemon log files for the daemon. -# These logs are great for troubleshooting, depending on your log_level -# settings. -# -# The special value "syslog" makes MPD use the local syslog daemon. This -# setting defaults to logging to syslog, otherwise logging is disabled. -# -log_file "/home/cjennings/.config/mpd/log" -# -# This setting sets the location of the file which stores the process ID -# for use of mpd --kill and some init scripts. This setting is disabled by -# default and the pid file will not be stored. -# -pid_file "/home/cjennings/.config/mpd/pid" -# -# This setting sets the location of the file which contains information about -# most variables to get MPD back into the same general shape it was in before -# it was brought down. This setting is disabled by default and the server -# state will be reset on server start up. -# -state_file "/home/cjennings/.config/mpd/state" -# -# The location of the sticker database. This is a database which -# manages dynamic information attached to songs. -# -sticker_file "/home/cjennings/.config/mpd/sticker.sql" -# -############################################################################### - - -# General music daemon options ################################################ -# -# This setting specifies the user that MPD will run as. MPD should never run as -# root and you may use this setting to make MPD change its user ID after -# initialization. This setting is disabled by default and MPD is run as the -# current user. -# -user "cjennings" -# -# This setting specifies the group that MPD will run as. If not specified -# primary group of user specified with "user" setting will be used (if set). -# This is useful if MPD needs to be a member of group such as "audio" to -# have permission to use sound card. -# -#group "nogroup" -# -# This setting sets the address for the daemon to listen on. Careful attention -# should be paid if this is assigned to anything other then the default, any. -# This setting can deny access to control of the daemon. Choose any if you want -# to have mpd listen on every address. Not effective if systemd socket -# activation is in use. -# -# For network -# bind_to_address "0.0.0.0" -# -# And for Unix Socket -# bind_to_address "/home/cjennings/.config/mpd/socket" -bind_to_address "127.0.0.1" -# -# This setting is the TCP port that is desired for the daemon to get assigned -# to. -# -port "6600" -# -# This setting controls the type of information which is logged. Available -# setting arguments are "default", "secure" or "verbose". The "verbose" setting -# argument is recommended for troubleshooting, though can quickly stretch -# available resources on limited hardware storage. -# -log_level "default" -# -# If you have a problem with your MP3s ending abruptly it is recommended that -# you set this argument to "no" to attempt to fix the problem. If this solves -# the problem, it is highly recommended to fix the MP3 files with vbrfix -# (available as vbrfix in the debian archive), at which -# point gapless MP3 playback can be enabled. -# -#gapless_mp3_playback "yes" -# -# Setting "restore_paused" to "yes" puts MPD into pause mode instead -# of starting playback after startup. -# -restore_paused "yes" -# -# This setting enables MPD to create playlists in a format usable by other -# music players. -# -save_absolute_paths_in_playlists "yes" -# -# This setting defines a list of tag types that will be extracted during the -# audio file discovery process. The complete list of possible values can be -# found in the mpd.conf man page. -#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc" -# -# This setting enables automatic update of MPD's database when files in -# music_directory are changed. -# -auto_update "yes" -# -# Limit the depth of the directories being watched, 0 means only watch -# the music directory itself. There is no limit by default. -# -#auto_update_depth "3" -# -# Buffer settings for smooth streaming -audio_buffer_size "16384" # 16 MB buffer (default 4096 KB) -# -############################################################################### - - -# Symbolic link behavior ###################################################### -# -# If this setting is set to "yes", MPD will discover audio files by following -# symbolic links outside of the configured music_directory. -# -#follow_outside_symlinks "yes" -# -# If this setting is set to "yes", MPD will discover audio files by following -# symbolic links inside of the configured music_directory. -# -#follow_inside_symlinks "yes" -# -############################################################################### - - -# Zeroconf / Avahi Service Discovery ########################################## -# -# If this setting is set to "yes", service information will be published with -# Zeroconf / Avahi. -# -# zeroconf_enabled "yes" -# -# The argument to this setting will be the Zeroconf / Avahi unique name for -# this MPD server on the network. -# -# zeroconf_name "Music Player Daemon" -# -############################################################################### - - -# Permissions ################################################################# -# -# If this setting is set, MPD will require password authorization. The password -# can setting can be specified multiple times for different password profiles. -# -#password "password@read,add,control,admin" -# -# This setting specifies the permissions a user has who has not yet logged in. -# -#default_permissions "read,add,control,admin" -# -############################################################################### - - -# Database ####################################################################### -# - -#database { -# plugin "proxy" -# host "other.mpd.host" -# port "6600" -#} - -# Input ####################################################################### -# - -input { - plugin "curl" - timeout "30000" # 30 second timeout for connections -# proxy "proxy.isp.com:8080" -# proxy_user "user" -# proxy_password "password" -} - -# -############################################################################### - -# Audio Output ################################################################ -# -# MPD supports various audio output types, as well as playing through multiple -# audio outputs at the same time, through multiple audio_output settings -# blocks. Setting this block is optional, though the server will only attempt -# autodetection for one sound card. -# -# An example of an ALSA output: -# -#audio_output { -# type "alsa" -# name "My ALSA Device" -# device "hw:0,0" # optional -# mixer_type "hardware" # optional -# mixer_device "default" # optional -# mixer_control "PCM" # optional -# mixer_index "0" # optional -#} -# -# An example of an OSS output: -# -#audio_output { -# type "oss" -# name "My OSS Device" -# device "/dev/dsp" # optional -# mixer_type "hardware" # optional -# mixer_device "/dev/mixer" # optional -# mixer_control "PCM" # optional -#} -# -# An example of a shout output (for streaming to Icecast): -# -#audio_output { -# type "shout" -# encoding "ogg" # optional -# name "My Shout Stream" -# host "localhost" -# port "8000" -# mount "/mpd.ogg" -# password "hackme" -# quality "5.0" -# bitrate "128" -# format "44100:16:1" -# protocol "icecast2" # optional -# user "source" # optional -# description "My Stream Description" # optional -# url "http://example.com" # optional -# genre "jazz" # optional -# public "no" # optional -# timeout "2" # optional -# mixer_type "software" # optional -#} -# -# An example of a recorder output: -# -#audio_output { -# type "recorder" -# name "My recorder" -# encoder "vorbis" # optional, vorbis or lame -# path "/var/lib/mpd/recorder/mpd.ogg" -## quality "5.0" # do not define if bitrate is defined -# bitrate "128" # do not define if quality is defined -# format "44100:16:1" -#} -# -# An example of a httpd output (built-in HTTP streaming server): -# -#audio_output { -# type "httpd" -# name "My HTTP Stream" -# encoder "vorbis" # optional, vorbis or lame -# port "8000" -# bind_to_address "0.0.0.0" # optional, IPv4 or IPv6 -# quality "5.0" # do not define if bitrate is defined -# bitrate "128" # do not define if quality is defined -# format "44100:16:1" -# max_clients "0" # optional 0=no limit -#} -# -## cjennings 2021-06-26 - -audio_output { - type "pulse" - name "pulse audio" -} - -audio_output { - type "fifo" - name "my_fifo" - path "/tmp/mpd.fifo" - format "44100:16:2" -} -# An example of a pulseaudio output (streaming to a remote pulseaudio server) -# Please see README.Debian if you want mpd to play through the pulseaudio -# daemon started as part of your graphical desktop session! -# -#audio_output { -# type "pulse" -# name "My Pulse Output" -# server "remote_server" # optional -# sink "remote_server_sink" # optional -#} -# -# An example of a winmm output (Windows multimedia API). -# -#audio_output { -# type "winmm" -# name "My WinMM output" -# device "Digital Audio (S/PDIF) (High Definition Audio Device)" # optional -# or -# device "0" # optional -# mixer_type "hardware" # optional -#} -# -# An example of an openal output. -# -#audio_output { -# type "openal" -# name "My OpenAL output" -# device "Digital Audio (S/PDIF) (High Definition Audio Device)" # optional -#} -# -## Example "pipe" output: -# -#audio_output { -# type "pipe" -# name "my pipe" -# command "aplay -f cd 2>/dev/null" -## Or if you're want to use AudioCompress -# command "AudioCompress -m | aplay -f cd 2>/dev/null" -## Or to send raw PCM stream through PCM: -# command "nc example.org 8765" -# format "44100:16:2" -#} -# -## An example of a null output (for no audio output): -# -#audio_output { -# type "null" -# name "My Null Output" -# mixer_type "none" # optional -#} -# -# If MPD has been compiled with libsamplerate support, this setting specifies -# the sample rate converter to use. Possible values can be found in the -# mpd.conf man page or the libsamplerate documentation. By default, this is -# setting is disabled. -# -#samplerate_converter "Fastest Sinc Interpolator" -# -############################################################################### - - -# Normalization automatic volume adjustments ################################## -# -# This setting specifies the type of ReplayGain to use. This setting can have -# the argument "off", "album", "track" or "auto". "auto" is a special mode that -# chooses between "track" and "album" depending on the current state of -# random playback. If random playback is enabled then "track" mode is used. -# See <http://www.replaygain.org> for more details about ReplayGain. -# This setting is off by default. -# -replaygain "auto" -# -# This setting sets the pre-amp used for files that have ReplayGain tags. By -# default this setting is disabled. -# -#replaygain_preamp "0" -# -# This setting sets the pre-amp used for files that do NOT have ReplayGain tags. -# By default this setting is disabled. -# -#replaygain_missing_preamp "0" -# -# This setting enables or disables ReplayGain limiting. -# MPD calculates actual amplification based on the ReplayGain tags -# and replaygain_preamp / replaygain_missing_preamp setting. -# If replaygain_limit is enabled MPD will never amplify audio signal -# above its original level. If replaygain_limit is disabled such amplification -# might occur. By default this setting is enabled. -# -#replaygain_limit "yes" -# -# This setting enables on-the-fly normalization volume adjustment. This will -# result in the volume of all playing audio to be adjusted so the output has -# equal "loudness". This setting is disabled by default. -# -volume_normalization "yes" -# -############################################################################### - - -# Character Encoding ########################################################## -# -# If file or directory names do not display correctly for your locale then you -# may need to modify this setting. -# -filesystem_charset "UTF-8" -# -# This setting controls the encoding that ID3v1 tags should be converted from. -# -# id3v1_encoding "UTF-8" (this is now deprecated) -# -############################################################################### - - -# SIDPlay decoder ############################################################# -# -# songlength_database: -# Location of your songlengths file, as distributed with the HVSC. -# The sidplay plugin checks this for matching MD5 fingerprints. -# See http://www.c64.org/HVSC/DOCUMENTS/Songlengths.faq -# -# default_songlength: -# This is the default playing time in seconds for songs not in the -# songlength database, or in case you're not using a database. -# A value of 0 means play indefinitely. -# -# filter: -# Turns the SID filter emulation on or off. -# -#decoder { -# plugin "sidplay" -# songlength_database "/media/C64Music/DOCUMENTS/Songlengths.txt" -# default_songlength "120" -# filter "true" -#} -# -############################################################################### - diff --git a/dotfiles/common/.config/mpd/musicpd.conf b/dotfiles/common/.config/mpd/musicpd.conf deleted file mode 100644 index 9f34c44..0000000 --- a/dotfiles/common/.config/mpd/musicpd.conf +++ /dev/null @@ -1,436 +0,0 @@ -# An example configuration file for MPD. -# Read the user manual for documentation: http://www.musicpd.org/doc/user/ -# or /usr/share/doc/mpd/user-manual.html - - -# Files and directories ####################################################### -# -# This setting controls the top directory which MPD will search to discover the -# available audio files and add them to the daemon's online database. This -# setting defaults to the XDG directory, otherwise the music directory will be -# be disabled and audio files will only be accepted over ipc socket (using -# file:// protocol) or streaming files over an accepted protocol. -# -music_directory "~cjennings/music" -# -# This setting sets the MPD internal playlist directory. The purpose of this -# directory is storage for playlists created by MPD. The server will use -# playlist files not created by the server but only if they are in the MPD -# format. This setting defaults to playlist saving being disabled. -# -playlist_directory "~cjennings/music" -# -# This setting sets the location of the MPD database. This file is used to -# load the database at server start up and store the database while the -# server is not up. This setting defaults to disabled which will allow -# MPD to accept files over ipc socket (using file:// protocol) or streaming -# files over an accepted protocol. -# -db_file "~cjennings/.config/mpd/database" -# -# These settings are the locations for the daemon log files for the daemon. -# These logs are great for troubleshooting, depending on your log_level -# settings. -# -# The special value "syslog" makes MPD use the local syslog daemon. This -# setting defaults to logging to syslog, otherwise logging is disabled. -# -log_file "~cjennings/.config/mpd/mpd.log" -# -# This setting sets the location of the file which stores the process ID -# for use of mpd --kill and some init scripts. This setting is disabled by -# default and the pid file will not be stored. -# -pid_file "~cjennings/.config/mpd/pid" -# -# This setting sets the location of the file which contains information about -# most variables to get MPD back into the same general shape it was in before -# it was brought down. This setting is disabled by default and the server -# state will be reset on server start up. -# -state_file "~cjennings/.config/mpd/state" -# -# The location of the sticker database. This is a database which -# manages dynamic information attached to songs. -# -sticker_file "~cjennings/.config/mpd/sticker.sql" -# -############################################################################### - - -# General music daemon options ################################################ -# -# This setting specifies the user that MPD will run as. MPD should never run as -# root and you may use this setting to make MPD change its user ID after -# initialization. This setting is disabled by default and MPD is run as the -# current user. -# -user "cjennings" -# -# This setting specifies the group that MPD will run as. If not specified -# primary group of user specified with "user" setting will be used (if set). -# This is useful if MPD needs to be a member of group such as "audio" to -# have permission to use sound card. -# -#group "nogroup" -# -# This setting sets the address for the daemon to listen on. Careful attention -# should be paid if this is assigned to anything other then the default, any. -# This setting can deny access to control of the daemon. Choose any if you want -# to have mpd listen on every address. Not effective if systemd socket -# activation is in use. -# -# For network -bind_to_address "0.0.0.0" -# -# And for Unix Socket -#bind_to_address "/run/mpd/socket" -# -# This setting is the TCP port that is desired for the daemon to get assigned -# to. -# -#port "6600" -# -# This setting controls the type of information which is logged. Available -# setting arguments are "default", "secure" or "verbose". The "verbose" setting -# argument is recommended for troubleshooting, though can quickly stretch -# available resources on limited hardware storage. -# -#log_level "default" -# -# If you have a problem with your MP3s ending abruptly it is recommended that -# you set this argument to "no" to attempt to fix the problem. If this solves -# the problem, it is highly recommended to fix the MP3 files with vbrfix -# (available as vbrfix in the debian archive), at which -# point gapless MP3 playback can be enabled. -# -#gapless_mp3_playback "yes" -# -# Setting "restore_paused" to "yes" puts MPD into pause mode instead -# of starting playback after startup. -# -restore_paused "yes" -# -# This setting enables MPD to create playlists in a format usable by other -# music players. -# -save_absolute_paths_in_playlists "yes" -# -# This setting defines a list of tag types that will be extracted during the -# audio file discovery process. The complete list of possible values can be -# found in the mpd.conf man page. -#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc" -# -# This setting enables automatic update of MPD's database when files in -# music_directory are changed. -# -auto_update "yes" -# -# Limit the depth of the directories being watched, 0 means only watch -# the music directory itself. There is no limit by default. -# -#auto_update_depth "3" -# -############################################################################### - - -# Symbolic link behavior ###################################################### -# -# If this setting is set to "yes", MPD will discover audio files by following -# symbolic links outside of the configured music_directory. -# -#follow_outside_symlinks "yes" -# -# If this setting is set to "yes", MPD will discover audio files by following -# symbolic links inside of the configured music_directory. -# -#follow_inside_symlinks "yes" -# -############################################################################### - - -# Zeroconf / Avahi Service Discovery ########################################## -# -# If this setting is set to "yes", service information will be published with -# Zeroconf / Avahi. -# -# zeroconf_enabled "yes" -# -# The argument to this setting will be the Zeroconf / Avahi unique name for -# this MPD server on the network. -# -# zeroconf_name "Music Player Daemon" -# -############################################################################### - - -# Permissions ################################################################# -# -# If this setting is set, MPD will require password authorization. The password -# can setting can be specified multiple times for different password profiles. -# -#password "password@read,add,control,admin" -# -# This setting specifies the permissions a user has who has not yet logged in. -# -#default_permissions "read,add,control,admin" -# -############################################################################### - - -# Database ####################################################################### -# - -#database { -# plugin "proxy" -# host "other.mpd.host" -# port "6600" -#} - -# Input ####################################################################### -# - -input { - plugin "curl" -# proxy "proxy.isp.com:8080" -# proxy_user "user" -# proxy_password "password" -} - -# -############################################################################### - -# Audio Output ################################################################ -# -# MPD supports various audio output types, as well as playing through multiple -# audio outputs at the same time, through multiple audio_output settings -# blocks. Setting this block is optional, though the server will only attempt -# autodetection for one sound card. -# -# An example of an ALSA output: -# -#audio_output { -# type "alsa" -# name "My ALSA Device" -# device "hw:0,0" # optional -# mixer_type "hardware" # optional -# mixer_device "default" # optional -# mixer_control "PCM" # optional -# mixer_index "0" # optional -#} -# -# An example of an OSS output: -# -#audio_output { -# type "oss" -# name "My OSS Device" -# device "/dev/dsp" # optional -# mixer_type "hardware" # optional -# mixer_device "/dev/mixer" # optional -# mixer_control "PCM" # optional -#} -# -# An example of a shout output (for streaming to Icecast): -# -#audio_output { -# type "shout" -# encoding "ogg" # optional -# name "My Shout Stream" -# host "localhost" -# port "8000" -# mount "/mpd.ogg" -# password "hackme" -# quality "5.0" -# bitrate "128" -# format "44100:16:1" -# protocol "icecast2" # optional -# user "source" # optional -# description "My Stream Description" # optional -# url "http://example.com" # optional -# genre "jazz" # optional -# public "no" # optional -# timeout "2" # optional -# mixer_type "software" # optional -#} -# -# An example of a recorder output: -# -#audio_output { -# type "recorder" -# name "My recorder" -# encoder "vorbis" # optional, vorbis or lame -# path "/var/lib/mpd/recorder/mpd.ogg" -## quality "5.0" # do not define if bitrate is defined -# bitrate "128" # do not define if quality is defined -# format "44100:16:1" -#} -# -# An example of a httpd output (built-in HTTP streaming server): -# -#audio_output { -# type "httpd" -# name "My HTTP Stream" -# encoder "vorbis" # optional, vorbis or lame -# port "8000" -# bind_to_address "0.0.0.0" # optional, IPv4 or IPv6 -# quality "5.0" # do not define if bitrate is defined -# bitrate "128" # do not define if quality is defined -# format "44100:16:1" -# max_clients "0" # optional 0=no limit -#} -# -## cjennings 2021-06-26 - -audio_output { - type "oss" - name "OSS Audio" - device "/dev/dsp" # optional - mixer_type "hardware" # optional - mixer_device "/dev/mixer" # optional - mixer_control "vol" # optional -} - -audio_output { - type "fifo" - name "my_fifo" - path "/tmp/mpd.fifo" - format "44100:16:2" -} -# An example of a pulseaudio output (streaming to a remote pulseaudio server) -# Please see README.Debian if you want mpd to play through the pulseaudio -# daemon started as part of your graphical desktop session! -# -#audio_output { -# type "pulse" -# name "My Pulse Output" -# server "remote_server" # optional -# sink "remote_server_sink" # optional -#} -# -# An example of a winmm output (Windows multimedia API). -# -#audio_output { -# type "winmm" -# name "My WinMM output" -# device "Digital Audio (S/PDIF) (High Definition Audio Device)" # optional -# or -# device "0" # optional -# mixer_type "hardware" # optional -#} -# -# An example of an openal output. -# -#audio_output { -# type "openal" -# name "My OpenAL output" -# device "Digital Audio (S/PDIF) (High Definition Audio Device)" # optional -#} -# -## Example "pipe" output: -# -#audio_output { -# type "pipe" -# name "my pipe" -# command "aplay -f cd 2>/dev/null" -## Or if you're want to use AudioCompress -# command "AudioCompress -m | aplay -f cd 2>/dev/null" -## Or to send raw PCM stream through PCM: -# command "nc example.org 8765" -# format "44100:16:2" -#} -# -## An example of a null output (for no audio output): -# -#audio_output { -# type "null" -# name "My Null Output" -# mixer_type "none" # optional -#} -# -# If MPD has been compiled with libsamplerate support, this setting specifies -# the sample rate converter to use. Possible values can be found in the -# mpd.conf man page or the libsamplerate documentation. By default, this is -# setting is disabled. -# -#samplerate_converter "Fastest Sinc Interpolator" -# -############################################################################### - - -# Normalization automatic volume adjustments ################################## -# -# This setting specifies the type of ReplayGain to use. This setting can have -# the argument "off", "album", "track" or "auto". "auto" is a special mode that -# chooses between "track" and "album" depending on the current state of -# random playback. If random playback is enabled then "track" mode is used. -# See <http://www.replaygain.org> for more details about ReplayGain. -# This setting is off by default. -# -replaygain "album" -# -# This setting sets the pre-amp used for files that have ReplayGain tags. By -# default this setting is disabled. -# -#replaygain_preamp "0" -# -# This setting sets the pre-amp used for files that do NOT have ReplayGain tags. -# By default this setting is disabled. -# -#replaygain_missing_preamp "0" -# -# This setting enables or disables ReplayGain limiting. -# MPD calculates actual amplification based on the ReplayGain tags -# and replaygain_preamp / replaygain_missing_preamp setting. -# If replaygain_limit is enabled MPD will never amplify audio signal -# above its original level. If replaygain_limit is disabled such amplification -# might occur. By default this setting is enabled. -# -#replaygain_limit "yes" -# -# This setting enables on-the-fly normalization volume adjustment. This will -# result in the volume of all playing audio to be adjusted so the output has -# equal "loudness". This setting is disabled by default. -# -volume_normalization "yes" -# -############################################################################### - - -# Character Encoding ########################################################## -# -# If file or directory names do not display correctly for your locale then you -# may need to modify this setting. -# -filesystem_charset "UTF-8" -# -# This setting controls the encoding that ID3v1 tags should be converted from. -# -# id3v1_encoding "UTF-8" -# -############################################################################### - - -# SIDPlay decoder ############################################################# -# -# songlength_database: -# Location of your songlengths file, as distributed with the HVSC. -# The sidplay plugin checks this for matching MD5 fingerprints. -# See http://www.c64.org/HVSC/DOCUMENTS/Songlengths.faq -# -# default_songlength: -# This is the default playing time in seconds for songs not in the -# songlength database, or in case you're not using a database. -# A value of 0 means play indefinitely. -# -# filter: -# Turns the SID filter emulation on or off. -# -#decoder { -# plugin "sidplay" -# songlength_database "/media/C64Music/DOCUMENTS/Songlengths.txt" -# default_songlength "120" -# filter "true" -#} -# -############################################################################### - diff --git a/dotfiles/common/.config/mpv/input.conf b/dotfiles/common/.config/mpv/input.conf deleted file mode 100644 index 937534c..0000000 --- a/dotfiles/common/.config/mpv/input.conf +++ /dev/null @@ -1,4 +0,0 @@ -UP add volume +5 -DOWN add volume -5 -- add volume -5 -= add volume 5 diff --git a/dotfiles/common/.config/mpv/mpv.conf b/dotfiles/common/.config/mpv/mpv.conf deleted file mode 100644 index 52a4082..0000000 --- a/dotfiles/common/.config/mpv/mpv.conf +++ /dev/null @@ -1 +0,0 @@ -volume=75 diff --git a/dotfiles/common/.config/ncmpcpp/bindings b/dotfiles/common/.config/ncmpcpp/bindings deleted file mode 100644 index d9369fd..0000000 --- a/dotfiles/common/.config/ncmpcpp/bindings +++ /dev/null @@ -1,566 +0,0 @@ -############################################################## -## This is the example bindings file. Copy it to ## -## $XDG_CONFIG_HOME/ncmpcpp/bindings or ~/.ncmpcpp/bindings ## -## and set up your preferences. ## -############################################################## -## -##### General rules ##### -## -## 1) Because each action has runtime checks whether it's -## ok to run it, a few actions can be bound to one key. -## Actions will be bound in order given in configuration -## file. When a key is pressed, first action in order -## will test itself whether it's possible to run it. If -## test succeeds, action is executed and other actions -## bound to this key are ignored. If it doesn't, next -## action in order tests itself etc. -## -## 2) It's possible to bind more that one action at once -## to a key. It can be done using the following syntax: -## -## def_key "key" -## action1 -## action2 -## ... -## -## This creates a chain of actions. When such chain is -## executed, each action in chain is run until the end of -## chain is reached or one of its actions fails to execute -## due to its requirements not being met. If multiple actions -## and/or chains are bound to the same key, they will be -## consecutively run until one of them gets fully executed. -## -## 3) When ncmpcpp starts, bindings configuration file is -## parsed and then ncmpcpp provides "missing pieces" -## of default keybindings. If you want to disable some -## bindings, there is a special action called 'dummy' -## for that purpose. Eg. if you want to disable ability -## to crop playlists, you need to put the following -## into configuration file: -## -## def_key "C" -## dummy -## -## After that ncmpcpp will not bind any default action -## to this key. -## -## 4) To let you write simple macros, the following special -## actions are provided: -## -## - push_character "character" - pushes given special -## character into input queue, so it will be immediately -## picked by ncmpcpp upon next call to readKey function. -## Accepted values: mouse, up, down, page_up, page_down, -## home, end, space, enter, insert, delete, left, right, -## tab, ctrl-a, ctrl-b, ..., ctrl-z, ctrl-[, ctrl-\\, -## ctrl-], ctrl-^, ctrl-_, f1, f2, ..., f12, backspace. -## In addition, most of these names can be prefixed with -## alt-/ctrl-/shift- to be recognized with the appropriate -## modifier key(s). -## -## - push_characters "string" - pushes given string into -## input queue. -## -## - require_runnable "action" - checks whether given action -## is runnable and fails if it isn't. This is especially -## useful when mixed with previous two functions. Consider -## the following macro definition: -## -## def_key "key" -## push_characters "custom_filter" -## apply_filter -## -## If apply_filter can't be currently run, we end up with -## sequence of characters in input queue which will be -## treated just as we typed them. This may lead to unexpected -## results (in this case 'c' will most likely clear current -## playlist, 'u' will trigger database update, 's' will stop -## playback etc.). To prevent such thing from happening, we -## need to change above definition to this one: -## -## def_key "key" -## require_runnable "apply_filter" -## push_characters "custom_filter" -## apply_filter -## -## Here, first we test whether apply_filter can be actually run -## before we stuff characters into input queue, so if condition -## is not met, whole chain is aborted and we're fine. -## -## - require_screen "screen" - checks whether given screen is -## currently active. accepted values: browser, clock, help, -## media_library, outputs, playlist, playlist_editor, -## search_engine, tag_editor, visualizer, last_fm, lyrics, -## selected_items_adder, server_info, song_info, -## sort_playlist_dialog, tiny_tag_editor. -## -## - run_external_command "command" - runs given command using -## system() function. -## -## - run_external_console_command "command" - runs given console -## command using system() function. -## -## -## 5) In addition to binding to a key, you can also bind actions -## or chains of actions to a command. If it comes to commands, -## syntax is very similar to defining keys. Here goes example -## definition of a command: -## -## def_command "quit" [deferred] -## stop -## quit -## -## If you execute the above command (which can be done by -## invoking action execute_command, typing 'quit' and pressing -## enter), ncmpcpp will stop the player and then quit. Note the -## presence of word 'deferred' enclosed in square brackets. It -## tells ncmpcpp to wait for confirmation (ie. pressing enter) -## after you typed quit. Instead of 'deferred', 'immediate' -## could be used. Then ncmpcpp will not wait for confirmation -## (enter) and will execute the command the moment it sees it. -## -## Note: while command chains are executed, internal environment -## update (which includes current window refresh and mpd status -## update) is not performed for performance reasons. However, it -## may be desirable to do so in some situration. Therefore it's -## possible to invoke by hand by performing 'update enviroment' -## action. -## -## Note: There is a difference between: -## -## def_key "key" -## action1 -## -## def_key "key" -## action2 -## -## and -## -## def_key "key" -## action1 -## action2 -## -## First one binds two single actions to the same key whilst -## second one defines a chain of actions. The behavior of -## these two is different and is described in (1) and (2). -## -## Note: Function def_key accepts non-ascii characters. -## -##### List of unbound actions ##### -## -## The following actions are not bound to any key/command: -## -## - set_volume -## - load -## -# -#def_key "mouse" -# mouse_event -# -#def_key "up" -# scroll_up -# -#def_key "shift-up" -# select_item -# scroll_up -# -#def_key "down" -# scroll_down -# -#def_key "shift-down" -# select_item -# scroll_down -# -#def_key "[" -# scroll_up_album -# -#def_key "]" -# scroll_down_album -# -#def_key "{" -# scroll_up_artist -# -#def_key "}" -# scroll_down_artist -# -#def_key "page_up" -# page_up -# -#def_key "page_down" -# page_down -# -#def_key "home" -# move_home -# -#def_key "end" -# move_end -# -#def_key "insert" -# select_item -# -#def_key "enter" -# enter_directory -# -#def_key "enter" -# toggle_output -# -#def_key "enter" -# run_action -# -#def_key "enter" -# play_item -# -#def_key "space" -# add_item_to_playlist -# -#def_key "space" -# toggle_lyrics_update_on_song_change -# -#def_key "space" -# toggle_visualization_type -# -def_key "space" - pause - -#def_key "delete" -# delete_playlist_items -# -#def_key "delete" -# delete_browser_items -# -#def_key "delete" -# delete_stored_playlist -# -#def_key "right" -# next_column -# -#def_key "right" -# slave_screen -# -#def_key "right" -# volume_up -# -def_key "right" - seek_forward -# -#def_key "+" -# volume_up -# -#def_key "left" -# previous_column -# -#def_key "left" -# master_screen -# -#def_key "left" -# volume_down -# -def_key "left" - seek_backward -# -#def_key "-" -# volume_down -# -#def_key ":" -# execute_command -# -#def_key "tab" -# next_screen -# -#def_key "shift-tab" -# previous_screen -# -#def_key "f1" -# show_help -# -#def_key "1" -# show_playlist -# -#def_key "2" -# show_browser -# -#def_key "2" -# change_browse_mode -# -#def_key "3" -# show_search_engine -# -#def_key "3" -# reset_search_engine -# -#def_key "4" -# show_media_library -# -#def_key "4" -# toggle_media_library_columns_mode -# -#def_key "5" -# show_playlist_editor -# -#def_key "6" -# show_tag_editor -# -#def_key "7" -# show_outputs -# -#def_key "8" -# show_visualizer -# -def_key "=" - show_clock -# -#def_key "@" -# show_server_info -# -#def_key "s" -# stop -# -#def_key "p" -# pause -# -def_key "p" - add_item_to_playlist - -#def_key ">" -# next -# -#def_key "<" -# previous -# -#def_key "ctrl-h" -# jump_to_parent_directory -# -#def_key "ctrl-h" -# replay_song -# -#def_key "backspace" -# jump_to_parent_directory -# -#def_key "backspace" -# replay_song -# -#def_key "backspace" -# play -# -#def_key "f" -# seek_forward -# -#def_key "b" -# seek_backward -# -#def_key "r" -# toggle_repeat -# -#def_key "z" -# toggle_random -# -#def_key "y" -# save_tag_changes -# -#def_key "y" -# start_searching -# -def_key "t" - toggle_single -# -def_key "x" - toggle_consume -# -#def_key "Y" -# toggle_replay_gain_mode -# -#def_key "T" -# toggle_add_mode -# -#def_key "|" -# toggle_mouse -# -#def_key "#" -# toggle_bitrate_visibility -# -#def_key "Z" -# shuffle -# -#def_key "x" -# toggle_crossfade -# -#def_key "X" -# set_crossfade -# -#def_key "u" -# update_database -# -#def_key "ctrl-s" -# sort_playlist -# -#def_key "ctrl-s" -# toggle_browser_sort_mode -# -#def_key "ctrl-s" -# toggle_media_library_sort_mode -# -#def_key "ctrl-r" -# reverse_playlist -# -#def_key "ctrl-f" -# apply_filter -# -#def_key "ctrl-_" -# select_found_items -# -#def_key "/" -# find -# -#def_key "/" -# find_item_forward -# -#def_key "?" -# find -# -#def_key "?" -# find_item_backward -# -#def_key "." -# next_found_item -# -#def_key "," -# previous_found_item -# -#def_key "w" -# toggle_find_mode -# -#def_key "e" -# edit_song -# -#def_key "e" -# edit_library_tag -# -#def_key "e" -# edit_library_album -# -#def_key "e" -# edit_directory_name -# -#def_key "e" -# edit_playlist_name -# -#def_key "e" -# edit_lyrics -# -def_key "i" - show_song_info -# -#def_key "I" -# show_artist_info -# -#def_key "g" -# jump_to_position_in_song -# -def_key "l" - show_lyrics -# -#def_key "ctrl-v" -# select_range -# -#def_key "v" -# reverse_selection -# -def_key "v" - toggle_visualization_type - -#def_key "V" -# remove_selection -# -#def_key "B" -# select_album -# -#def_key "a" -# add_selected_items -# -#def_key "c" -# clear_playlist -# -#def_key "c" -# clear_main_playlist -# -#def_key "C" -# crop_playlist -# -#def_key "C" -# crop_main_playlist -# -#def_key "m" -# move_sort_order_up -# -def_key "shift-up" - move_selected_items_up -# -#def_key "n" -# move_sort_order_down -# -def_key "shift-down" - move_selected_items_down -# -#def_key "M" -# move_selected_items_to -# -#def_key "A" -# add -# -def_key "S" - save_playlist -# -#def_key "o" -# jump_to_playing_song -# -#def_key "G" -# jump_to_browser -# -#def_key "G" -# jump_to_playlist_editor -# -#def_key "~" -# jump_to_media_library -# -#def_key "E" -# jump_to_tag_editor -# -#def_key "U" -# toggle_playing_song_centering -# -#def_key "P" -# toggle_display_mode -# -#def_key "\\" -# toggle_interface -# -#def_key "!" -# toggle_separators_between_albums -# -#def_key "L" -# toggle_lyrics_fetcher -# -#def_key "F" -# fetch_lyrics_in_background -# -#def_key "alt-l" -# toggle_fetching_lyrics_in_background -# -#def_key "ctrl-l" -# toggle_screen_lock -# -#def_key "`" -# toggle_library_tag_type -# -#def_key "`" -# refetch_lyrics -# -#def_key "`" -# add_random_items -# -#def_key "ctrl-p" -# set_selected_items_priority -# -#def_key "q" -# quit -# diff --git a/dotfiles/common/.config/ncmpcpp/config b/dotfiles/common/.config/ncmpcpp/config deleted file mode 100644 index 6520d5a..0000000 --- a/dotfiles/common/.config/ncmpcpp/config +++ /dev/null @@ -1,71 +0,0 @@ -# Connection -mpd_host = "127.0.0.1" -# mpd_host = "/home/cjennings/.config/mpd/socket" -mpd_port = "6600" -mpd_music_dir = "/home/cjennings/music" -mpd_connection_timeout = "10" -mpd_crossfade_time = "1" - -# Visualizer -visualizer_data_source = "/tmp/mpd.fifo" -visualizer_output_name = "FIFO" -visualizer_in_stereo = "yes" -visualizer_type = "wave_filled" -visualizer_color = 246,245,244,243,242,241,240,239,238,237,236,235 -visualizer_look = "|○" - -# Columns -song_columns_list_format = "(3f)[239]{} (35)[246]{t|f} (30)[blue]{a} (30)[green]{b} (5f)[240]{l}" -song_list_format = "{$5 %a$9 $1│$9 $8%t$9 }|{ $8%f$9}$R{$5%b $7}" -song_status_format = "{{{$5%a$9}} $8-$9 {$2%t$9}|{$0%f$9}{ $8-$9 $3%b$9{ $8-$9 $5%y$9}}}" -song_library_format = "{%n $8-$9 }{%t}|{%f}" -now_playing_prefix = "$8$b ➤ " -browser_playlist_prefix = "playlist" -selected_item_prefix = "$5" -selected_item_suffix = "$9" -song_window_title_format = "{%t}|{%f} - {%a}" - -# Various -playlist_show_remaining_time = "no" -playlist_shorten_total_times = "yes" -playlist_separate_albums = "no" -playlist_display_mode = "columns" -browser_display_mode = "columns" -search_engine_display_mode = "columns" -discard_colors_if_item_is_selected = "no" -incremental_seeking = "yes" -seek_time = "1" -autocenter_mode = "yes" -centered_cursor = "yes" -progressbar_look = "─╼─" -progressbar_color = 240 -progressbar_elapsed_color = white -user_interface = "classic" -header_visibility = "no" -titles_visibility = "no" -header_text_scrolling = "yes" -cyclic_scrolling = "no" -lines_scrolled = "2" -follow_now_playing_lyrics = "yes" -show_hidden_files_in_local_browser = "no" -jump_to_now_playing_song_at_start = "yes" -clock_display_seconds = "no" -display_volume_level = "no" -display_bitrate = "yes" -display_remaining_time = "no" -regular_expressions = "extended" -ignore_leading_the = "no" -block_search_constraints_change_if_items_found = "yes" -mouse_support = "yes" -mouse_list_scroll_whole_page = "yes" -external_editor = "vim" -use_console_editor = "yes" -colors_enabled = "yes" -empty_tag_color = "white" -header_window_color = "yellow" -state_line_color = "black" -state_flags_color = "black" -main_window_color = 243 -statusbar_color = "yellow" -active_window_border = "yellow" - diff --git a/dotfiles/common/.config/qalculate/qalculate-gtk.cfg b/dotfiles/common/.config/qalculate/qalculate-gtk.cfg deleted file mode 100644 index 2f95d0d..0000000 --- a/dotfiles/common/.config/qalculate/qalculate-gtk.cfg +++ /dev/null @@ -1,378 +0,0 @@ - -[General] -version=5.10.0 -allow_multiple_instances=0 -width=1167 -always_on_top=0 -enable_tooltips=1 -error_info_shown=1 -save_mode_on_exit=1 -save_definitions_on_exit=1 -save_history_separately=0 -auto_update_exchange_rates=-1 -clear_history_on_exit=0 -history_expression_type=2 -use_custom_history_font=0 -use_custom_expression_font=0 -replace_expression=0 -enable_completion=1 -enable_completion2=1 -completion_min=1 -completion_min2=1 -completion_delay=0 -use_custom_status_font=0 -vertical_button_padding=-1 -horizontal_button_padding=-1 -use_custom_keypad_font=0 -latest_button_currency=USD -use_custom_result_font=0 -continuous_conversion=1 -set_missing_prefixes=0 -show_bases_keypad=1 -keep_function_dialog_open=0 -ignore_locale=0 -load_global_definitions=1 -local_currency_conversion=1 -use_binary_prefixes=0 -check_version=0 -show_keypad=1 -show_history=0 -history_height=0 -minimal_width=500 -show_stack=1 -show_convert=0 -persistent_keypad=0 -minimal_mode=0 -rpn_keys=1 -display_expression_status=1 -parsed_expression_in_resultview=0 -calculate_as_you_type_history_delay=2000 -use_unicode_signs=1 -lower_case_numbers=0 -duodecimal_symbols=0 -exp_display=3 -imaginary_j=0 -base_display=1 -twos_complement=1 -hexadecimal_twos_complement=0 -twos_complement_input=0 -hexadecimal_twos_complement_input=0 -spell_out_logical_operators=1 -caret_as_xor=0 -close_with_esc=-1 -digit_grouping=1 -copy_ascii=0 -copy_ascii_without_units=0 -decimal_comma=-1 -dot_as_separator=0 -comma_as_separator=1 -use_custom_application_font=0 -multiplication_sign=2 -division_sign=1 -expression_history=30×4 -expression_history=60×120 -expression_history=60×12− -expression_history=60×60×8 -expression_history=60×60×6 -expression_history=60×60 -expression_history=7200/60 -expression_history=1024×8 -expression_history=125×7×6 -expression_history=7600×4 -expression_history=7600X4 -expression_history=10619.57−3000 -expression_history=1991−1964 -expression_history=(5×75)+150 -expression_history=5×75 -expression_history=100−35 -expression_history=262690−165393.50 -expression_history=212770+24960 -expression_history=212770−24960 -expression_history=49920/2 -expression_history=262690−212770 -expression_history=68/24 -expression_history=168/197 -expression_history=197/168 -expression_history=262690−19100 -expression_history=38200/2 -expression_history=$38,200/2 -expression_history=30000/197000 -expression_history=197250+6360+6140+3840 -expression_history=4550+4050 -expression_history=4,500+4060 -expression_history=490×6 -expression_history=7688.16×4 -expression_history=3277.52+46.85 -expression_history=3310.94+109.04 -expression_history=3277.52+46.86 -expression_history=30 + 15 + 5 + 10 + 30 + 30 -expression_history=30 +5+15+30+20+20 -expression_history=30 + 5 + 15 + 30 + 20 + 5 + 5 + 20 -expression_history=1024×16 -expression_history=3278×12 -expression_history=1026×16 -expression_history=96×2 -history_time=1773439073 -history_expression=30×4 -history_parse=30 × 4 -history_result=120 -history_time=1770959475 -history_expression=60×120 -history_parse=60 × 120 -history_result=7200 -history_time=1770959329 -history_expression=60×120 -history_parse=60 × 120 -history_result=7200 -history_time=1770959325 -history_expression=60×12− -history_parse=60 × 12 -history_warning=Misplaced operator(s) "−" ignored -history_result=720 -history_time=1770475922 -history_expression=60×60×8 -history_parse=60 × 60 × 8 -history_result=28 800 -history_time=1770475866 -history_expression=60×60×8 -history_parse=60 × 60 × 8 -history_result=28 800 -history_time=1770475857 -history_expression=60×60×6 -history_parse=60 × 60 × 6 -history_result=21 600 -history_time=1770475851 -history_expression=60×60 -history_parse=60 × 60 -history_result=3600 -history_time=1770475838 -history_expression=7200/60 -history_parse=7200 ∕ 60 -history_result=120 -history_time=1770329943 -history_expression=1024×8 -history_parse=1024 × 8 -history_result=8192 -history_time=1768330887 -history_expression=125×7×6 -history_parse=125 × 7 × 6 -history_result=5250 -history_time=1767984868 -history_expression=7600×4 -history_parse=7600 × 4 -history_result=30 400 -history_time=1767984861 -history_expression=7600X4 -history_parse=7600 -history_error=Trailing characters "X4" (not a valid variable/function/unit) in number "7600X4" were ignored. -history_result=7600 -history_time=1767984665 -history_expression=10619.57−3000 -history_parse=10 619.57 − 3000 -history_result=7619.57 -history_time=1767807514 -history_expression=1991−1964 -history_parse=1991 − 1964 -history_result=27 -history_time=1766252840 -history_expression=(5×75)+150 -history_parse=(5 × 75) + 150 -history_result=525 -history_time=1766252831 -history_expression=5×75 -history_parse=5 × 75 -history_result=375 -history_time=1764874377 -history_expression=100−35 -history_parse=100 − 35 -history_result=65 -history_time=1762832920 -history_expression=262690−165393.50 -history_parse=262 690 − 165 393.5 -history_result=97 296.5 -history_time=1762825204 -history_expression=212770+24960 -history_parse=212 770 + 24 960 -history_result=237 730 -history_time=1762825199 -history_expression=212770−24960 -history_parse=212 770 − 24 960 -history_result=187 810 -history_time=1762825172 -history_expression=49920/2 -history_parse=49 920 ∕ 2 -history_result=24 960 -history_time=1762823711 -history_expression=262690−212770 -history_parse=262 690 − 212 770 -history_result=49 920 -history_time=1762731979 -history_expression=68/24 -history_parse=68 ∕ 24 -history_result_approximate=2.833 333 333 -history_time=1762176587 -history_expression=168/197 -history_parse=168 ∕ 197 -history_result_approximate=0.852 791 878 2 -history_time=1762176579 -history_expression=197/168 -history_parse=197 ∕ 168 -history_result_approximate=1.172 619 048 -history_time=1761510158 -history_expression=262690−19100 -history_parse=262 690 − 19 100 -history_result=243 590 -history_time=1761508703 -history_expression=38200/2 -history_parse=38 200 ∕ 2 -history_result=19 100 -history_time=1761508667 -history_expression=$38,200/2 -history_parse=(USD × 38 200) ∕ 2 -history_result=$19 100 -history_time=1761449979 -history_expression=30000/197000 -history_parse=30 000 ∕ 197 000 -history_result_approximate=0.152 284 264 0 -history_time=1761245928 -history_expression=197250+6360+6140+3840 -history_parse=197 250 + 6360 + 6140 + 3840 -history_result=213 590 -history_time=1761167849 -history_expression=4550+4050 -history_parse=4550 + 4050 -history_result=8600 -history_time=1761167803 -history_expression=4,500+4060 -history_parse=4500 + 4060 -history_result=8560 -history_time=1752350332 -history_expression=490×6 -history_parse=490 × 6 -history_result=2940 -history_time=1752334636 -history_expression=7688.16×4 -history_parse=7688.16 × 4 -history_result=30 752.64 -history_time=1751310907 -history_expression=3277.52+46.85 -history_parse=3277.52 + 46.85 -history_result=3324.37 -history_time=1751310861 -history_expression=3310.94+109.04 -history_parse=3310.94 + 109.04 -history_result=3419.98 -history_time=1751310628 -history_expression=3277.52+46.86 -history_parse=3277.52 + 46.86 -history_result=3324.38 -history_time=1750342970 -history_expression=30 + 15 + 5 + 10 + 30 + 30 -history_parse=30 + 15 + 5 + 10 + 30 + 30 -history_result=120 -history_time=1750342955 -history_expression=30 +5+15+30+20+20 -history_parse=30 + 5 + 15 + 30 + 20 + 20 -history_result=120 -history_time=1750342806 -history_expression=30 + 15 + 5 + 10 + 30 + 30 -history_parse=30 + 15 + 5 + 10 + 30 + 30 -history_result=120 -history_time=1750342726 -history_expression=30 + 5 + 15 + 30 + 20 + 5 + 5 + 20 -history_parse=30 + 5 + 15 + 30 + 20 + 5 + 5 + 20 -history_result=130 -history_time=1749150165 -history_expression=1024×16 -history_parse=1024 × 16 -history_result=16 384 -history_time=1748972784 -history_expression=3278×12 -history_parse=3278 × 12 -history_result=39 336 -history_time=1748621453 -history_expression=1026×16 -history_parse=1026 × 16 -history_result=16 416 -history_time=1747760701 -history_expression=96×2 -history_parse=96 × 2 -history_result=192 -recent_functions= -recent_variables= -recent_units= - -[Mode] -min_deci=0 -use_min_deci=0 -max_deci=2 -use_max_deci=0 -precision=10 -interval_arithmetic=1 -interval_display=0 -min_exp=-1 -negative_exponents=0 -sort_minus_last=1 -number_fraction_format=0 -complex_number_form=0 -use_prefixes=1 -use_prefixes_for_all_units=0 -use_prefixes_for_currencies=0 -abbreviate_names=1 -all_prefixes_enabled=0 -denominator_prefix_enabled=1 -place_units_separately=1 -auto_post_conversion=3 -mixed_units_conversion=3 -number_base=10 -number_base_expression=10 -read_precision=0 -assume_denominators_nonzero=1 -warn_about_denominators_assumed_nonzero=1 -structuring=1 -angle_unit=1 -functions_enabled=1 -variables_enabled=1 -calculate_functions=1 -calculate_variables=1 -variable_units_enabled=1 -sync_units=1 -unknownvariables_enabled=0 -units_enabled=1 -allow_complex=1 -allow_infinite=1 -indicate_infinite_series=0 -show_ending_zeroes=1 -rounding_mode=0 -approximation=1 -interval_calculation=1 -concise_uncertainty_input=0 -calculate_as_you_type=0 -in_rpn_mode=0 -chain_mode=0 -limit_implicit_multiplication=0 -parsing_mode=0 -simplified_percentage=-1 -spacious=1 -excessive_parenthesis=0 -visible_keypad=0 -short_multiplication=1 -default_assumption_type=4 -default_assumption_sign=0 - -[Plotting] -plot_legend_placement=2 -plot_style=0 -plot_smoothing=0 -plot_display_grid=1 -plot_full_border=0 -plot_min=0 -plot_max=10 -plot_step=1 -plot_sampling_rate=1001 -plot_use_sampling_rate=1 -plot_variable=x -plot_rows=0 -plot_type=0 -plot_color=1 -plot_linewidth=2 diff --git a/dotfiles/common/.config/qt5ct/qt5ct.conf b/dotfiles/common/.config/qt5ct/qt5ct.conf deleted file mode 100644 index 56a88db..0000000 --- a/dotfiles/common/.config/qt5ct/qt5ct.conf +++ /dev/null @@ -1,32 +0,0 @@ -[Appearance] -color_scheme_path=/usr/share/qt5ct/colors/darker.conf -custom_palette=true -icon_theme=Papirus-Dark -standard_dialogs=default -style=Adwaita-Dark - -[Fonts] -fixed="Cantarell,11,-1,5,50,0,0,0,0,0" -general="Cantarell,11,-1,5,50,0,0,0,0,0" - -[Interface] -activate_item_on_single_click=1 -buttonbox_layout=0 -cursor_flash_time=1000 -dialog_buttons_have_icons=1 -double_click_interval=400 -gui_effects=@Invalid() -keyboard_scheme=2 -menus_have_icons=true -show_shortcuts_in_context_menus=true -stylesheets=@Invalid() -toolbutton_style=4 -underline_shortcut=1 -wheel_scroll_lines=3 - -[SettingsWindow] -geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0\0\0\0\0\x11\0\0\x4g\0\0\x2\xf0\0\0\0\0\0\0\0\x11\0\0\x4g\0\0\x2\xf0\0\0\0\0\0\0\0\0\x4h\0\0\0\0\0\0\0\x11\0\0\x4g\0\0\x2\xf0) - -[Troubleshooting] -force_raster_widgets=1 -ignored_applications=@Invalid() diff --git a/dotfiles/common/.config/qt6ct/qt6ct.conf b/dotfiles/common/.config/qt6ct/qt6ct.conf deleted file mode 100644 index 496c09d..0000000 --- a/dotfiles/common/.config/qt6ct/qt6ct.conf +++ /dev/null @@ -1,29 +0,0 @@ -[Appearance] -color_scheme_path=/usr/share/qt6ct/colors/darker.conf -custom_palette=true -icon_theme=Papirus-Dark -standard_dialogs=default -style=Adwaita-Dark - -[Fonts] -fixed="Cantarell,11,-1,5,50,0,0,0,0,0" -general="Cantarell,11,-1,5,50,0,0,0,0,0" - -[Interface] -activate_item_on_single_click=1 -buttonbox_layout=0 -cursor_flash_time=1000 -dialog_buttons_have_icons=1 -double_click_interval=400 -gui_effects=@Invalid() -keyboard_scheme=2 -menus_have_icons=true -show_shortcuts_in_context_menus=true -stylesheets=@Invalid() -toolbutton_style=4 -underline_shortcut=1 -wheel_scroll_lines=3 - -[Troubleshooting] -force_raster_widgets=1 -ignored_applications=@Invalid() diff --git a/dotfiles/common/.config/ranger/commands.py b/dotfiles/common/.config/ranger/commands.py deleted file mode 100644 index 97b7909..0000000 --- a/dotfiles/common/.config/ranger/commands.py +++ /dev/null @@ -1,62 +0,0 @@ -# This is a sample commands.py. You can add your own commands here. -# -# Please refer to commands_full.py for all the default commands and a complete -# documentation. Do NOT add them all here, or you may end up with defunct -# commands when upgrading ranger. - -# A simple command for demonstration purposes follows. -# ----------------------------------------------------------------------------- - -from __future__ import (absolute_import, division, print_function) - -# You can import any python module as needed. -import os - -# You always need to import ranger.api.commands here to get the Command class: -from ranger.api.commands import Command - - -# Any class that is a subclass of "Command" will be integrated into ranger as a -# command. Try typing ":my_edit<ENTER>" in ranger! -class my_edit(Command): - # The so-called doc-string of the class will be visible in the built-in - # help that is accessible by typing "?c" inside ranger. - """:my_edit <filename> - - A sample command for demonstration purposes that opens a file in an editor. - """ - - # The execute method is called when you run this command in ranger. - def execute(self): - # self.arg(1) is the first (space-separated) argument to the function. - # This way you can write ":my_edit somefilename<ENTER>". - if self.arg(1): - # self.rest(1) contains self.arg(1) and everything that follows - target_filename = self.rest(1) - else: - # self.fm is a ranger.core.filemanager.FileManager object and gives - # you access to internals of ranger. - # self.fm.thisfile is a ranger.container.file.File object and is a - # reference to the currently selected file. - target_filename = self.fm.thisfile.path - - # This is a generic function to print text in ranger. - self.fm.notify("Let's edit the file " + target_filename + "!") - - # Using bad=True in fm.notify allows you to print error messages: - if not os.path.exists(target_filename): - self.fm.notify("The given file does not exist!", bad=True) - return - - # This executes a function from ranger.core.acitons, a module with a - # variety of subroutines that can help you construct commands. - # Check out the source, or run "pydoc ranger.core.actions" for a list. - self.fm.edit_file(target_filename) - - # The tab method is called when you press tab, and should return a list of - # suggestions that the user will tab through. - # tabnum is 1 for <TAB> and -1 for <S-TAB> by default - def tab(self, tabnum): - # This is a generic tab-completion function that iterates through the - # content of the current directory. - return self._tab_directory_content() diff --git a/dotfiles/common/.config/ranger/commands_full.py b/dotfiles/common/.config/ranger/commands_full.py deleted file mode 100644 index d177203..0000000 --- a/dotfiles/common/.config/ranger/commands_full.py +++ /dev/null @@ -1,1836 +0,0 @@ -# -*- coding: utf-8 -*- -# This file is part of ranger, the console file manager. -# This configuration file is licensed under the same terms as ranger. -# =================================================================== -# -# NOTE: If you copied this file to /etc/ranger/commands_full.py or -# ~/.config/ranger/commands_full.py, then it will NOT be loaded by ranger, -# and only serve as a reference. -# -# =================================================================== -# This file contains ranger's commands. -# It's all in python; lines beginning with # are comments. -# -# Note that additional commands are automatically generated from the methods -# of the class ranger.core.actions.Actions. -# -# You can customize commands in the files /etc/ranger/commands.py (system-wide) -# and ~/.config/ranger/commands.py (per user). -# They have the same syntax as this file. In fact, you can just copy this -# file to ~/.config/ranger/commands_full.py with -# `ranger --copy-config=commands_full' and make your modifications, don't -# forget to rename it to commands.py. You can also use -# `ranger --copy-config=commands' to copy a short sample commands.py that -# has everything you need to get started. -# But make sure you update your configs when you update ranger. -# -# =================================================================== -# Every class defined here which is a subclass of `Command' will be used as a -# command in ranger. Several methods are defined to interface with ranger: -# execute(): called when the command is executed. -# cancel(): called when closing the console. -# tab(tabnum): called when <TAB> is pressed. -# quick(): called after each keypress. -# -# tab() argument tabnum is 1 for <TAB> and -1 for <S-TAB> by default -# -# The return values for tab() can be either: -# None: There is no tab completion -# A string: Change the console to this string -# A list/tuple/generator: cycle through every item in it -# -# The return value for quick() can be: -# False: Nothing happens -# True: Execute the command afterwards -# -# The return value for execute() and cancel() doesn't matter. -# -# =================================================================== -# Commands have certain attributes and methods that facilitate parsing of -# the arguments: -# -# self.line: The whole line that was written in the console. -# self.args: A list of all (space-separated) arguments to the command. -# self.quantifier: If this command was mapped to the key "X" and -# the user pressed 6X, self.quantifier will be 6. -# self.arg(n): The n-th argument, or an empty string if it doesn't exist. -# self.rest(n): The n-th argument plus everything that followed. For example, -# if the command was "search foo bar a b c", rest(2) will be "bar a b c" -# self.start(n): Anything before the n-th argument. For example, if the -# command was "search foo bar a b c", start(2) will be "search foo" -# -# =================================================================== -# And this is a little reference for common ranger functions and objects: -# -# self.fm: A reference to the "fm" object which contains most information -# about ranger. -# self.fm.notify(string): Print the given string on the screen. -# self.fm.notify(string, bad=True): Print the given string in RED. -# self.fm.reload_cwd(): Reload the current working directory. -# self.fm.thisdir: The current working directory. (A File object.) -# self.fm.thisfile: The current file. (A File object too.) -# self.fm.thistab.get_selection(): A list of all selected files. -# self.fm.execute_console(string): Execute the string as a ranger command. -# self.fm.open_console(string): Open the console with the given string -# already typed in for you. -# self.fm.move(direction): Moves the cursor in the given direction, which -# can be something like down=3, up=5, right=1, left=1, to=6, ... -# -# File objects (for example self.fm.thisfile) have these useful attributes and -# methods: -# -# tfile.path: The path to the file. -# tfile.basename: The base name only. -# tfile.load_content(): Force a loading of the directories content (which -# obviously works with directories only) -# tfile.is_directory: True/False depending on whether it's a directory. -# -# For advanced commands it is unavoidable to dive a bit into the source code -# of ranger. -# =================================================================== - -from __future__ import (absolute_import, division, print_function) - -from collections import deque -import os -import re - -from ranger.api.commands import Command - - -class alias(Command): - """:alias <newcommand> <oldcommand> - - Copies the oldcommand as newcommand. - """ - - context = 'browser' - resolve_macros = False - - def execute(self): - if not self.arg(1) or not self.arg(2): - self.fm.notify('Syntax: alias <newcommand> <oldcommand>', bad=True) - return - - self.fm.commands.alias(self.arg(1), self.rest(2)) - - -class echo(Command): - """:echo <text> - - Display the text in the statusbar. - """ - - def execute(self): - self.fm.notify(self.rest(1)) - - -class cd(Command): - """:cd [-r] <path> - - The cd command changes the directory. - If the path is a file, selects that file. - The command 'cd -' is equivalent to typing ``. - Using the option "-r" will get you to the real path. - """ - - def execute(self): - if self.arg(1) == '-r': - self.shift() - destination = os.path.realpath(self.rest(1)) - if os.path.isfile(destination): - self.fm.select_file(destination) - return - else: - destination = self.rest(1) - - if not destination: - destination = '~' - - if destination == '-': - self.fm.enter_bookmark('`') - else: - self.fm.cd(destination) - - def _tab_args(self): - # dest must be rest because path could contain spaces - if self.arg(1) == '-r': - start = self.start(2) - dest = self.rest(2) - else: - start = self.start(1) - dest = self.rest(1) - - if dest: - head, tail = os.path.split(os.path.expanduser(dest)) - if head: - dest_exp = os.path.join(os.path.normpath(head), tail) - else: - dest_exp = tail - else: - dest_exp = '' - return (start, dest_exp, os.path.join(self.fm.thisdir.path, dest_exp), - dest.endswith(os.path.sep)) - - @staticmethod - def _tab_paths(dest, dest_abs, ends_with_sep): - if not dest: - try: - return next(os.walk(dest_abs))[1], dest_abs - except (OSError, StopIteration): - return [], '' - - if ends_with_sep: - try: - return [os.path.join(dest, path) for path in next(os.walk(dest_abs))[1]], '' - except (OSError, StopIteration): - return [], '' - - return None, None - - def _tab_match(self, path_user, path_file): - if self.fm.settings.cd_tab_case == 'insensitive': - path_user = path_user.lower() - path_file = path_file.lower() - elif self.fm.settings.cd_tab_case == 'smart' and path_user.islower(): - path_file = path_file.lower() - return path_file.startswith(path_user) - - def _tab_normal(self, dest, dest_abs): - dest_dir = os.path.dirname(dest) - dest_base = os.path.basename(dest) - - try: - dirnames = next(os.walk(os.path.dirname(dest_abs)))[1] - except (OSError, StopIteration): - return [], '' - - return [os.path.join(dest_dir, d) for d in dirnames if self._tab_match(dest_base, d)], '' - - def _tab_fuzzy_match(self, basepath, tokens): - """ Find directories matching tokens recursively """ - if not tokens: - tokens = [''] - paths = [basepath] - while True: - token = tokens.pop() - matches = [] - for path in paths: - try: - directories = next(os.walk(path))[1] - except (OSError, StopIteration): - continue - matches += [os.path.join(path, d) for d in directories - if self._tab_match(token, d)] - if not tokens or not matches: - return matches - paths = matches - - return None - - def _tab_fuzzy(self, dest, dest_abs): - tokens = [] - basepath = dest_abs - while True: - basepath_old = basepath - basepath, token = os.path.split(basepath) - if basepath == basepath_old: - break - if os.path.isdir(basepath_old) and not token.startswith('.'): - basepath = basepath_old - break - tokens.append(token) - - paths = self._tab_fuzzy_match(basepath, tokens) - if not os.path.isabs(dest): - paths_rel = basepath - paths = [os.path.relpath(path, paths_rel) for path in paths] - else: - paths_rel = '' - return paths, paths_rel - - def tab(self, tabnum): - from os.path import sep - - start, dest, dest_abs, ends_with_sep = self._tab_args() - - paths, paths_rel = self._tab_paths(dest, dest_abs, ends_with_sep) - if paths is None: - if self.fm.settings.cd_tab_fuzzy: - paths, paths_rel = self._tab_fuzzy(dest, dest_abs) - else: - paths, paths_rel = self._tab_normal(dest, dest_abs) - - paths.sort() - - if self.fm.settings.cd_bookmarks: - paths[0:0] = [ - os.path.relpath(v.path, paths_rel) if paths_rel else v.path - for v in self.fm.bookmarks.dct.values() for path in paths - if v.path.startswith(os.path.join(paths_rel, path) + sep) - ] - - if not paths: - return None - if len(paths) == 1: - return start + paths[0] + sep - return [start + dirname for dirname in paths] - - -class chain(Command): - """:chain <command1>; <command2>; ... - - Calls multiple commands at once, separated by semicolons. - """ - - def execute(self): - if not self.rest(1).strip(): - self.fm.notify('Syntax: chain <command1>; <command2>; ...', bad=True) - return - for command in [s.strip() for s in self.rest(1).split(";")]: - self.fm.execute_console(command) - - -class shell(Command): - escape_macros_for_shell = True - - def execute(self): - if self.arg(1) and self.arg(1)[0] == '-': - flags = self.arg(1)[1:] - command = self.rest(2) - else: - flags = '' - command = self.rest(1) - - if command: - self.fm.execute_command(command, flags=flags) - - def tab(self, tabnum): - from ranger.ext.get_executables import get_executables - if self.arg(1) and self.arg(1)[0] == '-': - command = self.rest(2) - else: - command = self.rest(1) - start = self.line[0:len(self.line) - len(command)] - - try: - position_of_last_space = command.rindex(" ") - except ValueError: - return (start + program + ' ' for program - in get_executables() if program.startswith(command)) - if position_of_last_space == len(command) - 1: - selection = self.fm.thistab.get_selection() - if len(selection) == 1: - return self.line + selection[0].shell_escaped_basename + ' ' - return self.line + '%s ' - - before_word, start_of_word = self.line.rsplit(' ', 1) - return (before_word + ' ' + file.shell_escaped_basename - for file in self.fm.thisdir.files or [] - if file.shell_escaped_basename.startswith(start_of_word)) - - -class open_with(Command): - - def execute(self): - app, flags, mode = self._get_app_flags_mode(self.rest(1)) - self.fm.execute_file( - files=[f for f in self.fm.thistab.get_selection()], - app=app, - flags=flags, - mode=mode) - - def tab(self, tabnum): - return self._tab_through_executables() - - def _get_app_flags_mode(self, string): # pylint: disable=too-many-branches,too-many-statements - """Extracts the application, flags and mode from a string. - - examples: - "mplayer f 1" => ("mplayer", "f", 1) - "atool 4" => ("atool", "", 4) - "p" => ("", "p", 0) - "" => None - """ - - app = '' - flags = '' - mode = 0 - split = string.split() - - if len(split) == 1: - part = split[0] - if self._is_app(part): - app = part - elif self._is_flags(part): - flags = part - elif self._is_mode(part): - mode = part - - elif len(split) == 2: - part0 = split[0] - part1 = split[1] - - if self._is_app(part0): - app = part0 - if self._is_flags(part1): - flags = part1 - elif self._is_mode(part1): - mode = part1 - elif self._is_flags(part0): - flags = part0 - if self._is_mode(part1): - mode = part1 - elif self._is_mode(part0): - mode = part0 - if self._is_flags(part1): - flags = part1 - - elif len(split) >= 3: - part0 = split[0] - part1 = split[1] - part2 = split[2] - - if self._is_app(part0): - app = part0 - if self._is_flags(part1): - flags = part1 - if self._is_mode(part2): - mode = part2 - elif self._is_mode(part1): - mode = part1 - if self._is_flags(part2): - flags = part2 - elif self._is_flags(part0): - flags = part0 - if self._is_mode(part1): - mode = part1 - elif self._is_mode(part0): - mode = part0 - if self._is_flags(part1): - flags = part1 - - return app, flags, int(mode) - - def _is_app(self, arg): - return not self._is_flags(arg) and not arg.isdigit() - - @staticmethod - def _is_flags(arg): - from ranger.core.runner import ALLOWED_FLAGS - return all(x in ALLOWED_FLAGS for x in arg) - - @staticmethod - def _is_mode(arg): - return all(x in '0123456789' for x in arg) - - -class set_(Command): - """:set <option name>=<python expression> - - Gives an option a new value. - - Use `:set <option>!` to toggle or cycle it, e.g. `:set flush_input!` - """ - name = 'set' # don't override the builtin set class - - def execute(self): - name = self.arg(1) - name, value, _, toggle = self.parse_setting_line_v2() - if toggle: - self.fm.toggle_option(name) - else: - self.fm.set_option_from_string(name, value) - - def tab(self, tabnum): # pylint: disable=too-many-return-statements - from ranger.gui.colorscheme import get_all_colorschemes - name, value, name_done = self.parse_setting_line() - settings = self.fm.settings - if not name: - return sorted(self.firstpart + setting for setting in settings) - if not value and not name_done: - return sorted(self.firstpart + setting for setting in settings - if setting.startswith(name)) - if not value: - value_completers = { - "colorscheme": - # Cycle through colorschemes when name, but no value is specified - lambda: sorted(self.firstpart + colorscheme for colorscheme - in get_all_colorschemes(self.fm)), - - "column_ratios": - lambda: self.firstpart + ",".join(map(str, settings[name])), - } - - def default_value_completer(): - return self.firstpart + str(settings[name]) - - return value_completers.get(name, default_value_completer)() - if bool in settings.types_of(name): - if 'true'.startswith(value.lower()): - return self.firstpart + 'True' - if 'false'.startswith(value.lower()): - return self.firstpart + 'False' - # Tab complete colorscheme values if incomplete value is present - if name == "colorscheme": - return sorted(self.firstpart + colorscheme for colorscheme - in get_all_colorschemes(self.fm) if colorscheme.startswith(value)) - return None - - -class setlocal(set_): - """:setlocal path=<regular expression> <option name>=<python expression> - - Gives an option a new value. - """ - PATH_RE_DQUOTED = re.compile(r'^setlocal\s+path="(.*?)"') - PATH_RE_SQUOTED = re.compile(r"^setlocal\s+path='(.*?)'") - PATH_RE_UNQUOTED = re.compile(r'^path=(.*?)$') - - def _re_shift(self, match): - if not match: - return None - path = os.path.expanduser(match.group(1)) - for _ in range(len(path.split())): - self.shift() - return path - - def execute(self): - path = self._re_shift(self.PATH_RE_DQUOTED.match(self.line)) - if path is None: - path = self._re_shift(self.PATH_RE_SQUOTED.match(self.line)) - if path is None: - path = self._re_shift(self.PATH_RE_UNQUOTED.match(self.arg(1))) - if path is None and self.fm.thisdir: - path = self.fm.thisdir.path - if not path: - return - - name, value, _ = self.parse_setting_line() - self.fm.set_option_from_string(name, value, localpath=path) - - -class setintag(set_): - """:setintag <tag or tags> <option name>=<option value> - - Sets an option for directories that are tagged with a specific tag. - """ - - def execute(self): - tags = self.arg(1) - self.shift() - name, value, _ = self.parse_setting_line() - self.fm.set_option_from_string(name, value, tags=tags) - - -class default_linemode(Command): - - def execute(self): - from ranger.container.fsobject import FileSystemObject - - if len(self.args) < 2: - self.fm.notify( - "Usage: default_linemode [path=<regexp> | tag=<tag(s)>] <linemode>", bad=True) - - # Extract options like "path=..." or "tag=..." from the command line - arg1 = self.arg(1) - method = "always" - argument = None - if arg1.startswith("path="): - method = "path" - argument = re.compile(arg1[5:]) - self.shift() - elif arg1.startswith("tag="): - method = "tag" - argument = arg1[4:] - self.shift() - - # Extract and validate the line mode from the command line - lmode = self.rest(1) - if lmode not in FileSystemObject.linemode_dict: - self.fm.notify( - "Invalid linemode: %s; should be %s" % ( - lmode, "/".join(FileSystemObject.linemode_dict)), - bad=True, - ) - - # Add the prepared entry to the fm.default_linemodes - entry = [method, argument, lmode] - self.fm.default_linemodes.appendleft(entry) - - # Redraw the columns - if self.fm.ui.browser: - for col in self.fm.ui.browser.columns: - col.need_redraw = True - - def tab(self, tabnum): - return (self.arg(0) + " " + lmode - for lmode in self.fm.thisfile.linemode_dict.keys() - if lmode.startswith(self.arg(1))) - - -class quit(Command): # pylint: disable=redefined-builtin - """:quit - - Closes the current tab, if there's only one tab. - Otherwise quits if there are no tasks in progress. - """ - def _exit_no_work(self): - if self.fm.loader.has_work(): - self.fm.notify('Not quitting: Tasks in progress: Use `quit!` to force quit') - else: - self.fm.exit() - - def execute(self): - if len(self.fm.tabs) >= 2: - self.fm.tab_close() - else: - self._exit_no_work() - - -class quit_bang(Command): - """:quit! - - Closes the current tab, if there's only one tab. - Otherwise force quits immediately. - """ - name = 'quit!' - allow_abbrev = False - - def execute(self): - if len(self.fm.tabs) >= 2: - self.fm.tab_close() - else: - self.fm.exit() - - -class quitall(Command): - """:quitall - - Quits if there are no tasks in progress. - """ - def _exit_no_work(self): - if self.fm.loader.has_work(): - self.fm.notify('Not quitting: Tasks in progress: Use `quitall!` to force quit') - else: - self.fm.exit() - - def execute(self): - self._exit_no_work() - - -class quitall_bang(Command): - """:quitall! - - Force quits immediately. - """ - name = 'quitall!' - allow_abbrev = False - - def execute(self): - self.fm.exit() - - -class terminal(Command): - """:terminal - - Spawns an "x-terminal-emulator" starting in the current directory. - """ - - def execute(self): - from ranger.ext.get_executables import get_term - self.fm.run(get_term(), flags='f') - - -class delete(Command): - """:delete - - Tries to delete the selection or the files passed in arguments (if any). - The arguments use a shell-like escaping. - - "Selection" is defined as all the "marked files" (by default, you - can mark files with space or v). If there are no marked files, - use the "current file" (where the cursor is) - - When attempting to delete non-empty directories or multiple - marked files, it will require a confirmation. - """ - - allow_abbrev = False - escape_macros_for_shell = True - - def execute(self): - import shlex - from functools import partial - - def is_directory_with_files(path): - return os.path.isdir(path) and not os.path.islink(path) and len(os.listdir(path)) > 0 - - if self.rest(1): - files = shlex.split(self.rest(1)) - many_files = (len(files) > 1 or is_directory_with_files(files[0])) - else: - cwd = self.fm.thisdir - tfile = self.fm.thisfile - if not cwd or not tfile: - self.fm.notify("Error: no file selected for deletion!", bad=True) - return - - # relative_path used for a user-friendly output in the confirmation. - files = [f.relative_path for f in self.fm.thistab.get_selection()] - many_files = (cwd.marked_items or is_directory_with_files(tfile.path)) - - confirm = self.fm.settings.confirm_on_delete - if confirm != 'never' and (confirm != 'multiple' or many_files): - self.fm.ui.console.ask( - "Confirm deletion of: %s (y/N)" % ', '.join(files), - partial(self._question_callback, files), - ('n', 'N', 'y', 'Y'), - ) - else: - # no need for a confirmation, just delete - self.fm.delete(files) - - def tab(self, tabnum): - return self._tab_directory_content() - - def _question_callback(self, files, answer): - if answer == 'y' or answer == 'Y': - self.fm.delete(files) - - -class jump_non(Command): - """:jump_non [-FLAGS...] - - Jumps to first non-directory if highlighted file is a directory and vice versa. - - Flags: - -r Jump in reverse order - -w Wrap around if reaching end of filelist - """ - def __init__(self, *args, **kwargs): - super(jump_non, self).__init__(*args, **kwargs) - - flags, _ = self.parse_flags() - self._flag_reverse = 'r' in flags - self._flag_wrap = 'w' in flags - - @staticmethod - def _non(fobj, is_directory): - return fobj.is_directory if not is_directory else not fobj.is_directory - - def execute(self): - tfile = self.fm.thisfile - passed = False - found_before = None - found_after = None - for fobj in self.fm.thisdir.files[::-1] if self._flag_reverse else self.fm.thisdir.files: - if fobj.path == tfile.path: - passed = True - continue - - if passed: - if self._non(fobj, tfile.is_directory): - found_after = fobj.path - break - elif not found_before and self._non(fobj, tfile.is_directory): - found_before = fobj.path - - if found_after: - self.fm.select_file(found_after) - elif self._flag_wrap and found_before: - self.fm.select_file(found_before) - - -class mark_tag(Command): - """:mark_tag [<tags>] - - Mark all tags that are tagged with either of the given tags. - When leaving out the tag argument, all tagged files are marked. - """ - do_mark = True - - def execute(self): - cwd = self.fm.thisdir - tags = self.rest(1).replace(" ", "") - if not self.fm.tags or not cwd.files: - return - for fileobj in cwd.files: - try: - tag = self.fm.tags.tags[fileobj.realpath] - except KeyError: - continue - if not tags or tag in tags: - cwd.mark_item(fileobj, val=self.do_mark) - self.fm.ui.status.need_redraw = True - self.fm.ui.need_redraw = True - - -class console(Command): - """:console <command> - - Open the console with the given command. - """ - - def execute(self): - position = None - if self.arg(1)[0:2] == '-p': - try: - position = int(self.arg(1)[2:]) - except ValueError: - pass - else: - self.shift() - self.fm.open_console(self.rest(1), position=position) - - -class load_copy_buffer(Command): - """:load_copy_buffer - - Load the copy buffer from datadir/copy_buffer - """ - copy_buffer_filename = 'copy_buffer' - - def execute(self): - import sys - from ranger.container.file import File - from os.path import exists - fname = self.fm.datapath(self.copy_buffer_filename) - unreadable = IOError if sys.version_info[0] < 3 else OSError - try: - fobj = open(fname, 'r') - except unreadable: - return self.fm.notify( - "Cannot open %s" % (fname or self.copy_buffer_filename), bad=True) - - self.fm.copy_buffer = set(File(g) - for g in fobj.read().split("\n") if exists(g)) - fobj.close() - self.fm.ui.redraw_main_column() - return None - - -class save_copy_buffer(Command): - """:save_copy_buffer - - Save the copy buffer to datadir/copy_buffer - """ - copy_buffer_filename = 'copy_buffer' - - def execute(self): - import sys - fname = None - fname = self.fm.datapath(self.copy_buffer_filename) - unwritable = IOError if sys.version_info[0] < 3 else OSError - try: - fobj = open(fname, 'w') - except unwritable: - return self.fm.notify("Cannot open %s" % - (fname or self.copy_buffer_filename), bad=True) - fobj.write("\n".join(fobj.path for fobj in self.fm.copy_buffer)) - fobj.close() - return None - - -class unmark_tag(mark_tag): - """:unmark_tag [<tags>] - - Unmark all tags that are tagged with either of the given tags. - When leaving out the tag argument, all tagged files are unmarked. - """ - do_mark = False - - -class mkdir(Command): - """:mkdir <dirname> - - Creates a directory with the name <dirname>. - """ - - def execute(self): - from os.path import join, expanduser, lexists - from os import makedirs - - dirname = join(self.fm.thisdir.path, expanduser(self.rest(1))) - if not lexists(dirname): - makedirs(dirname) - else: - self.fm.notify("file/directory exists!", bad=True) - - def tab(self, tabnum): - return self._tab_directory_content() - - -class touch(Command): - """:touch <fname> - - Creates a file with the name <fname>. - """ - - def execute(self): - from os.path import join, expanduser, lexists - - fname = join(self.fm.thisdir.path, expanduser(self.rest(1))) - if not lexists(fname): - open(fname, 'a').close() - else: - self.fm.notify("file/directory exists!", bad=True) - - def tab(self, tabnum): - return self._tab_directory_content() - - -class edit(Command): - """:edit <filename> - - Opens the specified file in vim - """ - - def execute(self): - if not self.arg(1): - self.fm.edit_file(self.fm.thisfile.path) - else: - self.fm.edit_file(self.rest(1)) - - def tab(self, tabnum): - return self._tab_directory_content() - - -class eval_(Command): - """:eval [-q] <python code> - - Evaluates the python code. - `fm' is a reference to the FM instance. - To display text, use the function `p'. - - Examples: - :eval fm - :eval len(fm.directories) - :eval p("Hello World!") - """ - name = 'eval' - resolve_macros = False - - def execute(self): - # The import is needed so eval() can access the ranger module - import ranger # NOQA pylint: disable=unused-import,unused-variable - if self.arg(1) == '-q': - code = self.rest(2) - quiet = True - else: - code = self.rest(1) - quiet = False - global cmd, fm, p, quantifier # pylint: disable=invalid-name,global-variable-undefined - fm = self.fm - cmd = self.fm.execute_console - p = fm.notify - quantifier = self.quantifier - try: - try: - result = eval(code) # pylint: disable=eval-used - except SyntaxError: - exec(code) # pylint: disable=exec-used - else: - if result and not quiet: - p(result) - except Exception as err: # pylint: disable=broad-except - fm.notify("The error `%s` was caused by evaluating the " - "following code: `%s`" % (err, code), bad=True) - - -class rename(Command): - """:rename <newname> - - Changes the name of the currently highlighted file to <newname> - """ - - def execute(self): - from ranger.container.file import File - from os import access - - new_name = self.rest(1) - - if not new_name: - return self.fm.notify('Syntax: rename <newname>', bad=True) - - if new_name == self.fm.thisfile.relative_path: - return None - - if access(new_name, os.F_OK): - return self.fm.notify("Can't rename: file already exists!", bad=True) - - if self.fm.rename(self.fm.thisfile, new_name): - file_new = File(new_name) - self.fm.bookmarks.update_path(self.fm.thisfile.path, file_new) - self.fm.tags.update_path(self.fm.thisfile.path, file_new.path) - self.fm.thisdir.pointed_obj = file_new - self.fm.thisfile = file_new - - return None - - def tab(self, tabnum): - return self._tab_directory_content() - - -class rename_append(Command): - """:rename_append [-FLAGS...] - - Opens the console with ":rename <current file>" with the cursor positioned - before the file extension. - - Flags: - -a Position before all extensions - -r Remove everything before extensions - """ - def __init__(self, *args, **kwargs): - super(rename_append, self).__init__(*args, **kwargs) - - flags, _ = self.parse_flags() - self._flag_ext_all = 'a' in flags - self._flag_remove = 'r' in flags - - def execute(self): - from ranger import MACRO_DELIMITER, MACRO_DELIMITER_ESC - - tfile = self.fm.thisfile - relpath = tfile.relative_path.replace(MACRO_DELIMITER, MACRO_DELIMITER_ESC) - basename = tfile.basename.replace(MACRO_DELIMITER, MACRO_DELIMITER_ESC) - - if basename.find('.') <= 0: - self.fm.open_console('rename ' + relpath) - return - - if self._flag_ext_all: - pos_ext = re.search(r'[^.]+', basename).end(0) - else: - pos_ext = basename.rindex('.') - pos = len(relpath) - len(basename) + pos_ext - - if self._flag_remove: - relpath = relpath[:-len(basename)] + basename[pos_ext:] - pos -= pos_ext - - self.fm.open_console('rename ' + relpath, position=(7 + pos)) - - -class chmod(Command): - """:chmod <octal number> - - Sets the permissions of the selection to the octal number. - - The octal number is between 0 and 777. The digits specify the - permissions for the user, the group and others. - - A 1 permits execution, a 2 permits writing, a 4 permits reading. - Add those numbers to combine them. So a 7 permits everything. - """ - - def execute(self): - mode_str = self.rest(1) - if not mode_str: - if not self.quantifier: - self.fm.notify("Syntax: chmod <octal number>", bad=True) - return - mode_str = str(self.quantifier) - - try: - mode = int(mode_str, 8) - if mode < 0 or mode > 0o777: - raise ValueError - except ValueError: - self.fm.notify("Need an octal number between 0 and 777!", bad=True) - return - - for fobj in self.fm.thistab.get_selection(): - try: - os.chmod(fobj.path, mode) - except OSError as ex: - self.fm.notify(ex) - - # reloading directory. maybe its better to reload the selected - # files only. - self.fm.thisdir.content_outdated = True - - -class bulkrename(Command): - """:bulkrename - - This command opens a list of selected files in an external editor. - After you edit and save the file, it will generate a shell script - which does bulk renaming according to the changes you did in the file. - - This shell script is opened in an editor for you to review. - After you close it, it will be executed. - """ - - def execute(self): # pylint: disable=too-many-locals,too-many-statements - import sys - import tempfile - from ranger.container.file import File - from ranger.ext.shell_escape import shell_escape as esc - py3 = sys.version_info[0] >= 3 - - # Create and edit the file list - filenames = [f.relative_path for f in self.fm.thistab.get_selection()] - listfile = tempfile.NamedTemporaryFile(delete=False) - listpath = listfile.name - - if py3: - listfile.write("\n".join(filenames).encode("utf-8")) - else: - listfile.write("\n".join(filenames)) - listfile.close() - self.fm.execute_file([File(listpath)], app='editor') - listfile = open(listpath, 'r') - new_filenames = listfile.read().split("\n") - listfile.close() - os.unlink(listpath) - if all(a == b for a, b in zip(filenames, new_filenames)): - self.fm.notify("No renaming to be done!") - return - - # Generate script - cmdfile = tempfile.NamedTemporaryFile() - script_lines = [] - script_lines.append("# This file will be executed when you close the editor.\n") - script_lines.append("# Please double-check everything, clear the file to abort.\n") - script_lines.extend("mv -vi -- %s %s\n" % (esc(old), esc(new)) - for old, new in zip(filenames, new_filenames) if old != new) - script_content = "".join(script_lines) - if py3: - cmdfile.write(script_content.encode("utf-8")) - else: - cmdfile.write(script_content) - cmdfile.flush() - - # Open the script and let the user review it, then check if the script - # was modified by the user - self.fm.execute_file([File(cmdfile.name)], app='editor') - cmdfile.seek(0) - script_was_edited = (script_content != cmdfile.read()) - - # Do the renaming - self.fm.run(['/bin/sh', cmdfile.name], flags='w') - cmdfile.close() - - # Retag the files, but only if the script wasn't changed during review, - # because only then we know which are the source and destination files. - if not script_was_edited: - tags_changed = False - for old, new in zip(filenames, new_filenames): - if old != new: - oldpath = self.fm.thisdir.path + '/' + old - newpath = self.fm.thisdir.path + '/' + new - if oldpath in self.fm.tags: - old_tag = self.fm.tags.tags[oldpath] - self.fm.tags.remove(oldpath) - self.fm.tags.tags[newpath] = old_tag - tags_changed = True - if tags_changed: - self.fm.tags.dump() - else: - fm.notify("files have not been retagged") - - -class relink(Command): - """:relink <newpath> - - Changes the linked path of the currently highlighted symlink to <newpath> - """ - - def execute(self): - new_path = self.rest(1) - tfile = self.fm.thisfile - - if not new_path: - return self.fm.notify('Syntax: relink <newpath>', bad=True) - - if not tfile.is_link: - return self.fm.notify('%s is not a symlink!' % tfile.relative_path, bad=True) - - if new_path == os.readlink(tfile.path): - return None - - try: - os.remove(tfile.path) - os.symlink(new_path, tfile.path) - except OSError as err: - self.fm.notify(err) - - self.fm.reset() - self.fm.thisdir.pointed_obj = tfile - self.fm.thisfile = tfile - - return None - - def tab(self, tabnum): - if not self.rest(1): - return self.line + os.readlink(self.fm.thisfile.path) - return self._tab_directory_content() - - -class help_(Command): - """:help - - Display ranger's manual page. - """ - name = 'help' - - def execute(self): - def callback(answer): - if answer == "q": - return - elif answer == "m": - self.fm.display_help() - elif answer == "c": - self.fm.dump_commands() - elif answer == "k": - self.fm.dump_keybindings() - elif answer == "s": - self.fm.dump_settings() - - self.fm.ui.console.ask( - "View [m]an page, [k]ey bindings, [c]ommands or [s]ettings? (press q to abort)", - callback, - list("mqkcs") - ) - - -class copymap(Command): - """:copymap <keys> <newkeys1> [<newkeys2>...] - - Copies a "browser" keybinding from <keys> to <newkeys> - """ - context = 'browser' - - def execute(self): - if not self.arg(1) or not self.arg(2): - return self.fm.notify("Not enough arguments", bad=True) - - for arg in self.args[2:]: - self.fm.ui.keymaps.copy(self.context, self.arg(1), arg) - - return None - - -class copypmap(copymap): - """:copypmap <keys> <newkeys1> [<newkeys2>...] - - Copies a "pager" keybinding from <keys> to <newkeys> - """ - context = 'pager' - - -class copycmap(copymap): - """:copycmap <keys> <newkeys1> [<newkeys2>...] - - Copies a "console" keybinding from <keys> to <newkeys> - """ - context = 'console' - - -class copytmap(copymap): - """:copycmap <keys> <newkeys1> [<newkeys2>...] - - Copies a "taskview" keybinding from <keys> to <newkeys> - """ - context = 'taskview' - - -class unmap(Command): - """:unmap <keys> [<keys2>, ...] - - Remove the given "browser" mappings - """ - context = 'browser' - - def execute(self): - for arg in self.args[1:]: - self.fm.ui.keymaps.unbind(self.context, arg) - - -class cunmap(unmap): - """:cunmap <keys> [<keys2>, ...] - - Remove the given "console" mappings - """ - context = 'browser' - - -class punmap(unmap): - """:punmap <keys> [<keys2>, ...] - - Remove the given "pager" mappings - """ - context = 'pager' - - -class tunmap(unmap): - """:tunmap <keys> [<keys2>, ...] - - Remove the given "taskview" mappings - """ - context = 'taskview' - - -class map_(Command): - """:map <keysequence> <command> - - Maps a command to a keysequence in the "browser" context. - - Example: - map j move down - map J move down 10 - """ - name = 'map' - context = 'browser' - resolve_macros = False - - def execute(self): - if not self.arg(1) or not self.arg(2): - self.fm.notify("Syntax: {0} <keysequence> <command>".format(self.get_name()), bad=True) - return - - self.fm.ui.keymaps.bind(self.context, self.arg(1), self.rest(2)) - - -class cmap(map_): - """:cmap <keysequence> <command> - - Maps a command to a keysequence in the "console" context. - - Example: - cmap <ESC> console_close - cmap <C-x> console_type test - """ - context = 'console' - - -class tmap(map_): - """:tmap <keysequence> <command> - - Maps a command to a keysequence in the "taskview" context. - """ - context = 'taskview' - - -class pmap(map_): - """:pmap <keysequence> <command> - - Maps a command to a keysequence in the "pager" context. - """ - context = 'pager' - - -class scout(Command): - """:scout [-FLAGS...] <pattern> - - Swiss army knife command for searching, traveling and filtering files. - - Flags: - -a Automatically open a file on unambiguous match - -e Open the selected file when pressing enter - -f Filter files that match the current search pattern - -g Interpret pattern as a glob pattern - -i Ignore the letter case of the files - -k Keep the console open when changing a directory with the command - -l Letter skipping; e.g. allow "rdme" to match the file "readme" - -m Mark the matching files after pressing enter - -M Unmark the matching files after pressing enter - -p Permanent filter: hide non-matching files after pressing enter - -r Interpret pattern as a regular expression pattern - -s Smart case; like -i unless pattern contains upper case letters - -t Apply filter and search pattern as you type - -v Inverts the match - - Multiple flags can be combined. For example, ":scout -gpt" would create - a :filter-like command using globbing. - """ - # pylint: disable=bad-whitespace - AUTO_OPEN = 'a' - OPEN_ON_ENTER = 'e' - FILTER = 'f' - SM_GLOB = 'g' - IGNORE_CASE = 'i' - KEEP_OPEN = 'k' - SM_LETTERSKIP = 'l' - MARK = 'm' - UNMARK = 'M' - PERM_FILTER = 'p' - SM_REGEX = 'r' - SMART_CASE = 's' - AS_YOU_TYPE = 't' - INVERT = 'v' - # pylint: enable=bad-whitespace - - def __init__(self, *args, **kwargs): - super(scout, self).__init__(*args, **kwargs) - self._regex = None - self.flags, self.pattern = self.parse_flags() - - def execute(self): # pylint: disable=too-many-branches - thisdir = self.fm.thisdir - flags = self.flags - pattern = self.pattern - regex = self._build_regex() - count = self._count(move=True) - - self.fm.thistab.last_search = regex - self.fm.set_search_method(order="search") - - if (self.MARK in flags or self.UNMARK in flags) and thisdir.files: - value = flags.find(self.MARK) > flags.find(self.UNMARK) - if self.FILTER in flags: - for fobj in thisdir.files: - thisdir.mark_item(fobj, value) - else: - for fobj in thisdir.files: - if regex.search(fobj.relative_path): - thisdir.mark_item(fobj, value) - - if self.PERM_FILTER in flags: - thisdir.filter = regex if pattern else None - - # clean up: - self.cancel() - - if self.OPEN_ON_ENTER in flags or \ - (self.AUTO_OPEN in flags and count == 1): - if pattern == '..': - self.fm.cd(pattern) - else: - self.fm.move(right=1) - if self.quickly_executed: - self.fm.block_input(0.5) - - if self.KEEP_OPEN in flags and thisdir != self.fm.thisdir: - # reopen the console: - if not pattern: - self.fm.open_console(self.line) - else: - self.fm.open_console(self.line[0:-len(pattern)]) - - if self.quickly_executed and thisdir != self.fm.thisdir and pattern != "..": - self.fm.block_input(0.5) - - def cancel(self): - self.fm.thisdir.temporary_filter = None - self.fm.thisdir.refilter() - - def quick(self): - asyoutype = self.AS_YOU_TYPE in self.flags - if self.FILTER in self.flags: - self.fm.thisdir.temporary_filter = self._build_regex() - if self.PERM_FILTER in self.flags and asyoutype: - self.fm.thisdir.filter = self._build_regex() - if self.FILTER in self.flags or self.PERM_FILTER in self.flags: - self.fm.thisdir.refilter() - if self._count(move=asyoutype) == 1 and self.AUTO_OPEN in self.flags: - return True - return False - - def tab(self, tabnum): - self._count(move=True, offset=tabnum) - - def _build_regex(self): - if self._regex is not None: - return self._regex - - frmat = "%s" - flags = self.flags - pattern = self.pattern - - if pattern == ".": - return re.compile("") - - # Handle carets at start and dollar signs at end separately - if pattern.startswith('^'): - pattern = pattern[1:] - frmat = "^" + frmat - if pattern.endswith('$'): - pattern = pattern[:-1] - frmat += "$" - - # Apply one of the search methods - if self.SM_REGEX in flags: - regex = pattern - elif self.SM_GLOB in flags: - regex = re.escape(pattern).replace("\\*", ".*").replace("\\?", ".") - elif self.SM_LETTERSKIP in flags: - regex = ".*".join(re.escape(c) for c in pattern) - else: - regex = re.escape(pattern) - - regex = frmat % regex - - # Invert regular expression if necessary - if self.INVERT in flags: - regex = "^(?:(?!%s).)*$" % regex - - # Compile Regular Expression - # pylint: disable=no-member - options = re.UNICODE - if self.IGNORE_CASE in flags or self.SMART_CASE in flags and \ - pattern.islower(): - options |= re.IGNORECASE - # pylint: enable=no-member - try: - self._regex = re.compile(regex, options) - except re.error: - self._regex = re.compile("") - return self._regex - - def _count(self, move=False, offset=0): - count = 0 - cwd = self.fm.thisdir - pattern = self.pattern - - if not pattern or not cwd.files: - return 0 - if pattern == '.': - return 0 - if pattern == '..': - return 1 - - deq = deque(cwd.files) - deq.rotate(-cwd.pointer - offset) - i = offset - regex = self._build_regex() - for fsobj in deq: - if regex.search(fsobj.relative_path): - count += 1 - if move and count == 1: - cwd.move(to=(cwd.pointer + i) % len(cwd.files)) - self.fm.thisfile = cwd.pointed_obj - if count > 1: - return count - i += 1 - - return count == 1 - - -class narrow(Command): - """ - :narrow - - Show only the files selected right now. If no files are selected, - disable narrowing. - """ - def execute(self): - if self.fm.thisdir.marked_items: - selection = [f.basename for f in self.fm.thistab.get_selection()] - self.fm.thisdir.narrow_filter = selection - else: - self.fm.thisdir.narrow_filter = None - self.fm.thisdir.refilter() - - -class filter_inode_type(Command): - """ - :filter_inode_type [dfl] - - Displays only the files of specified inode type. Parameters - can be combined. - - d display directories - f display files - l display links - """ - - def execute(self): - if not self.arg(1): - self.fm.thisdir.inode_type_filter = "" - else: - self.fm.thisdir.inode_type_filter = self.arg(1) - self.fm.thisdir.refilter() - - -class filter_stack(Command): - """ - :filter_stack ... - - Manages the filter stack. - - filter_stack add FILTER_TYPE ARGS... - filter_stack pop - filter_stack decompose - filter_stack rotate [N=1] - filter_stack clear - filter_stack show - """ - def execute(self): - from ranger.core.filter_stack import SIMPLE_FILTERS, FILTER_COMBINATORS - - subcommand = self.arg(1) - - if subcommand == "add": - try: - self.fm.thisdir.filter_stack.append( - SIMPLE_FILTERS[self.arg(2)](self.rest(3)) - ) - except KeyError: - FILTER_COMBINATORS[self.arg(2)](self.fm.thisdir.filter_stack) - elif subcommand == "pop": - self.fm.thisdir.filter_stack.pop() - elif subcommand == "decompose": - inner_filters = self.fm.thisdir.filter_stack.pop().decompose() - if inner_filters: - self.fm.thisdir.filter_stack.extend(inner_filters) - elif subcommand == "clear": - self.fm.thisdir.filter_stack = [] - elif subcommand == "rotate": - rotate_by = int(self.arg(2) or 1) - self.fm.thisdir.filter_stack = ( - self.fm.thisdir.filter_stack[-rotate_by:] - + self.fm.thisdir.filter_stack[:-rotate_by] - ) - elif subcommand == "show": - stack = list(map(str, self.fm.thisdir.filter_stack)) - pager = self.fm.ui.open_pager() - pager.set_source(["Filter stack: "] + stack) - pager.move(to=100, percentage=True) - return - else: - self.fm.notify( - "Unknown subcommand: {}".format(subcommand), - bad=True - ) - return - - self.fm.thisdir.refilter() - - -class grep(Command): - """:grep <string> - - Looks for a string in all marked files or directories - """ - - def execute(self): - if self.rest(1): - action = ['grep', '--line-number'] - action.extend(['-e', self.rest(1), '-r']) - action.extend(f.path for f in self.fm.thistab.get_selection()) - self.fm.execute_command(action, flags='p') - - -class flat(Command): - """ - :flat <level> - - Flattens the directory view up to the specified level. - - -1 fully flattened - 0 remove flattened view - """ - - def execute(self): - try: - level_str = self.rest(1) - level = int(level_str) - except ValueError: - level = self.quantifier - if level is None: - self.fm.notify("Syntax: flat <level>", bad=True) - return - if level < -1: - self.fm.notify("Need an integer number (-1, 0, 1, ...)", bad=True) - self.fm.thisdir.unload() - self.fm.thisdir.flat = level - self.fm.thisdir.load_content() - -# Version control commands -# -------------------------------- - - -class stage(Command): - """ - :stage - - Stage selected files for the corresponding version control system - """ - - def execute(self): - from ranger.ext.vcs import VcsError - - if self.fm.thisdir.vcs and self.fm.thisdir.vcs.track: - filelist = [f.path for f in self.fm.thistab.get_selection()] - try: - self.fm.thisdir.vcs.action_add(filelist) - except VcsError as ex: - self.fm.notify('Unable to stage files: {0}'.format(ex)) - self.fm.ui.vcsthread.process(self.fm.thisdir) - else: - self.fm.notify('Unable to stage files: Not in repository') - - -class unstage(Command): - """ - :unstage - - Unstage selected files for the corresponding version control system - """ - - def execute(self): - from ranger.ext.vcs import VcsError - - if self.fm.thisdir.vcs and self.fm.thisdir.vcs.track: - filelist = [f.path for f in self.fm.thistab.get_selection()] - try: - self.fm.thisdir.vcs.action_reset(filelist) - except VcsError as ex: - self.fm.notify('Unable to unstage files: {0}'.format(ex)) - self.fm.ui.vcsthread.process(self.fm.thisdir) - else: - self.fm.notify('Unable to unstage files: Not in repository') - -# Metadata commands -# -------------------------------- - - -class prompt_metadata(Command): - """ - :prompt_metadata <key1> [<key2> [<key3> ...]] - - Prompt the user to input metadata for multiple keys in a row. - """ - - _command_name = "meta" - _console_chain = None - - def execute(self): - prompt_metadata._console_chain = self.args[1:] - self._process_command_stack() - - def _process_command_stack(self): - if prompt_metadata._console_chain: - key = prompt_metadata._console_chain.pop() - self._fill_console(key) - else: - for col in self.fm.ui.browser.columns: - col.need_redraw = True - - def _fill_console(self, key): - metadata = self.fm.metadata.get_metadata(self.fm.thisfile.path) - if key in metadata and metadata[key]: - existing_value = metadata[key] - else: - existing_value = "" - text = "%s %s %s" % (self._command_name, key, existing_value) - self.fm.open_console(text, position=len(text)) - - -class meta(prompt_metadata): - """ - :meta <key> [<value>] - - Change metadata of a file. Deletes the key if value is empty. - """ - - def execute(self): - key = self.arg(1) - update_dict = dict() - update_dict[key] = self.rest(2) - selection = self.fm.thistab.get_selection() - for fobj in selection: - self.fm.metadata.set_metadata(fobj.path, update_dict) - self._process_command_stack() - - def tab(self, tabnum): - key = self.arg(1) - metadata = self.fm.metadata.get_metadata(self.fm.thisfile.path) - if key in metadata and metadata[key]: - return [" ".join([self.arg(0), self.arg(1), metadata[key]])] - return [self.arg(0) + " " + k for k in sorted(metadata) - if k.startswith(self.arg(1))] - - -class linemode(default_linemode): - """ - :linemode <mode> - - Change what is displayed as a filename. - - - "mode" may be any of the defined linemodes (see: ranger.core.linemode). - "normal" is mapped to "filename". - """ - - def execute(self): - mode = self.arg(1) - - if mode == "normal": - from ranger.core.linemode import DEFAULT_LINEMODE - mode = DEFAULT_LINEMODE - - if mode not in self.fm.thisfile.linemode_dict: - self.fm.notify("Unhandled linemode: `%s'" % mode, bad=True) - return - - self.fm.thisdir.set_linemode_of_children(mode) - - # Ask the browsercolumns to redraw - for col in self.fm.ui.browser.columns: - col.need_redraw = True - - -class yank(Command): - """:yank [name|dir|path] - - Copies the file's name (default), directory or path into both the primary X - selection and the clipboard. - """ - - modes = { - '': 'basename', - 'name_without_extension': 'basename_without_extension', - 'name': 'basename', - 'dir': 'dirname', - 'path': 'path', - } - - def execute(self): - import subprocess - - def clipboards(): - from ranger.ext.get_executables import get_executables - clipboard_managers = { - 'xclip': [ - ['xclip'], - ['xclip', '-selection', 'clipboard'], - ], - 'xsel': [ - ['xsel'], - ['xsel', '-b'], - ], - 'pbcopy': [ - ['pbcopy'], - ], - } - ordered_managers = ['pbcopy', 'xclip', 'xsel'] - executables = get_executables() - for manager in ordered_managers: - if manager in executables: - return clipboard_managers[manager] - return [] - - clipboard_commands = clipboards() - - mode = self.modes[self.arg(1)] - selection = self.get_selection_attr(mode) - - new_clipboard_contents = "\n".join(selection) - for command in clipboard_commands: - process = subprocess.Popen(command, universal_newlines=True, - stdin=subprocess.PIPE) - process.communicate(input=new_clipboard_contents) - - def get_selection_attr(self, attr): - return [getattr(item, attr) for item in - self.fm.thistab.get_selection()] - - def tab(self, tabnum): - return ( - self.start(1) + mode for mode - in sorted(self.modes.keys()) - if mode - ) diff --git a/dotfiles/common/.config/ranger/rc.conf b/dotfiles/common/.config/ranger/rc.conf deleted file mode 100644 index 89d09ff..0000000 --- a/dotfiles/common/.config/ranger/rc.conf +++ /dev/null @@ -1,790 +0,0 @@ - -# =================================================================== -# This file contains the default startup commands for ranger. -# To change them, it is recommended to create either /etc/ranger/rc.conf -# (system-wide) or ~/.config/ranger/rc.conf (per user) and add your custom -# commands there. -# -# If you copy this whole file there, you may want to set the environment -# variable RANGER_LOAD_DEFAULT_RC to FALSE to avoid loading it twice. -# -# The purpose of this file is mainly to define keybindings and settings. -# For running more complex python code, please create a plugin in "plugins/" or -# a command in "commands.py". -# -# Each line is a command that will be run before the user interface -# is initialized. As a result, you can not use commands which rely -# on the UI such as :delete or :mark. -# =================================================================== - -# =================================================================== -# == Options -# =================================================================== - -# Which viewmode should be used? Possible values are: -# miller: Use miller columns which show multiple levels of the hierarchy -# multipane: Midnight-commander like multipane view showing all tabs next -# to each other -set viewmode miller -#set viewmode multipane - -# How many columns are there, and what are their relative widths? -set column_ratios 1,3,4 - -# Which files should be hidden? (regular expression) -set hidden_filter ^\.|\.(?:pyc|pyo|bak|swp)$|^lost\+found$|^__(py)?cache__$ - -# Show hidden files? You can toggle this by typing 'zh' -set show_hidden false - -# Ask for a confirmation when running the "delete" command? -# Valid values are "always", "never", "multiple" (default) -# With "multiple", ranger will ask only if you delete multiple files at once. -set confirm_on_delete multiple - -# Use non-default path for file preview script? -# ranger ships with scope.sh, a script that calls external programs (see -# README.md for dependencies) to preview images, archives, etc. -set preview_script ~/.config/ranger/scope.sh - -# Use the external preview script or display simple plain text or image previews? -set use_preview_script true - -# Automatically count files in the directory, even before entering them? -set automatically_count_files true - -# Open all images in this directory when running certain image viewers -# like feh or sxiv? You can still open selected files by marking them. -set open_all_images true - -# Be aware of version control systems and display information. -set vcs_aware true - -# State of the four backends git, hg, bzr, svn. The possible states are -# disabled, local (only show local info), enabled (show local and remote -# information). -set vcs_backend_git enabled -set vcs_backend_hg disabled -set vcs_backend_bzr disabled -set vcs_backend_svn disabled - -# Use one of the supported image preview protocols -set preview_images true - -# Set the preview image method. Supported methods: -# -# * w3m (default): -# Preview images in full color with the external command "w3mimgpreview"? -# This requires the console web browser "w3m" and a supported terminal. -# It has been successfully tested with "xterm" and "urxvt" without tmux. -# -# * iterm2: -# Preview images in full color using iTerm2 image previews -# (http://iterm2.com/images.html). This requires using iTerm2 compiled -# with image preview support. -# -# This feature relies on the dimensions of the terminal's font. By default, a -# width of 8 and height of 11 are used. To use other values, set the options -# iterm2_font_width and iterm2_font_height to the desired values. -# -# * terminology: -# Previews images in full color in the terminology terminal emulator. -# Supports a wide variety of formats, even vector graphics like svg. -# -# * urxvt: -# Preview images in full color using urxvt image backgrounds. This -# requires using urxvt compiled with pixbuf support. -# -# * urxvt-full: -# The same as urxvt but utilizing not only the preview pane but the -# whole terminal window. -# -# * kitty: -# Preview images in full color using kitty image protocol. -# Requires python PIL or pillow library. -# If ranger does not share the local filesystem with kitty -# the transfer method is changed to encode the whole image; -# while slower, this allows remote previews, -# for example during an ssh session. -# Tmux is unsupported. -set preview_images_method ueberzug - -# Delay in seconds before displaying an image with the w3m method. -# Increase it in case of experiencing display corruption. -set w3m_delay 0.05 - -# Default iTerm2 font size (see: preview_images_method: iterm2) -set iterm2_font_width 8 -set iterm2_font_height 11 - -# Use a unicode "..." character to mark cut-off filenames? -set unicode_ellipsis false - -# BIDI support - try to properly display file names in RTL languages (Hebrew, Arabic). -# Requires the python-bidi pip package -set bidi_support false - -# Show dotfiles in the bookmark preview box? -set show_hidden_bookmarks true - -# Which colorscheme to use? These colorschemes are available by default: -# default, jungle, snow, solarized -# set colorscheme gruvbox - -# Preview files on the rightmost column? -# And collapse (shrink) the last column if there is nothing to preview? -set preview_files true -set preview_directories true -set collapse_preview true - -# Save the console history on exit? -set save_console_history true - -# Draw the status bar on top of the browser window (default: bottom) -set status_bar_on_top false - -# Draw a progress bar in the status bar which displays the average state of all -# currently running tasks which support progress bars? -set draw_progress_bar_in_status_bar true - -# Draw borders around columns? (separators, outline, both, or none) -# Separators are vertical lines between columns. -# Outline draws a box around all the columns. -# Both combines the two. -set draw_borders none - -# Display the directory name in tabs? -set dirname_in_tabs false - -# Enable the mouse support? -set mouse_enabled true - -# Display the file size in the main column or status bar? -set display_size_in_main_column true -set display_size_in_status_bar true - -# Display the free disk space in the status bar? -set display_free_space_in_status_bar true - -# Display files tags in all columns or only in main column? -set display_tags_in_all_columns true - -# Set a title for the window? -set update_title false - -# Set the title to "ranger" in the tmux program? -set update_tmux_title true - -# Shorten the title if it gets long? The number defines how many -# directories are displayed at once, 0 turns off this feature. -set shorten_title 3 - -# Show hostname in titlebar? -set hostname_in_titlebar true - -# Abbreviate $HOME with ~ in the titlebar (first line) of ranger? -set tilde_in_titlebar false - -# How many directory-changes or console-commands should be kept in history? -set max_history_size 20 -set max_console_history_size 50 - -# Try to keep so much space between the top/bottom border when scrolling: -set scroll_offset 8 - -# Flush the input after each key hit? (Noticeable when ranger lags) -set flushinput true - -# Padding on the right when there's no preview? -# This allows you to click into the space to run the file. -set padding_right true - -# Save bookmarks (used with mX and `X) instantly? -# This helps to synchronize bookmarks between multiple ranger -# instances but leads to *slight* performance loss. -# When false, bookmarks are saved when ranger is exited. -set autosave_bookmarks true - -# Save the "`" bookmark to disk. This can be used to switch to the last -# directory by typing "``". -set save_backtick_bookmark true - -# You can display the "real" cumulative size of directories by using the -# command :get_cumulative_size or typing "dc". The size is expensive to -# calculate and will not be updated automatically. You can choose -# to update it automatically though by turning on this option: -set autoupdate_cumulative_size false - -# Turning this on makes sense for screen readers: -set show_cursor false - -# One of: size, natural, basename, atime, ctime, mtime, type, random -set sort natural - -# Additional sorting options -set sort_reverse false -set sort_case_insensitive true -set sort_directories_first true -set sort_unicode false - -# Enable this if key combinations with the Alt Key don't work for you. -# (Especially on xterm) -set xterm_alt_key false - -# Whether to include bookmarks in cd command -set cd_bookmarks true - -# Changes case sensitivity for the cd command tab completion -set cd_tab_case sensitive - -# Use fuzzy tab completion with the "cd" command. For example, -# ":cd /u/lo/b<tab>" expands to ":cd /usr/local/bin". -set cd_tab_fuzzy false - -# Avoid previewing files larger than this size, in bytes. Use a value of 0 to -# disable this feature. -set preview_max_size 0 - -# The key hint lists up to this size have their sublists expanded. -# Otherwise the submaps are replaced with "...". -set hint_collapse_threshold 10 - -# Add the highlighted file to the path in the titlebar -set show_selection_in_titlebar true - -# The delay that ranger idly waits for user input, in milliseconds, with a -# resolution of 100ms. Lower delay reduces lag between directory updates but -# increases CPU load. -set idle_delay 2000 - -# When the metadata manager module looks for metadata, should it only look for -# a ".metadata.json" file in the current directory, or do a deep search and -# check all directories above the current one as well? -set metadata_deep_search false - -# Clear all existing filters when leaving a directory -set clear_filters_on_dir_change false - -# Disable displaying line numbers in main column. -# Possible values: false, absolute, relative. -set line_numbers false - -# When line_numbers=relative show the absolute line number in the -# current line. -set relative_current_zero false - -# Start line numbers from 1 instead of 0 -set one_indexed false - -# Save tabs on exit -set save_tabs_on_exit false - -# Enable scroll wrapping - moving down while on the last item will wrap around to -# the top and vice versa. -set wrap_scroll false - -# Set the global_inode_type_filter to nothing. Possible options: d, f and l for -# directories, files and symlinks respectively. -set global_inode_type_filter - -# This setting allows to freeze the list of files to save I/O bandwidth. It -# should be 'false' during start-up, but you can toggle it by pressing F. -set freeze_files false - -# =================================================================== -# == Local Options -# =================================================================== -# You can set local options that only affect a single directory. - -# Examples: -# setlocal path=~/downloads sort mtime - -# =================================================================== -# == Command Aliases in the Console -# =================================================================== - -alias e edit -alias q quit -alias q! quit! -alias qa quitall -alias qa! quitall! -alias qall quitall -alias qall! quitall! -alias setl setlocal - -alias filter scout -prts -alias find scout -aets -alias mark scout -mr -alias unmark scout -Mr -alias search scout -rs -alias search_inc scout -rts -alias travel scout -aefklst - -# =================================================================== -# == Define keys for the browser -# =================================================================== - -# Basic -map Q quitall -map q quit -copymap q ZZ ZQ - -map R reload_cwd -map F set freeze_files! -map <C-r> reset -map <C-l> redraw_window -map <C-c> abort -map <esc> change_mode normal -map ~ set viewmode! - -map i display_file -map ? help -map W display_log -map w taskview_open -map S shell $SHELL - -map : console -map ; console -map ! console shell%space -map @ console -p6 shell %%s -map # console shell -p%space -map s console shell%space -map r chain draw_possible_programs; console open_with%%space -map f console find%space -map cd console cd%space - -map <C-p> chain console; eval fm.ui.console.history_move(-1) - -# Change the line mode -map Mf linemode filename -map Mi linemode fileinfo -map Mm linemode mtime -map Mp linemode permissions -map Ms linemode sizemtime -map Mt linemode metatitle - -# Tagging / Marking -map t tag_toggle -map ut tag_remove -map "<any> tag_toggle tag=%any -map <Space> mark_files toggle=True -map v mark_files all=True toggle=True -map uv mark_files all=True val=False -map V toggle_visual_mode -map uV toggle_visual_mode reverse=True - -# For the nostalgics: Midnight Commander bindings -map <F1> help -map <F2> rename_append -map <F3> display_file -map <F4> edit -map <F5> copy -map <F6> cut -map <F7> console mkdir%space -map <F8> console delete -map <F10> exit - -# In case you work on a keyboard with dvorak layout -map <UP> move up=1 -map <DOWN> move down=1 -map <LEFT> move left=1 -map <RIGHT> move right=1 -map <HOME> move to=0 -map <END> move to=-1 -map <PAGEDOWN> move down=1 pages=True -map <PAGEUP> move up=1 pages=True -map <CR> move right=1 -#map <DELETE> console delete -map <INSERT> console touch%space - -# VIM-like -copymap <UP> k -copymap <DOWN> j -copymap <LEFT> h -copymap <RIGHT> l -copymap <HOME> gg -copymap <END> G -copymap <PAGEDOWN> <C-F> -copymap <PAGEUP> <C-B> - -map J move down=0.5 pages=True -map K move up=0.5 pages=True -copymap J <C-D> -copymap K <C-U> - -# Jumping around -map H history_go -1 -map L history_go 1 -map ] move_parent 1 -map [ move_parent -1 -map } traverse -map { traverse_backwards -map ) jump_non - -map gh cd ~ -map ge cd /etc -map gu cd /usr -map gd cd /dev -map gl cd -r . -map gL cd -r %f -map go cd /opt -map gv cd /var -map gi eval fm.cd('/run/media/' + os.getenv('USER')) -map gM cd /media -map gs cd /srv -map gp cd /tmp -map gR eval fm.cd(ranger.RANGERDIR) -map g/ cd / -map g? cd /usr/share/doc/ranger - -# External Programs -map E edit -map du shell -p du --max-depth=1 -h --apparent-size -map dU shell -p du --max-depth=1 -h --apparent-size | sort -rh -map yp yank path -map yd yank dir -map yn yank name -map y. yank name_without_extension - -# Filesystem Operations -map = chmod - -map cw console rename%space -map a rename_append -map A eval fm.open_console('rename ' + fm.thisfile.relative_path.replace("%", "%%")) -map I eval fm.open_console('rename ' + fm.thisfile.relative_path.replace("%", "%%"), position=7) - -map pp paste -map po paste overwrite=True -map pP paste append=True -map pO paste overwrite=True append=True -map pl paste_symlink relative=False -map pL paste_symlink relative=True -map phl paste_hardlink -map pht paste_hardlinked_subtree - -map dd console delete - -map da cut mode=add -map dr cut mode=remove -map dt cut mode=toggle - -map yy copy -map uy uncut -map ya copy mode=add -map yr copy mode=remove -map yt copy mode=toggle - -# Temporary workarounds -map dgg eval fm.cut(dirarg=dict(to=0), narg=quantifier) -map dG eval fm.cut(dirarg=dict(to=-1), narg=quantifier) -map dj eval fm.cut(dirarg=dict(down=1), narg=quantifier) -map dk eval fm.cut(dirarg=dict(up=1), narg=quantifier) -map ygg eval fm.copy(dirarg=dict(to=0), narg=quantifier) -map yG eval fm.copy(dirarg=dict(to=-1), narg=quantifier) -map yj eval fm.copy(dirarg=dict(down=1), narg=quantifier) -map yk eval fm.copy(dirarg=dict(up=1), narg=quantifier) - -# Searching -map / console search%space -map n search_next -map N search_next forward=False -map ct search_next order=tag -map cs search_next order=size -map ci search_next order=mimetype -map cc search_next order=ctime -map cm search_next order=mtime -map ca search_next order=atime - -# Tabs -map <C-n> tab_new -map <C-w> tab_close -map <TAB> tab_move 1 -map <S-TAB> tab_move -1 -map <A-Right> tab_move 1 -map <A-Left> tab_move -1 -map gt tab_move 1 -map gT tab_move -1 -map gn tab_new -map gc tab_close -map uq tab_restore -map <a-1> tab_open 1 -map <a-2> tab_open 2 -map <a-3> tab_open 3 -map <a-4> tab_open 4 -map <a-5> tab_open 5 -map <a-6> tab_open 6 -map <a-7> tab_open 7 -map <a-8> tab_open 8 -map <a-9> tab_open 9 -map <a-r> tab_shift 1 -map <a-l> tab_shift -1 - -# Sorting -map or set sort_reverse! -map oz set sort=random -map os chain set sort=size; set sort_reverse=False -map ob chain set sort=basename; set sort_reverse=False -map on chain set sort=natural; set sort_reverse=False -map om chain set sort=mtime; set sort_reverse=False -map oc chain set sort=ctime; set sort_reverse=False -map oa chain set sort=atime; set sort_reverse=False -map ot chain set sort=type; set sort_reverse=False -map oe chain set sort=extension; set sort_reverse=False - -map oS chain set sort=size; set sort_reverse=True -map oB chain set sort=basename; set sort_reverse=True -map oN chain set sort=natural; set sort_reverse=True -map oM chain set sort=mtime; set sort_reverse=True -map oC chain set sort=ctime; set sort_reverse=True -map oA chain set sort=atime; set sort_reverse=True -map oT chain set sort=type; set sort_reverse=True -map oE chain set sort=extension; set sort_reverse=True - -map dc get_cumulative_size - -# Settings -map zc set collapse_preview! -map zd set sort_directories_first! -map zh set show_hidden! -map <C-h> set show_hidden! -copymap <C-h> <backspace> -copymap <backspace> <backspace2> -map zI set flushinput! -map zi set preview_images! -map zm set mouse_enabled! -map zp set preview_files! -map zP set preview_directories! -map zs set sort_case_insensitive! -map zu set autoupdate_cumulative_size! -map zv set use_preview_script! -map zf console filter%space -copymap zf zz - -# Filter stack -map .n console filter_stack add name%space -map .m console filter_stack add mime%space -map .d filter_stack add type d -map .f filter_stack add type f -map .l filter_stack add type l -map .| filter_stack add or -map .& filter_stack add and -map .! filter_stack add not -map .r console filter_stack rotate -map .c filter_stack clear -map .* filter_stack decompose -map .p filter_stack pop -map .. filter_stack show - -# Bookmarks -map `<any> enter_bookmark %any -map '<any> enter_bookmark %any -map m<any> set_bookmark %any -map um<any> unset_bookmark %any - -map m<bg> draw_bookmarks -copymap m<bg> um<bg> `<bg> '<bg> - -# Generate all the chmod bindings with some python help: -eval for arg in "rwxXst": cmd("map +u{0} shell -f chmod u+{0} %s".format(arg)) -eval for arg in "rwxXst": cmd("map +g{0} shell -f chmod g+{0} %s".format(arg)) -eval for arg in "rwxXst": cmd("map +o{0} shell -f chmod o+{0} %s".format(arg)) -eval for arg in "rwxXst": cmd("map +a{0} shell -f chmod a+{0} %s".format(arg)) -eval for arg in "rwxXst": cmd("map +{0} shell -f chmod u+{0} %s".format(arg)) - -eval for arg in "rwxXst": cmd("map -u{0} shell -f chmod u-{0} %s".format(arg)) -eval for arg in "rwxXst": cmd("map -g{0} shell -f chmod g-{0} %s".format(arg)) -eval for arg in "rwxXst": cmd("map -o{0} shell -f chmod o-{0} %s".format(arg)) -eval for arg in "rwxXst": cmd("map -a{0} shell -f chmod a-{0} %s".format(arg)) -eval for arg in "rwxXst": cmd("map -{0} shell -f chmod u-{0} %s".format(arg)) - -# =================================================================== -# == Define keys for the console -# =================================================================== -# Note: Unmapped keys are passed directly to the console. - -# Basic -cmap <tab> eval fm.ui.console.tab() -cmap <s-tab> eval fm.ui.console.tab(-1) -cmap <ESC> eval fm.ui.console.close() -cmap <CR> eval fm.ui.console.execute() -cmap <C-l> redraw_window - -copycmap <ESC> <C-c> -copycmap <CR> <C-j> - -# Move around -cmap <up> eval fm.ui.console.history_move(-1) -cmap <down> eval fm.ui.console.history_move(1) -cmap <left> eval fm.ui.console.move(left=1) -cmap <right> eval fm.ui.console.move(right=1) -cmap <home> eval fm.ui.console.move(right=0, absolute=True) -cmap <end> eval fm.ui.console.move(right=-1, absolute=True) -cmap <a-b> eval fm.ui.console.move_word(left=1) -cmap <a-f> eval fm.ui.console.move_word(right=1) - -copycmap <a-b> <a-left> -copycmap <a-f> <a-right> - -# Line Editing -cmap <backspace> eval fm.ui.console.delete(-1) -cmap <delete> eval fm.ui.console.delete(0) -cmap <C-w> eval fm.ui.console.delete_word() -cmap <A-d> eval fm.ui.console.delete_word(backward=False) -cmap <C-k> eval fm.ui.console.delete_rest(1) -cmap <C-u> eval fm.ui.console.delete_rest(-1) -cmap <C-y> eval fm.ui.console.paste() - -# And of course the emacs way -copycmap <ESC> <C-g> -copycmap <up> <C-p> -copycmap <down> <C-n> -copycmap <left> <C-b> -copycmap <right> <C-f> -copycmap <home> <C-a> -copycmap <end> <C-e> -copycmap <delete> <C-d> -copycmap <backspace> <C-h> - -# Note: There are multiple ways to express backspaces. <backspace> (code 263) -# and <backspace2> (code 127). To be sure, use both. -copycmap <backspace> <backspace2> - -# This special expression allows typing in numerals: -cmap <allow_quantifiers> false - -# =================================================================== -# == Pager Keybindings -# =================================================================== - -# Movement -pmap <down> pager_move down=1 -pmap <up> pager_move up=1 -pmap <left> pager_move left=4 -pmap <right> pager_move right=4 -pmap <home> pager_move to=0 -pmap <end> pager_move to=-1 -pmap <pagedown> pager_move down=1.0 pages=True -pmap <pageup> pager_move up=1.0 pages=True -pmap <C-d> pager_move down=0.5 pages=True -pmap <C-u> pager_move up=0.5 pages=True - -copypmap <UP> k <C-p> -copypmap <DOWN> j <C-n> <CR> -copypmap <LEFT> h -copypmap <RIGHT> l -copypmap <HOME> g -copypmap <END> G -copypmap <C-d> d -copypmap <C-u> u -copypmap <PAGEDOWN> n f <C-F> <Space> -copypmap <PAGEUP> p b <C-B> - -# Basic -pmap <C-l> redraw_window -pmap <ESC> pager_close -copypmap <ESC> q Q i <F3> -pmap E edit_file - -# =================================================================== -# == Taskview Keybindings -# =================================================================== - -# Movement -tmap <up> taskview_move up=1 -tmap <down> taskview_move down=1 -tmap <home> taskview_move to=0 -tmap <end> taskview_move to=-1 -tmap <pagedown> taskview_move down=1.0 pages=True -tmap <pageup> taskview_move up=1.0 pages=True -tmap <C-d> taskview_move down=0.5 pages=True -tmap <C-u> taskview_move up=0.5 pages=True - -copytmap <UP> k <C-p> -copytmap <DOWN> j <C-n> <CR> -copytmap <HOME> g -copytmap <END> G -copytmap <C-u> u -copytmap <PAGEDOWN> n f <C-F> <Space> -copytmap <PAGEUP> p b <C-B> - -# Changing priority and deleting tasks -tmap J eval -q fm.ui.taskview.task_move(-1) -tmap K eval -q fm.ui.taskview.task_move(0) -# tmap dd eval -q fm.ui.taskview.task_remove() -tmap <pagedown> eval -q fm.ui.taskview.task_move(-1) -tmap <pageup> eval -q fm.ui.taskview.task_move(0) -tmap <delete> eval -q fm.ui.taskview.task_remove() - -# Basic -tmap <C-l> redraw_window -tmap <ESC> taskview_close -copytmap <ESC> q Q w <C-c> - -######### cjennings - -## image transformation -map r9 shell convert %s -rotate 90 %s - - -## navigation and file management -map gslr cd /media/remote0/3 -map mslr shell mv /media/remote0/3 -map cslr shell cp /media/remote0/3 - -### MAIN DIRECTORIES -map mdx shell mv %s ~/documents -map cdx shell cp %s ~/documents -map gdx cd ~/documents - -map gdr cd ~/documents/reference -map mdr shell mv %s ~/documents/reference -map cdr shell cp %s ~/documents/reference - -map mdl shell mv %d ~/downloads -map cdl shell cp %d ~/downloads -map gdl cd ~/downloads - -map mpx shell mv %s ~/pictures -map cpx shell cp %s ~/pictures -map gpx cd ~/pictures - -map gps cd ~/pictures/screenshots -map mps shell mv %s ~/pictures/screenshots -map cps shell cp %s ~/pictures/screenshots - -map gpw cd ~/pictures/wallpaper -map mpw shell mv %s ~/pictures/wallpaper -map cpw shell cp %s ~/pictures/wallpaper - -map gmx cd ~/music - -map gvx cd ~/videos -map cvx shell cp %s ~/videos -map mvx shell mv %s ~/videos - -## CONFIG -map grc cd ~/.config/ranger -map crc shell cp %s ~/.config/ranger -map mrc shell mv %s ~/.config/ranger - -### OPEN WITH -# open with alternate video player -map owm shell mpv %s - -# open with gimp -map owg shell gimp %s - -# open with zathura -map owz shell zathura %s - -### MISC ACTIONS -# set background wallpaper using feh -map bg shell feh --bg-fill %s && notify-send "ranger" "wallpaper updated" - -# delete -map dx shell rm -f %s - -# remap cut/uncut -map xx cut -map ux uncut diff --git a/dotfiles/common/.config/ranger/rifle.conf b/dotfiles/common/.config/ranger/rifle.conf deleted file mode 100644 index 8cc0ce7..0000000 --- a/dotfiles/common/.config/ranger/rifle.conf +++ /dev/null @@ -1,257 +0,0 @@ -# vim: ft=cfg -# -# This is the configuration file of "rifle", ranger's file executor/opener. -# Each line consists of conditions and a command. For each line the conditions -# are checked and if they are met, the respective command is run. -# -# Syntax: -# <condition1> , <condition2> , ... = command -# -# The command can contain these environment variables: -# $1-$9 | The n-th selected file -# $@ | All selected files -# -# If you use the special command "ask", rifle will ask you what program to run. -# -# Prefixing a condition with "!" will negate its result. -# These conditions are currently supported: -# match <regexp> | The regexp matches $1 -# ext <regexp> | The regexp matches the extension of $1 -# mime <regexp> | The regexp matches the mime type of $1 -# name <regexp> | The regexp matches the basename of $1 -# path <regexp> | The regexp matches the absolute path of $1 -# has <program> | The program is installed (i.e. located in $PATH) -# env <variable> | The environment variable "variable" is non-empty -# file | $1 is a file -# directory | $1 is a directory -# number <n> | change the number of this command to n -# terminal | stdin, stderr and stdout are connected to a terminal -# X | $DISPLAY is not empty (i.e. Xorg runs) -# -# There are also pseudo-conditions which have a "side effect": -# flag <flags> | Change how the program is run. See below. -# label <label> | Assign a label or name to the command so it can -# | be started with :open_with <label> in ranger -# | or `rifle -p <label>` in the standalone executable. -# else | Always true. -# -# Flags are single characters which slightly transform the command: -# f | Fork the program, make it run in the background. -# | New command = setsid $command >& /dev/null & -# r | Execute the command with root permissions -# | New command = sudo $command -# t | Run the program in a new terminal. If $TERMCMD is not defined, -# | rifle will attempt to extract it from $TERM. -# | New command = $TERMCMD -e $command -# Note: The "New command" serves only as an illustration, the exact -# implementation may differ. -# Note: When using rifle in ranger, there is an additional flag "c" for -# only running the current file even if you have marked multiple files. - -#------------------------------------------- -# Websites -#------------------------------------------- -# Rarely installed browsers get higher priority; It is assumed that if you -# install a rare browser, you probably use it. Firefox/konqueror/w3m on the -# other hand are often only installed as fallback browsers. -ext x?html?, has surf, X, flag f = surf -- file://"$1" -ext x?html?, has vimprobable, X, flag f = vimprobable -- "$@" -ext x?html?, has vimprobable2, X, flag f = vimprobable2 -- "$@" -ext x?html?, has qutebrowser, X, flag f = qutebrowser -- "$@" -ext x?html?, has dwb, X, flag f = dwb -- "$@" -ext x?html?, has jumanji, X, flag f = jumanji -- "$@" -ext x?html?, has luakit, X, flag f = luakit -- "$@" -ext x?html?, has uzbl, X, flag f = uzbl -- "$@" -ext x?html?, has uzbl-tabbed, X, flag f = uzbl-tabbed -- "$@" -ext x?html?, has uzbl-browser, X, flag f = uzbl-browser -- "$@" -ext x?html?, has uzbl-core, X, flag f = uzbl-core -- "$@" -ext x?html?, has midori, X, flag f = midori -- "$@" -ext x?html?, has chromium-browser, X, flag f = chromium-browser -- "$@" -ext x?html?, has chromium, X, flag f = chromium -- "$@" -ext x?html?, has google-chrome, X, flag f = google-chrome -- "$@" -ext x?html?, has opera, X, flag f = opera -- "$@" -ext x?html?, has firefox, X, flag f = firefox -- "$@" -ext x?html?, has seamonkey, X, flag f = seamonkey -- "$@" -ext x?html?, has iceweasel, X, flag f = iceweasel -- "$@" -ext x?html?, has epiphany, X, flag f = epiphany -- "$@" -ext x?html?, has konqueror, X, flag f = konqueror -- "$@" -ext x?html?, has elinks, terminal = elinks "$@" -ext x?html?, has links2, terminal = links2 "$@" -ext x?html?, has links, terminal = links "$@" -ext x?html?, has lynx, terminal = lynx -- "$@" -ext x?html?, has w3m, terminal = w3m "$@" - -#------------------------------------------- -# Misc -#------------------------------------------- -# Define the "editor" for text files as first action -mime ^text, label editor = ${VISUAL:-$EDITOR} -- "$@" -mime ^text, label pager = "$PAGER" -- "$@" -!mime ^text, label editor, ext xml|json|csv|tex|py|pl|rb|js|sh|php = ${VISUAL:-$EDITOR} -- "$@" -!mime ^text, label pager, ext xml|json|csv|tex|py|pl|rb|js|sh|php = "$PAGER" -- "$@" - -ext 1 = man "$1" -ext s[wmf]c, has zsnes, X = zsnes "$1" -ext s[wmf]c, has snes9x-gtk,X = snes9x-gtk "$1" -ext nes, has fceux, X = fceux "$1" -ext exe = wine "$1" -name ^[mM]akefile$ = make - -#-------------------------------------------- -# Code -#------------------------------------------- -ext py = python3 -- "$1" -ext pl = perl -- "$1" -ext rb = ruby -- "$1" -ext js = node -- "$1" -ext sh = sh -- "$1" -ext php = php -- "$1" - -#-------------------------------------------- -# Audio without X -#------------------------------------------- -mime ^audio|ogg$, terminal, has mpv = mpv -- "$@" -mime ^audio|ogg$, terminal, has mplayer2 = mplayer2 -- "$@" -mime ^audio|ogg$, terminal, has mplayer = mplayer -- "$@" -ext midi?, terminal, has wildmidi = wildmidi -- "$@" - -#-------------------------------------------- -# Video/Audio with a GUI -#------------------------------------------- -mime ^video, has mpv, X, flag f = mpv -- "$@" -mime ^video, has mpv, X, flag f = mpv --fs -- "$@" -mime ^video|audio, has vlc, X, flag f = vlc -- "$@" -mime ^video|audio, has gmplayer, X, flag f = gmplayer -- "$@" -mime ^video|audio, has smplayer, X, flag f = smplayer "$@" -mime ^video, has mplayer2, X, flag f = mplayer2 -- "$@" -mime ^video, has mplayer2, X, flag f = mplayer2 -fs -- "$@" -mime ^video, has mplayer, X, flag f = mplayer -- "$@" -mime ^video, has mplayer, X, flag f = mplayer -fs -- "$@" -mime ^video|audio, has totem, X, flag f = totem -- "$@" -mime ^video|audio, has totem, X, flag f = totem --fullscreen -- "$@" - -#-------------------------------------------- -# Video without X: -#------------------------------------------- -mime ^video, terminal, !X, has mpv = mpv -- "$@" -mime ^video, terminal, !X, has mplayer2 = mplayer2 -- "$@" -mime ^video, terminal, !X, has mplayer = mplayer -- "$@" - -#------------------------------------------- -# Documents -#------------------------------------------- -ext pdf, has llpp, X, flag f = llpp "$@" -ext pdf, has zathura, X, flag f = zathura -- "$@" -ext pdf, has mupdf, X, flag f = mupdf "$@" -ext pdf, has mupdf-x11,X, flag f = mupdf-x11 "$@" -ext pdf, has apvlv, X, flag f = apvlv -- "$@" -ext pdf, has xpdf, X, flag f = xpdf -- "$@" -ext pdf, has evince, X, flag f = evince -- "$@" -ext pdf, has atril, X, flag f = atril -- "$@" -ext pdf, has okular, X, flag f = okular -- "$@" -ext pdf, has epdfview, X, flag f = epdfview -- "$@" -ext pdf, has qpdfview, X, flag f = qpdfview "$@" -ext pdf, has open, X, flag f = open "$@" - -ext docx?, has catdoc, terminal = catdoc -- "$@" | "$PAGER" - -ext sxc|xlsx?|xlt|xlw|gnm|gnumeric, has gnumeric, X, flag f = gnumeric -- "$@" -ext sxc|xlsx?|xlt|xlw|gnm|gnumeric, has kspread, X, flag f = kspread -- "$@" -ext pptx?|od[dfgpst]|docx?|sxc|xlsx?|xlt|xlw|gnm|gnumeric, has libreoffice, X, flag f = libreoffice "$@" -ext pptx?|od[dfgpst]|docx?|sxc|xlsx?|xlt|xlw|gnm|gnumeric, has soffice, X, flag f = soffice "$@" -ext pptx?|od[dfgpst]|docx?|sxc|xlsx?|xlt|xlw|gnm|gnumeric, has ooffice, X, flag f = ooffice "$@" - -ext djvu, has zathura,X, flag f = zathura -- "$@" -ext djvu, has evince, X, flag f = evince -- "$@" -ext djvu, has atril, X, flag f = atril -- "$@" -ext djvu, has djview, X, flag f = djview -- "$@" - -ext epub, has zathura, X, flag f = zathura -- "$@" -ext mobi, has ebook-viewer, X, flag f = ebook-viewer -- "$@" - -#------------------------------------------- -# Image Viewing: -#------------------------------------------- -mime ^image/svg, has inkscape, X, flag f = inkscape -- "$@" -mime ^image/svg, has display, X, flag f = display -- "$@" - -mime ^image, has pqiv, X, flag f = pqiv -- "$@" -mime ^image, has nsxiv, X, flag f = nsxiv -- "$@" -mime ^image, has sxiv, X, flag f = sxiv -- "$@" -mime ^image, has feh, X, flag f = feh -- "$@" -mime ^image, has mirage, X, flag f = mirage -- "$@" -mime ^image, has ristretto, X, flag f = ristretto "$@" -mime ^image, has eog, X, flag f = eog -- "$@" -mime ^image, has eom, X, flag f = eom -- "$@" -mime ^image, has nomacs, X, flag f = nomacs -- "$@" -mime ^image, has geeqie, X, flag f = geeqie -- "$@" -mime ^image, has gwenview, X, flag f = gwenview -- "$@" -mime ^image, has gimp, X, flag f = gimp -- "$@" -ext xcf, X, flag f = gimp -- "$@" - -#------------------------------------------- -# Archives -#------------------------------------------- - -# avoid password prompt by providing empty password -ext 7z, has 7z = 7z -p l "$@" | "$PAGER" -# This requires atool -ext ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz, has atool = atool --list --each -- "$@" | "$PAGER" -ext iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has atool = atool --list --each -- "$@" | "$PAGER" -ext 7z|ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz, has atool = atool --extract --each -- "$@" -ext iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has atool = atool --extract --each -- "$@" - -# Listing and extracting archives without atool: -ext tar|gz|bz2|xz, has tar = tar vvtf "$1" | "$PAGER" -ext tar|gz|bz2|xz, has tar = for file in "$@"; do tar vvxf "$file"; done -ext bz2, has bzip2 = for file in "$@"; do bzip2 -dk "$file"; done -ext zip, has unzip = unzip -l "$1" | "$PAGER" -ext zip, has unzip = for file in "$@"; do unzip -d "${file%.*}" "$file"; done -ext ace, has unace = unace l "$1" | "$PAGER" -ext ace, has unace = for file in "$@"; do unace e "$file"; done -ext rar, has unrar = unrar l "$1" | "$PAGER" -ext rar, has unrar = for file in "$@"; do unrar x "$file"; done - -#------------------------------------------- -# Flag t fallback terminals -#------------------------------------------- -# Rarely installed terminal emulators get higher priority; It is assumed that -# if you install a rare terminal emulator, you probably use it. -# gnome-terminal/konsole/xterm on the other hand are often installed as part of -# a desktop environment or as fallback terminal emulators. -mime ^ranger/x-terminal-emulator, has terminator = terminator -x "$@" -mime ^ranger/x-terminal-emulator, has st = st -e "$@" -mime ^ranger/x-terminal-emulator, has terminology = terminology -e "$@" -mime ^ranger/x-terminal-emulator, has kitty = kitty -- "$@" -mime ^ranger/x-terminal-emulator, has alacritty = alacritty -e "$@" -mime ^ranger/x-terminal-emulator, has sakura = sakura -e "$@" -mime ^ranger/x-terminal-emulator, has lilyterm = lilyterm -e "$@" -#mime ^ranger/x-terminal-emulator, has cool-retro-term = cool-retro-term -e "$@" -mime ^ranger/x-terminal-emulator, has termite = termite -x '"$@"' -#mime ^ranger/x-terminal-emulator, has yakuake = yakuake -e "$@" -mime ^ranger/x-terminal-emulator, has guake = guake -ne "$@" -mime ^ranger/x-terminal-emulator, has tilda = tilda -c "$@" -mime ^ranger/x-terminal-emulator, has urxvt = urxvt -e "$@" -mime ^ranger/x-terminal-emulator, has pantheon-terminal = pantheon-terminal -e "$@" -mime ^ranger/x-terminal-emulator, has lxterminal = lxterminal -e "$@" -mime ^ranger/x-terminal-emulator, has mate-terminal = mate-terminal -x "$@" -mime ^ranger/x-terminal-emulator, has xfce4-terminal = xfce4-terminal -x "$@" -mime ^ranger/x-terminal-emulator, has konsole = konsole -e "$@" -mime ^ranger/x-terminal-emulator, has gnome-terminal = gnome-terminal -- "$@" -mime ^ranger/x-terminal-emulator, has xterm = xterm -e "$@" - -#------------------------------------------- -# Misc -#------------------------------------------- -label wallpaper, number 11, mime ^image, has feh, X = feh --bg-scale "$1" -label wallpaper, number 12, mime ^image, has feh, X = feh --bg-tile "$1" -label wallpaper, number 13, mime ^image, has feh, X = feh --bg-center "$1" -label wallpaper, number 14, mime ^image, has feh, X = feh --bg-fill "$1" - -# Define the editor for non-text files + pager as last action - !mime ^text, !ext xml|json|csv|tex|py|pl|rb|js|sh|php = ask -label editor, !mime ^text, !ext xml|json|csv|tex|py|pl|rb|js|sh|php = ${VISUAL:-$EDITOR} -- "$@" -label pager, !mime ^text, !ext xml|json|csv|tex|py|pl|rb|js|sh|php = "$PAGER" -- "$@" - -# The very last action, so that it's never triggered accidentally, is to execute a program: -mime application/x-executable = "$1" diff --git a/dotfiles/common/.config/ranger/scope.sh b/dotfiles/common/.config/ranger/scope.sh deleted file mode 100755 index 13a25b4..0000000 --- a/dotfiles/common/.config/ranger/scope.sh +++ /dev/null @@ -1,216 +0,0 @@ -#!/usr/bin/env bash - -set -o noclobber -o noglob -o nounset -o pipefail -IFS=$'\n' - -# If the option `use_preview_script` is set to `true`, -# then this script will be called and its output will be displayed in ranger. -# ANSI color codes are supported. -# STDIN is disabled, so interactive scripts won't work properly - -# This script is considered a configuration file and must be updated manually. -# It will be left untouched if you upgrade ranger. - -# Meanings of exit codes: -# code | meaning | action of ranger -# -----+------------+------------------------------------------- -# 0 | success | Display stdout as preview -# 1 | no preview | Display no preview at all -# 2 | plain text | Display the plain content of the file -# 3 | fix width | Don't reload when width changes -# 4 | fix height | Don't reload when height changes -# 5 | fix both | Don't ever reload -# 6 | image | Display the image `$IMAGE_CACHE_PATH` points to as an image preview -# 7 | image | Display the file directly as an image - -# Script arguments -FILE_PATH="${1}" # Full path of the highlighted file -PV_WIDTH="${2}" # Width of the preview pane (number of fitting characters) -PV_HEIGHT="${3}" # Height of the preview pane (number of fitting characters) -IMAGE_CACHE_PATH="${4}" # Full path that should be used to cache image preview -PV_IMAGE_ENABLED="${5}" # 'True' if image previews are enabled, 'False' otherwise. - -FILE_EXTENSION="${FILE_PATH##*.}" -FILE_EXTENSION_LOWER=$(echo ${FILE_EXTENSION} | tr '[:upper:]' '[:lower:]') - -# Settings -HIGHLIGHT_SIZE_MAX=262143 # 256KiB -HIGHLIGHT_TABWIDTH=8 -HIGHLIGHT_STYLE='pablo' -PYGMENTIZE_STYLE='autumn' - - -handle_extension() { - case "${FILE_EXTENSION_LOWER}" in - # Archive - a|ace|alz|arc|arj|bz|bz2|cab|cpio|deb|gz|jar|lha|lz|lzh|lzma|lzo|\ - rpm|rz|t7z|tar|tbz|tbz2|tgz|tlz|txz|tZ|tzo|war|xpi|xz|Z|zip) - atool --list -- "${FILE_PATH}" && exit 5 - bsdtar --list --file "${FILE_PATH}" && exit 5 - exit 1;; - rar) - # Avoid password prompt by providing empty password - unrar lt -p- -- "${FILE_PATH}" && exit 5 - exit 1;; - 7z) - # Avoid password prompt by providing empty password - 7z l -p -- "${FILE_PATH}" && exit 5 - exit 1;; - - # PDF - pdf) - # Preview as text conversion - pdftotext -l 10 -nopgbrk -q -- "${FILE_PATH}" - | fmt -w ${PV_WIDTH} && exit 5 - mutool draw -F txt -i -- "${FILE_PATH}" 1-10 | fmt -w ${PV_WIDTH} && exit 5 - exiftool "${FILE_PATH}" && exit 5 - exit 1;; - - # BitTorrent - torrent) - transmission-show -- "${FILE_PATH}" && exit 5 - exit 1;; - - # OpenDocument - odt|ods|odp|sxw) - # Preview as text conversion - odt2txt "${FILE_PATH}" && exit 5 - exit 1;; - - # HTML - htm|html|xhtml) - # Preview as text conversion - w3m -dump "${FILE_PATH}" && exit 5 - lynx -dump -- "${FILE_PATH}" && exit 5 - elinks -dump "${FILE_PATH}" && exit 5 - ;; # Continue with next handler on failure - esac -} - -handle_image() { - local mimetype="${1}" - case "${mimetype}" in - # SVG - # image/svg+xml) - # convert "${FILE_PATH}" "${IMAGE_CACHE_PATH}" && exit 6 - # exit 1;; - - # Image - image/*) - local orientation - orientation="$( identify -format '%[EXIF:Orientation]\n' -- "${FILE_PATH}" )" - # If orientation data is present and the image actually - # needs rotating ("1" means no rotation)... - if [[ -n "$orientation" && "$orientation" != 1 ]]; then - # ...auto-rotate the image according to the EXIF data. - convert -- "${FILE_PATH}" -auto-orient "${IMAGE_CACHE_PATH}" && exit 6 - fi - - # `w3mimgdisplay` will be called for all images (unless overriden as above), - # but might fail for unsupported types. - exit 7;; - - # Video - # video/*) - # # Thumbnail - # ffmpegthumbnailer -i "${FILE_PATH}" -o "${IMAGE_CACHE_PATH}" -s 0 && exit 6 - # exit 1;; - # PDF - # application/pdf) - # pdftoppm -f 1 -l 1 \ - # -scale-to-x 1920 \ - # -scale-to-y -1 \ - # -singlefile \ - # -jpeg -tiffcompression jpeg \ - # -- "${FILE_PATH}" "${IMAGE_CACHE_PATH%.*}" \ - # && exit 6 || exit 1;; - - # Preview archives using the first image inside. - # (Very useful for comic book collections for example.) - # application/zip|application/x-rar|application/x-7z-compressed|\ - # application/x-xz|application/x-bzip2|application/x-gzip|application/x-tar) - # local fn=""; local fe="" - # local zip=""; local rar=""; local tar=""; local bsd="" - # case "${mimetype}" in - # application/zip) zip=1 ;; - # application/x-rar) rar=1 ;; - # application/x-7z-compressed) ;; - # *) tar=1 ;; - # esac - # { [ "$tar" ] && fn=$(tar --list --file "${FILE_PATH}"); } || \ - # { fn=$(bsdtar --list --file "${FILE_PATH}") && bsd=1 && tar=""; } || \ - # { [ "$rar" ] && fn=$(unrar lb -p- -- "${FILE_PATH}"); } || \ - # { [ "$zip" ] && fn=$(zipinfo -1 -- "${FILE_PATH}"); } || return - # - # fn=$(echo "$fn" | python -c "import sys; import mimetypes as m; \ - # [ print(l, end='') for l in sys.stdin if \ - # (m.guess_type(l[:-1])[0] or '').startswith('image/') ]" |\ - # sort -V | head -n 1) - # [ "$fn" = "" ] && return - # [ "$bsd" ] && fn=$(printf '%b' "$fn") - # - # [ "$tar" ] && tar --extract --to-stdout \ - # --file "${FILE_PATH}" -- "$fn" > "${IMAGE_CACHE_PATH}" && exit 6 - # fe=$(echo -n "$fn" | sed 's/[][*?\]/\\\0/g') - # [ "$bsd" ] && bsdtar --extract --to-stdout \ - # --file "${FILE_PATH}" -- "$fe" > "${IMAGE_CACHE_PATH}" && exit 6 - # [ "$bsd" ] || [ "$tar" ] && rm -- "${IMAGE_CACHE_PATH}" - # [ "$rar" ] && unrar p -p- -inul -- "${FILE_PATH}" "$fn" > \ - # "${IMAGE_CACHE_PATH}" && exit 6 - # [ "$zip" ] && unzip -pP "" -- "${FILE_PATH}" "$fe" > \ - # "${IMAGE_CACHE_PATH}" && exit 6 - # [ "$rar" ] || [ "$zip" ] && rm -- "${IMAGE_CACHE_PATH}" - # ;; - esac -} - -handle_mime() { - local mimetype="${1}" - case "${mimetype}" in - # Text - text/* | */xml) - # Syntax highlight - if [[ "$( stat --printf='%s' -- "${FILE_PATH}" )" -gt "${HIGHLIGHT_SIZE_MAX}" ]]; then - exit 2 - fi - if [[ "$( tput colors )" -ge 256 ]]; then - local pygmentize_format='terminal256' - local highlight_format='xterm256' - else - local pygmentize_format='terminal' - local highlight_format='ansi' - fi - highlight --replace-tabs="${HIGHLIGHT_TABWIDTH}" --out-format="${highlight_format}" \ - --style="${HIGHLIGHT_STYLE}" --force -- "${FILE_PATH}" && exit 5 - # pygmentize -f "${pygmentize_format}" -O "style=${PYGMENTIZE_STYLE}" -- "${FILE_PATH}" && exit 5 - exit 2;; - - # Image - image/*) - # Preview as text conversion - # img2txt --gamma=0.6 --width="${PV_WIDTH}" -- "${FILE_PATH}" && exit 4 - exiftool "${FILE_PATH}" && exit 5 - exit 1;; - - # Video and audio - video/* | audio/*) - mediainfo "${FILE_PATH}" && exit 5 - exiftool "${FILE_PATH}" && exit 5 - exit 1;; - esac -} - -handle_fallback() { - echo '----- File Type Classification -----' && file --dereference --brief -- "${FILE_PATH}" && exit 5 - exit 1 -} - - -MIMETYPE="$( file --dereference --brief --mime-type -- "${FILE_PATH}" )" -if [[ "${PV_IMAGE_ENABLED}" == 'True' ]]; then - handle_image "${MIMETYPE}" -fi -handle_extension -handle_mime "${MIMETYPE}" -handle_fallback - -exit 1 diff --git a/dotfiles/common/.config/rofi/config.rasi b/dotfiles/common/.config/rofi/config.rasi deleted file mode 100644 index 3f50cc6..0000000 --- a/dotfiles/common/.config/rofi/config.rasi +++ /dev/null @@ -1,6 +0,0 @@ -configuration { - show-icons: true; - dpi: 1; -} - -@theme "~/.config/rofi/themes/rounded-gray-dark.rasi" diff --git a/dotfiles/common/.config/rofi/themes/rounded-gray-dark.rasi b/dotfiles/common/.config/rofi/themes/rounded-gray-dark.rasi deleted file mode 100644 index de5bbc3..0000000 --- a/dotfiles/common/.config/rofi/themes/rounded-gray-dark.rasi +++ /dev/null @@ -1,106 +0,0 @@ -/* ROFI THEME - Matched to dunst notifications */ -/* Based on: Newman Sanchez (https://github.com/newmanls) */ - -* { - bg0: #383c4af0; - bg1: #2d303c; - bg2: #4a4e5c80; - bg3: #2d303c; - fg0: #cdd1dc; - fg1: #ffffff; - fg2: #969696; - fg3: #4a4e5c; - - font: "FiraCode Nerd Font Mono 10"; - - background-color: transparent; - text-color: @fg0; - - margin: 0px; - padding: 0px; - spacing: 0px; -} - -window { - location: north; - y-offset: calc(50% - 176px); - width: 480; - border-radius: 10px; - border: 1px; - border-color: @bg1; - - background-color: @bg0; -} - -mainbox { - padding: 12px; -} - -inputbar { - background-color: @bg1; - border-color: @bg3; - - border: 1px; - border-radius: 6px; - - padding: 12px 20px; - spacing: 8px; - children: [ prompt, entry ]; -} - -prompt { - text-color: @fg2; -} - -entry { - placeholder: "Search"; - placeholder-color: @fg3; -} - -message { - margin: 12px 0 0; - border-radius: 6px; - border-color: @bg2; - background-color: @bg2; -} - -textbox { - padding: 8px 24px; -} - -listview { - background-color: transparent; - - margin: 12px 0 0; - lines: 8; - columns: 1; - - fixed-height: false; -} - -element { - padding: 8px 16px; - spacing: 8px; - border-radius: 6px; -} - -element normal active { - text-color: @bg3; -} - -element alternate active { - text-color: @bg3; -} - -element selected normal, element selected active { - background-color: @bg3; -} - -element-icon { - size: 1em; - vertical-align: 0.5; -} - -element-text { - text-color: inherit; -} diff --git a/dotfiles/common/.config/sxhkd/sxhkdrc b/dotfiles/common/.config/sxhkd/sxhkdrc deleted file mode 100644 index 16b90c7..0000000 --- a/dotfiles/common/.config/sxhkd/sxhkdrc +++ /dev/null @@ -1,106 +0,0 @@ -# SXHDRC -# This is the sxhkdrc for the Simple X Hotkey Daemon. -# https://github.com/baskerville/sxhkd - - -super + space - rofi -show drun -super + shift + s - rofi -show ssh - - -# -# Browsers -# - -super + w - $BROWSER -super + shift + w - $ALTBROWSER -ctrl + alt + w - tor-browser - -# -# Applications -# - -# Emacs -super + e - emacsclient -c -a "" || emacs - -# File Manager -control + alt + f - nautilus - -# Virtualbox -super + v - virtualbox - - -# Library -super + shift + l - calibre - -# Radio -super + shift + r - shortwave - -# -# Music -# -# super + slash -# st -e ncmpcpp -# ctrl + alt + slash -# easytag ~/music - - -# -# Accessories -# - - -# Colorpicker -super + c - xcolor | xsel -b - -# Screenshots -super + s - file="$(date +%Y.%m.%d-%M%S).png" && maim ~/pictures/screenshots/$file && \ - notify-send "screenshot" "Image saved to ~/Pictures/screenshots/$file" - -# -# System -# - -# Audio Keys -XF86AudioMute - amixer -q -D pulse sset Master toggle && -XF86Audio{Raise,Lower}Volume - pactl -- set-sink-volume 0 {+,-}10% && - -# Brightness Keys -XF86MonBrightnessUp - brightnessctl s +10% && notify-send "Brightness" "Brightness increased" -XF86MonBrightnessDown - brightnessctl s 10%- && notify-send "Brightness" "Brightness decreased" - -# Microphone Toggle -control + alt + space - amixer set Capture toggle && amixer get Capture | grep '\[off\]' && notify-send "Microphone" "Microphone muted" || notify-send "Microphone" "Microphone on" - -# Bluetooth -super + shift + b - blueman-manager - -# Lock Screen -super + Escape - slock & - -# -# Keybinding -# - -super + k - $EDITOR ~/.config/sxhkd/sxhkdrc && notify-send "Keyboard Shortcuts" "Editing shortcuts" -super + shift + k - sudo killall sxhkd && sxhkd && notify-send "Keyboard Shortcuts" "Keyboard shortcuts reloaded" diff --git a/dotfiles/common/.config/systemd/user/emacs.service b/dotfiles/common/.config/systemd/user/emacs.service deleted file mode 100644 index 0e3a102..0000000 --- a/dotfiles/common/.config/systemd/user/emacs.service +++ /dev/null @@ -1,19 +0,0 @@ -[Unit] -Description=Emacs text editor -Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/ - -[Service] -Type=notify -ExecStart=/home/cjennings/.local/bin/emacs --fg-daemon - -# Emacs will exit with status 15 after having received SIGTERM, which -# is the default "KillSignal" value systemd uses to stop services. -SuccessExitStatus=15 - -# The location of the SSH auth socket varies by distribution, and some -# set it from PAM, so don't override by default. -# Environment=SSH_AUTH_SOCK=%t/keyring/ssh -Restart=on-failure - -[Install] -WantedBy=default.target diff --git a/dotfiles/common/.config/systemd/user/geoclue-agent.service b/dotfiles/common/.config/systemd/user/geoclue-agent.service deleted file mode 100644 index 9f2adc0..0000000 --- a/dotfiles/common/.config/systemd/user/geoclue-agent.service +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=redshift needs to get a (geo)clue - -[Service] -ExecStart=/usr/lib/geoclue-2.0/demos/agent - -[Install] -WantedBy=default.target diff --git a/dotfiles/common/.config/tickrs/config.yml b/dotfiles/common/.config/tickrs/config.yml deleted file mode 100644 index a9c07c3..0000000 --- a/dotfiles/common/.config/tickrs/config.yml +++ /dev/null @@ -1,95 +0,0 @@ ---- -# List of ticker symbols to start app with -#symbols: -# - SPY -# - AMD - -# Chart type to start app with -# Default is line -# Possible values: line, candle, kagi -#chart_type: candle - -# Use specified time frame when starting program and when new stocks are added -# Default is 1D -# Possible values: 1D, 1W, 1M, 3M, 6M, 1Y, 5Y -#time_frame: 1D - -# Interval to update data from API (seconds) -# Default is 1 -#update_interval: 1 - -# Enable pre / post market hours for graphs -#enable_pre_post: true - -# Hide help icon in top right -#hide_help: true - -# Hide previous close line on 1D chart -#hide_prev_close: true - -# Hide toggle block -#hide_toggle: true - -# Show volumes graph -#show_volumes: true - -# Show x-axis labels -#show_x_labels: true - -# Start in summary mode -#summary: true - -# Truncate pre market graphing to only 30 minutes prior to markets opening -#trunc_pre: true - -# Ticker options for Kagi charts -# -# A map of each ticker with reversal and/or price fields (both optional). If no -# entry is defined for a symbol, a default of 'close' price and 1% for 1D and 4% -# for non-1D timeframes is used. This can be updated in the GUI by pressing 'e' -# -# reversal can be supplied as a single value, or a map on time frame to give each -# time frame a different reversal amount -# -# reversal.type can be 'amount' or 'pct' -# -# price can be 'close' or 'high_low' -# -#kagi_options: -# SPY: -# reversal: -# type: amount -# value: 5.00 -# price: close -# AMD: -# price: high_low -# TSLA: -# reversal: -# type: pct -# value: 0.08 -# NVDA: -# reversal: -# 1D: -# type: pct -# value: 0.02 -# 5Y: -# type: pct -# value: 0.10 - -# Apply a custom theme -# -# All colors are optional. If commented out / omitted, the color will get sourced -# from your terminal color scheme -#theme: -# background: '#403E41' -# gray: '#727072' -# profit: '#ADD977' -# loss: '#FA648A' -# text_normal: '#FCFCFA' -# text_primary: '#FFDA65' -# text_secondary: '#79DBEA' -# border_primary: '#FC9766' -# border_secondary: '#FCFCFA' -# border_axis: '#FC9766' -# highlight_focused: '#FC9766' -# highlight_unfocused: '#727072' diff --git a/dotfiles/common/.config/topgrade.toml b/dotfiles/common/.config/topgrade.toml deleted file mode 100644 index f2dece4..0000000 --- a/dotfiles/common/.config/topgrade.toml +++ /dev/null @@ -1,246 +0,0 @@ -# Include any additional configuration file(s) -# [include] sections are processed in the order you write them -# Files in $CONFIG_DIR/topgrade.d/ are automatically included before this file -[include] -# paths = ["/etc/topgrade.toml"] - - -[misc] -# Run `sudo -v` to cache credentials at the start of the run -# This avoids a blocking password prompt in the middle of an unattended run -# (default: false) -pre_sudo = true - -# Sudo command to be used -sudo_command = "sudo" - -# Disable specific steps - same options as the command line flag -# disable = ["system", "emacs"] -disable = ["emacs", "poetry", "gnome_shell_extensions", "lensfun"] - -# Ignore failures for these steps -# ignore_failures = ["powershell"] - -# List of remote machines with Topgrade installed on them -# remote_topgrades = ["toothless", "pi", "parnas"] - -# Path to Topgrade executable on remote machines -# remote_topgrade_path = ".cargo/bin/topgrade" - -# Arguments to pass to SSH when upgrading remote systems -# ssh_arguments = "-o ConnectTimeout=2" - -# Arguments to pass tmux when pulling Repositories -# tmux_arguments = "-S /var/tmux.sock" - -# Do not set the terminal title (default: true) -# set_title = true - -# Display the time in step titles (default: true) -# display_time = true - -# Don't ask for confirmations (no default value) -assume_yes = true - -# Do not ask to retry failed steps (default: false) -no_retry = true - -# Run inside tmux (default: false) -# run_in_tmux = true - -# Cleanup temporary or old files (default: false) -cleanup = true - -# Send a notification for every step (default: false) -# notify_each_step = false - -# Skip sending a notification at the end of a run (default: false) -# skip_notify = true - -# The Bash-it branch to update (default: "stable") -# bashit_branch = "stable" - -# Run specific steps - same options as the command line flag -# only = ["system", "emacs"] - -# Whether to self update -# -# this will be ignored if the binary is built without self update support -# -# available also via setting the environment variable TOPGRADE_NO_SELF_UPGRADE) -# no_self_update = true - -# Extra tracing filter directives -# These are prepended to the `--log-filter` argument -# See: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives -# log_filters = ["topgrade::command=debug", "warn"] - - -# Commands to run before anything -[pre_commands] -# "Emacs Snapshot" = "rm -rf ~/.emacs.d/elpa.bak && cp -rl ~/.emacs.d/elpa ~/.emacs.d/elpa.bak" - - -# Commands to run after anything -[post_commands] -# "Emacs Snapshot" = "rm -rf ~/.emacs.d/elpa.bak && cp -rl ~/.emacs.d/elpa ~/.emacs.d/elpa.bak" - - -# Custom commands -[commands] -# "Python Environment" = "~/dev/.env/bin/pip install -i https://pypi.python.org/simple -U --upgrade-strategy eager jupyter" -# "Custom command using interactive shell (unix)" = "-i vim_upgrade" - - -[python] -# enable_pip_review = true ###disabled by default -# enable_pip_review_local = true ###disabled by default -# enable_pipupgrade = true ###disabled by default -# pipupgrade_arguments = "-y -u --pip-path pip" ###disabled by default - - -[composer] -# self_update = true - - -[brew] -# For the BrewCask step -# If `Repo Cask Upgrade` exists, then use the `-a` option. -# Otherwise, use the `--greedy` option. -# greedy_cask = true - -# For the BrewCask step -# If `Repo Cask Upgrade` does not exist, then use the `--greedy_latest` option. -# NOTE: the above entry `greedy_cask` contains this entry, though you can enable -# both of them, they won't clash with each other. -# greedy_latest = true - -# For the BrewFormula step -# Execute `brew autoremove` after the step. -# autoremove = true - -# For the BrewFormula step -# Upgrade formulae built from the HEAD branch; `brew upgrade --fetch-HEAD` -# fetch_head = true - - -[linux] -# Arch Package Manager to use. -# Allowed values: -# autodetect, aura, garuda_update, pacman, pamac, paru, pikaur, trizen, yay -arch_package_manager = "yay" - -# Arguments to pass yay (or paru) when updating packages -# yay_arguments = "--nodevel" - -# Arguments to pass dnf when updating packages -# dnf_arguments = "--refresh" - -# aura_aur_arguments = "-kx" - -# aura_pacman_arguments = "" -# garuda_update_arguments = "" - -show_arch_news = true - -# trizen_arguments = "--devel" - -# pikaur_arguments = "" - -# pamac_arguments = "--no-devel" - -# enable_tlmgr = true - -# emerge_sync_flags = "-q" - -# emerge_update_flags = "-uDNa --with-bdeps=y world" - -# redhat_distro_sync = false - -# suse_dup = false - -# rpm_ostree = false - -# nix_arguments = "--flake" - -# nix_env_arguments = "--prebuilt-only" - -# Extra Home Manager arguments -# home_manager_arguments = ["--flake", "file"] - - -[git] -# How many repos to pull at max in parallel -max_concurrency = 5 - -# Additional git repositories to pull -repos = [ - "~/code/*/" -] - -# Don't pull the predefined git repos -# pull_predefined = false - -# Arguments to pass Git when pulling Repositories -arguments = "--rebase --autostash" - - -[windows] -# Manually select Windows updates -# accept_all_updates = false - -# open_remotes_in_new_terminal = true - -# wsl_update_pre_release = true - -# wsl_update_use_web_download = true - -# Causes Topgrade to rename itself during the run to allow package managers -# to upgrade it. Use this only if you installed Topgrade by using a package -# manager such as Scoop or Cargo -# self_rename = true - - -[npm] -# Use sudo if the NPM directory isn't owned by the current user -# use_sudo = true - - -[yarn] -# Run `yarn global upgrade` with `sudo` -# use_sudo = true - - -[vim] -# For `vim-plug`, execute `PlugUpdate!` instead of `PlugUpdate` -force_plug_update = true - - -[firmware] -# Offer to update firmware; if false just check for and display available updates -upgrade = false - - -[vagrant] -# Vagrant directories -# directories = [] - -# power on vagrant boxes if needed -# power_on = true - -# Always suspend vagrant boxes instead of powering off -# always_suspend = true - - -[flatpak] -# Use sudo for updating the system-wide installation -# use_sudo = true - - -[distrobox] -# use_root = false - -# containers = ["archlinux-latest"] -[containers] -# Specify the containers to ignore while updating (Wildcard supported) -# ignored_containers = ["ghcr.io/rancher-sandbox/rancher-desktop/rdx-proxy:latest", "docker.io*"] diff --git a/dotfiles/common/.config/transmission-daemon b/dotfiles/common/.config/transmission-daemon deleted file mode 120000 index e768f74..0000000 --- a/dotfiles/common/.config/transmission-daemon +++ /dev/null @@ -1 +0,0 @@ -transmission
\ No newline at end of file diff --git a/dotfiles/common/.config/transmission/settings.json b/dotfiles/common/.config/transmission/settings.json deleted file mode 100644 index c232f9d..0000000 --- a/dotfiles/common/.config/transmission/settings.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "alt-speed-down": 500, - "alt-speed-enabled": true, - "alt-speed-time-begin": 480, - "alt-speed-time-day": 127, - "alt-speed-time-enabled": true, - "alt-speed-time-end": 1380, - "alt-speed-up": 500, - "announce-ip": "", - "announce-ip-enabled": false, - "anti-brute-force-enabled": false, - "anti-brute-force-threshold": 100, - "bind-address-ipv4": "0.0.0.0", - "bind-address-ipv6": "::", - "blocklist-date": 0, - "blocklist-enabled": false, - "blocklist-updates-enabled": true, - "blocklist-url": "http://www.example.com/blocklist", - "cache-size-mb": 4, - "compact-view": false, - "default-trackers": "", - "details-window-height": 712, - "details-window-width": 831, - "dht-enabled": true, - "download-dir": "/home/cjennings/downloads/torrents/complete", - "download-queue-enabled": true, - "download-queue-size": 5, - "encryption": 1, - "filter-mode": "show-downloading", - "filter-trackers": "", - "idle-seeding-limit": 30, - "idle-seeding-limit-enabled": false, - "incomplete-dir": "/home/cjennings/downloads/torrents/incomplete", - "incomplete-dir-enabled": true, - "inhibit-desktop-hibernation": false, - "lpd-enabled": true, - "main-window-height": 981, - "main-window-is-maximized": false, - "main-window-layout-order": "menu,toolbar,filter,list,statusbar", - "main-window-width": 1504, - "main-window-x": -3016, - "main-window-y": 22, - "message-level": 4, - "open-dialog-dir": "/home/cjennings", - "peer-congestion-algorithm": "", - "peer-limit-global": 200, - "peer-limit-per-torrent": 50, - "peer-port": 51413, - "peer-port-random-high": 65535, - "peer-port-random-low": 49152, - "peer-port-random-on-start": false, - "peer-socket-tos": "le", - "pex-enabled": true, - "port-forwarding-enabled": true, - "preallocation": 1, - "prefetch-enabled": true, - "prompt-before-exit": true, - "queue-stalled-enabled": true, - "queue-stalled-minutes": 30, - "ratio-limit": 2, - "ratio-limit-enabled": false, - "read-clipboard": false, - "remote-session-enabled": false, - "remote-session-host": "localhost", - "remote-session-https": false, - "remote-session-password": "", - "remote-session-port": 9091, - "remote-session-requres-authentication": false, - "remote-session-username": "", - "rename-partial-files": true, - "rpc-authentication-required": true, - "rpc-bind-address": "0.0.0.0", - "rpc-enabled": true, - "rpc-host-whitelist": "", - "rpc-host-whitelist-enabled": true, - "rpc-password": "{bad68dff996a5c90ed8d070ed66477bc49fe9f9fWgWCPwIG", - "rpc-port": 9091, - "rpc-socket-mode": "0750", - "rpc-url": "/transmission/", - "rpc-username": "cjennings", - "rpc-whitelist": "127.0.0.1,::1", - "rpc-whitelist-enabled": true, - "scrape-paused-torrents-enabled": true, - "script-torrent-added-enabled": false, - "script-torrent-added-filename": "", - "script-torrent-done-enabled": false, - "script-torrent-done-filename": "", - "script-torrent-done-seeding-enabled": false, - "script-torrent-done-seeding-filename": "", - "seed-queue-enabled": false, - "seed-queue-size": 10, - "show-backup-trackers": false, - "show-extra-peer-details": false, - "show-filterbar": true, - "show-notification-area-icon": true, - "show-options-window": false, - "show-statusbar": true, - "show-toolbar": true, - "show-tracker-scrapes": false, - "sort-mode": "sort-by-progress", - "sort-reversed": false, - "speed-limit-down": 500, - "speed-limit-down-enabled": false, - "speed-limit-up": 100, - "speed-limit-up-enabled": false, - "start-added-torrents": true, - "start-minimized": true, - "statusbar-stats": "total-ratio", - "tcp-enabled": true, - "torrent-added-notification-enabled": true, - "torrent-added-verify-mode": "fast", - "torrent-complete-notification-enabled": false, - "torrent-complete-sound-command": [ - "canberra-gtk-play", - "-i", - "complete-download", - "-d", - "transmission torrent downloaded" - ], - "torrent-complete-sound-enabled": true, - "trash-can-enabled": true, - "trash-original-torrent-files": true, - "umask": "022", - "upload-slots-per-torrent": 8, - "user-has-given-informed-consent": true, - "utp-enabled": true, - "watch-dir": "/home/cjennings/downloads/torrents/files", - "watch-dir-enabled": true -} diff --git a/dotfiles/common/.config/user-dirs.dirs b/dotfiles/common/.config/user-dirs.dirs deleted file mode 100644 index 0db0cae..0000000 --- a/dotfiles/common/.config/user-dirs.dirs +++ /dev/null @@ -1,15 +0,0 @@ -# This file is written by xdg-user-dirs-update -# If you want to change or add directories, just edit the line you're -# interested in. All local changes will be retained on the next run. -# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped -# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an -# absolute path. No other format is supported. -# -XDG_DESKTOP_DIR="$HOME/" -XDG_DOWNLOAD_DIR="$HOME/downloads" -XDG_TEMPLATES_DIR="$HOME/" -XDG_PUBLICSHARE_DIR="$HOME/" -XDG_DOCUMENTS_DIR="$HOME/documents" -XDG_MUSIC_DIR="$HOME/music" -XDG_PICTURES_DIR="$HOME/pictures" -XDG_VIDEOS_DIR="$HOME/videos" diff --git a/dotfiles/common/.config/user-dirs.locale b/dotfiles/common/.config/user-dirs.locale deleted file mode 100644 index 3e0b419..0000000 --- a/dotfiles/common/.config/user-dirs.locale +++ /dev/null @@ -1 +0,0 @@ -en_US
\ No newline at end of file diff --git a/dotfiles/common/.config/wavemon/wavemonrc b/dotfiles/common/.config/wavemon/wavemonrc deleted file mode 100644 index e39c80b..0000000 --- a/dotfiles/common/.config/wavemon/wavemonrc +++ /dev/null @@ -1,13 +0,0 @@ -interface = wlp192s0 -cisco_mac = off -sort_order = chan/sig -sort_ascending = off -scan_filter_band = both -scan_hidden_essids = on -stat_updates = 100 -lhist_slot_size = 4 -meter_smoothness = 0 -info_updates = 10 -override_auto_scale = off -transparent_bg = on -startup_screen = info screen diff --git a/dotfiles/common/.config/yt-dlp/config b/dotfiles/common/.config/yt-dlp/config deleted file mode 100644 index 2e024cd..0000000 --- a/dotfiles/common/.config/yt-dlp/config +++ /dev/null @@ -1,23 +0,0 @@ -# yt-dlp config -# Craig Jennings <c@cjennings.net> - -# Embed metadata into file ---embed-metadata - -# Best quality video+audio, fallback to best single file ---format bestvideo+bestaudio/best - -# Don't overwrite existing files ---no-overwrites - -# Continue on errors (useful for playlists) ---ignore-errors - -# Best audio quality for audio extraction (0=best, 9=worst) ---audio-quality 0 - -# Output template: ~/videos/channel-title.ext --o ~/videos/%(channel)s-%(title)s.%(ext)s - -# Download YouTube JS challenge solver from official yt-dlp/ejs repo ---remote-components ejs:github diff --git a/dotfiles/common/.config/zathura/zathurarc b/dotfiles/common/.config/zathura/zathurarc deleted file mode 100644 index 7f61084..0000000 --- a/dotfiles/common/.config/zathura/zathurarc +++ /dev/null @@ -1,8 +0,0 @@ -set selection-clipboard clipboard -set recolor true -map [normal] F2 bmark current -map [normal] F2 blist -map [fullscreen] F2 bmark current -map [fullscreen] F3 blist -map [normal] = zoom in -map [fullscreen] = zoom in
\ No newline at end of file |
