Created
May 18, 2022 09:06
-
-
Save neko-neko-nyan/35d59d5c2cdbe3dea2548a541bb986b3 to your computer and use it in GitHub Desktop.
Desktop background changing daemon (for linux, using feh)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
import json | |
import math | |
import os | |
import random | |
import subprocess | |
import sys | |
import threading | |
DEFAULT_BASEDIR = os.path.expanduser('~/Изображения/Saved backgrounds') | |
def sizes(cwd, imgs): | |
for line in subprocess.run(['feh', '-l', *imgs], cwd=cwd, stdout=subprocess.PIPE).stdout.decode().splitlines()[1:]: | |
i = line.split(maxsplit=7) | |
yield i[7], (int(i[2]), int(i[3])) | |
def index(directory, output): | |
files = {} | |
if os.path.isdir(directory): | |
f = os.listdir(directory) | |
end = len(f) | |
for i in range(0, end, 50): | |
print('\r{:>5} / {}'.format(i, end), end='') | |
files.update(dict(sizes(directory, f[i:i+50]))) | |
else: | |
directory = os.path.split(directory) | |
files[directory[1]] = next(sizes(directory[0], [directory[1]]))[1] | |
directory = directory[0] | |
with open(output, 'w') as f: | |
json.dump({ | |
'files': files, | |
'base_dir': directory | |
}, f) | |
def filter_sizes(inp, out, size): | |
size = size.split('@', 2) | |
limit = float(size[1]) | |
size = size[0].split('x', 2) | |
q = float(size[0]) / float(size[1]) | |
with open(inp) as f: | |
files = json.load(f) | |
files_out = {} | |
for path, (width, height) in files['files'].items(): | |
if math.fabs(width/height - q) < limit: | |
files_out[path] = (width, height) | |
print(len(files_out), 'of', len(files['files'])) | |
with open(out, 'w') as f: | |
json.dump({ | |
'files': files_out, | |
'base_dir': files['base_dir'] | |
}, f) | |
def save(file): | |
subprocess.Popen(['neko', 'gui', 'save-background', file]) | |
class History: | |
def __init__(self): | |
self._data = [] | |
self._curr = 0 | |
def add(self, item): | |
self._data.append(item) | |
self._curr = len(self._data) - 1 | |
def prev(self, c=2): | |
self._curr -= c | |
def next(self): | |
if len(self._data) - 1 == self._curr or not self._data: | |
return None | |
self._curr += 1 | |
return self._data[self._curr] | |
def curr(self): | |
return self._data[self._curr] | |
def reset(self): | |
d = self._data | |
self._data = [] | |
self._curr = 0 | |
return d | |
class BackgroundChanger(threading.Thread): | |
def __init__(self, timeout=10.0, shuffle=True, scale_mode='max'): | |
super().__init__(name=self.__class__.__name__) | |
self.timeout = timeout | |
self.shuffle = shuffle | |
self.scale_mode = scale_mode | |
self.input = [] | |
self.shown = History() | |
self.pause = False | |
self._waiting = threading.Condition(threading.Lock()) | |
def run(self): | |
while True: | |
image = self._next_image() | |
self._show(image) | |
with self._waiting: | |
self._waiting.wait(None if self.pause else self.timeout) | |
@staticmethod | |
def _show(image): | |
subprocess.run(['xfconf-query', '-c', 'xfce4-desktop', '-p', | |
'/backdrop/screen0/monitoreDP/workspace0/last-image', '-s', image]) | |
def _next_image(self): | |
image = self.shown.next() | |
if image: | |
return image | |
if not self.input: | |
self.input = self.shown.reset() | |
if self.shuffle: | |
i = random.randint(0, len(self.input) - 1) | |
image = self.input.pop(i) | |
else: | |
image = self.input.pop(0) | |
self.shown.add(image) | |
return image | |
def save(self): | |
save(self.shown.curr()) | |
def next(self, prev=False): | |
if prev: | |
self.shown.prev() | |
with self._waiting: | |
self._waiting.notify_all() | |
def add_files(self, inp): | |
with open(inp) as f: | |
files = json.load(f) | |
base = files['base_dir'] | |
self.input.extend((os.path.join(base, i) for i in files['files'].keys())) | |
def toggle_pause(self, pause=None): | |
if pause is None: | |
self.pause = not self.pause | |
else: | |
self.pause = pause | |
if self.pause: | |
self.shown.prev(1) | |
with self._waiting: | |
self._waiting.notify_all() | |
class InputDispatcher(threading.Thread): | |
def __init__(self, changer, pipe='/tmp/bg-sc.pipe'): | |
super().__init__(name=self.__class__.__name__) | |
self.changer = changer | |
self.pipe = pipe | |
def run(self): | |
if not os.path.exists(self.pipe): | |
os.mkfifo(self.pipe) | |
with open(self.pipe, 'r') as f: | |
while True: | |
line = f.readline().split(maxsplit=1) | |
cmd = line[0] if line else None | |
arg = line[1] if len(line) > 1 else None | |
if cmd == 'next': | |
self.changer.next() | |
elif cmd == 'prev': | |
self.changer.next(True) | |
elif cmd == 'timeout': | |
self.changer.timeout = float(arg) | |
elif cmd == 'scale_mode': | |
self.changer.scale_mode = arg | |
elif cmd == 'add_files': | |
self.changer.add_files(arg) | |
elif cmd == 'save': | |
self.changer.save() | |
elif cmd == 'shuffle': | |
self.changer.shuffle = True | |
elif cmd == 'no' and arg == 'shuffle': | |
self.changer.shuffle = False | |
elif cmd == 'toggle' and arg == 'shuffle': | |
self.changer.shuffle = not self.changer.shuffle | |
elif cmd == 'pause': | |
self.changer.toggle_pause(True) | |
elif cmd == 'no' and arg == 'pause': | |
self.changer.toggle_pause(False) | |
elif cmd == 'toggle' and arg == 'pause': | |
self.changer.toggle_pause() | |
def show(inp=None, timeout=10.0, shuffle=True, scale_mode='max', pipe='/tmp/bg-sc.pipe'): | |
c = BackgroundChanger(float(timeout), bool(shuffle), scale_mode) | |
d = InputDispatcher(c, pipe) | |
if inp: | |
c.add_files(inp) | |
c.start() | |
d.start() | |
def main(): | |
if len(sys.argv) < 2: | |
print("Usage: %s COMMAND ARGS..." % sys.argv[0]) | |
sys.exit(1) | |
cmd = sys.argv[1] | |
if cmd == 'index': | |
index(sys.argv[2], sys.argv[3]) | |
elif cmd == 'filter': | |
filter_sizes(sys.argv[2], sys.argv[3], sys.argv[4]) | |
elif cmd == 'show': | |
if len(sys.argv) == 2: | |
show() | |
else: | |
show(sys.argv[2], **dict((i.split('=', 1) for i in sys.argv[3:]))) | |
elif cmd == 'exec': | |
s = '/tmp/bg-sc.pipe' | |
i = 2 | |
if sys.argv[2][:2] == '-S': | |
s = sys.argv[2] | |
i = 3 | |
with open(s, 'w') as f: | |
f.write(' '.join(sys.argv[i:])) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment