first changes

This commit is contained in:
2018-04-05 05:30:27 +02:00
parent 12bb915b60
commit 2b1789d8fc
27 changed files with 1094 additions and 116 deletions

View File

@@ -0,0 +1,128 @@
from .Module import Module as _Module
import locale as _locale
import subprocess as _subprocess
import shlex as _shlex
from pathlib import Path as _Path
from os import listdir as _listdir
from os.path import isfile as _isfile, join as _join
class Application(_Module):
def __init__(self):
self.db_type = "Application"
self._home = str(_Path.home())
self._lang = _locale.getlocale()[0].split("_")[0].strip()
self._dirs = ["/usr/share/applications", self._home + "/.local/share/applications"]
self._references = {}
def build_db(self):
"""Function which adds entries to the database.
returns dict with:
Name, json, disabled, type, "file" """
db = {}
for d in self._dirs:
for fi in _listdir(d):
if _isfile(_join(d, fi)):
fc = self._scan_file(_join(d, fi))
for k, v in fc.items():
db.update({k: {
"Name": k,
"json": v,
"disabled": (("NoDisplay" in v and v["NoDisplay"] == "true") or
("Hidden" in v and v["Hidden"] == "true")),
"file": v['file'],
"type": self.db_type
}})
return db
def update_db(self, database: dict):
"""Function which updates entries in the database.
returns dict with modified/deleted/added database entries"""
entries = self.build_db()
dbremove = []
# dbKey == id, dbValue dict of fileds
for dbKey, dbValue in database:
if dbValue["type"] == self.db_type:
if dbValue["Name"] not in entries:
dbremove.append(dbKey)
else:
# update all values managed by this module
for k, v in entries[dbValue["Name"]]:
dbValue[k] = v
# remove no more existing entries
for i in dbremove:
database.pop(i)
return database
def build_menu(self, items: dict):
"""Builds the menu entries.
return list of tuples (id, name, hits)"""
# we only get entries of our app type so no problem here
# json is already unjsonified
NAME_PREFIX="run "
NAME_POSTFIX=" (%PATH%)"
entries = []
for r in items:
identifier=(NAME_PREFIX + r['Name'] + NAME_POSTFIX).replace("%PATH%", r['json']['Exec'])
entries.append((r['ID'], identifier, r['hits']))
if ("Terminal" not in r["json"]):
r['json']["Terminal"] = "false"
self._references[identifier] = (r['json']["Exec"], r['json']["Terminal"], r['file'], r['Name'])
return entries
def call(self, cmd: str):
"""Handles the selected entry"""
# find the entry
reference = list(filter(lambda x: cmd.startswith(x), self._references.keys()))
entry = reference[0]
cmd = cmd.replace(entry, "").lstrip()
# Just call the program
_subprocess.Popen(_shlex.split(self._expand_fieldcodes(self._references[entry][0], cmd, reference)),
encoding="utf-8",
shell=(self._references[entry][1] == "true"))
def _scan_file(self, fi):
entries = {}
with open(fi, encoding="utf-8", mode="r") as f:
data = {}
for line in f:
line = line.strip()
if line.startswith("["): # and line.rstrip() != "[Desktop Entry]":
data["header"] = line.strip("[]")
if "Name" in data and "Exec" in data:
data['file'] = fi
entries[data["Name"]] = data
data = {}
if "=" in line:
s = line.split("=")
if "[" in s[0] and "]" in s[0]:
if "[" + self._lang + "]" in s[0]:
data[s[0].replace("[" + self._lang + "]", "").strip()] = s[1]
elif s[0] != "Name" or "Name" not in data.keys():
data[s[0]] = s[1]
if "Name" in data:
data['file'] = fi
entries[data["Name"]] = data
return entries
def _expand_fieldcodes(self, entry: str, cmd: str, reference):
fieldcodes = ['%f', '%F', '%u', '%U', '%i', '%c', '%k'] # todo implement %i as expand icon-key
for fc in range(0, len(fieldcodes)):
fieldcodes[fc] = fieldcodes[fc] in entry
if True in fieldcodes:
if '%k' in entry:
entry = entry.replace('%k', '"' + reference[2] + "'")
if '%c' in entry:
entry = entry.replace('%c', '"' + reference[3] + "'")
if '%i' in entry:
entry = entry.replace(' %i', "")
if '%f' in entry or '%F' in entry or '%u' in entry or '%U' in entry:
entry = entry.replace('%f', cmd)
entry = entry.replace('%F', cmd)
entry = entry.replace('%u', cmd)
entry = entry.replace('%U', cmd)
cmd = ""
return (entry + " " + cmd).rstrip()

View File

@@ -0,0 +1,118 @@
from .Module import Module as _Module
import locale as _locale
from pathlib import Path as _Path
from Dmenu import Dmenu
import subprocess as _subprocess
class MPC(_Module):
def __init__(self):
self.db_type = "MPC"
self._home = str(_Path.home())
self._lang = _locale.getlocale()[0].split("_")[0].strip()
self._dirs = ["/usr/share/applications", self._home + "/.local/share/applications"]
self.replace = True
def build_db(self):
"""Function which adds entries to the database.
returns dict with:
Name, json, disabled, type, "file" """
# we only add an generic Submenu entry.
db = {"Media Player Control [MPC]": {
"Name": "Media Player Control [MPC]",
"json": "",
"disabled": False,
"file": "none",
"type": self.db_type
}}
return db
def update_db(self, database: dict):
"""Function which updates entries in the database.
returns dict with modified/deleted/added database entries"""
return self.build_db()
def build_menu(self, items: dict):
"""Builds the menu entries.
return list of tuples (id, name, hits)"""
# we only get entries of our app type so no problem here
# json is already unjsonified
entries = []
for r in items:
entries.append((r['ID'], r['Name'], r['hits']))
return entries
def gatherInfo(self):
out = {}
infoprocess = _subprocess.run("mpc", stdout=_subprocess.PIPE, shell=True)
info = infoprocess.stdout.decode("utf8").split("\n")
if len(info) == 4:
status = [x.split(":") for x in info[2].split(" ")]
out["song"] = info[0]
out["playing"] = "[playing]" in info[1]
else:
status = [x.split(":") for x in info[0].split(" ")]
out["replace"] = self.replace
status = [x for x in status if len(x) == 2]
for i in status:
i[0] = i[0].strip()
i[1] = i[1].strip()
if i[1] == "off":
out[i[0]] = False
elif i[1] == "on":
out[i[0]] = True
else:
out[i[0]] = i[1]
return out
def buildStatusMenu(self):
info = self.gatherInfo()
if info["replace"]:
playlists = ['MODE: REPLACE']
else:
playlists = ['MODE: APPEND']
if info["random"]:
playlists += ['RANDOM: on']
else:
playlists += ['RANDOM: off']
return (len(playlists), playlists)
def handleStatusMenu(self, text: str):
if text.startswith("MODE: "):
self.replace = not self.replace
elif text.startswith("RANDOM: "):
_subprocess.run("mpc random", shell=True)
def call(self, cmd: str):
"""Handles the selected entry"""
# our menu entry was selected, time to create and show the submenu
while True:
infolen, playlists = self.buildStatusMenu()
playprocess = _subprocess.run("mpc lsplaylists", stdout=_subprocess.PIPE, shell=True)
playlists += playprocess.stdout.decode("utf8").split("\n")
menu = Dmenu(playlists)
ex = menu.run()
if ex[0] == 0:
if ex[1].rstrip() in playlists[0:infolen]:
self.handleStatusMenu(ex[1].rstrip())
continue
# user selected an entry
# mpc clear(?) -> mpc load ex[1] -> mpc play
command = "mpc load '" + ex[1].rstrip() + "'"
if self.replace:
command = "mpc clear; " + command + "; mpc play"
_subprocess.run(command, shell=True)
break
else:
break

View File

@@ -0,0 +1,25 @@
class Module(object):
"""Abstract class, implement: build_db, update_db, build_menu, call"""
def __init__(self, obj):
pass
def build_db():
"""Function which adds entries to the database.
returns dict with:
Name, json, disabled, type, "file" """
raise NotImplementedError("Should have implemented this")
def update_db(database: dict):
"""Function which updates entries in the database.
returns dict with modified/deleted/added database entries"""
raise NotImplementedError("Should have implemented this")
def build_menu(items: dict):
"""Builds the menu entries.
return list of tuples (id, name, hits)"""
raise NotImplementedError("Should have implemented this")
def call(entry: str):
"""Handles the selected entry"""
raise NotImplementedError("Should have implemented this")

View File

@@ -0,0 +1,2 @@
from .Application import Application
from .MPC import MPC