Last active
August 29, 2015 14:22
-
-
Save Chocimier/633ff525eb87e7d4a3f5 to your computer and use it in GitHub Desktop.
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/env python3 | |
import os | |
import re | |
import sys | |
from curses.ascii import isblank | |
class DesktopEntry: | |
name = '' | |
icon = '' | |
executable = '' | |
identifier = '' | |
path = '' | |
types = [] | |
def __init__(self, baseDirectory, relative): | |
try: | |
lines = open(baseDirectory + '/' + relative).readlines() | |
except (FileNotFoundError, UnicodeDecodeError): | |
return | |
self.path = baseDirectory + '/' + relative | |
self.identifier = relative.replace('/', '-') | |
while self.identifier.startswith('-'): | |
self.identifier = self.identifier[1:] | |
group = '' | |
for line in lines: | |
if line == '' or line[0] == '#' or isblank(line[0]): | |
continue | |
line = line.strip() | |
if line.startswith('['): | |
group = line | |
if group != '[Desktop Entry]': | |
continue | |
if line.startswith('Name='): | |
self.name = line.split('=', maxsplit=1)[1] | |
elif line.startswith('Icon='): | |
self.icon = line.split('=', maxsplit=1)[1] | |
elif line.startswith('Exec='): | |
self.executable = line.split('=', maxsplit=1)[1] | |
elif line.startswith('MimeType='): | |
self.types = line.split('=', maxsplit=1)[1].split(';') | |
self.types = list(filter(lambda x: x, self.types)) | |
def __repr__(self): | |
return "DE({} <{}> <{}>)".format(self.name, self.identifier, self.path) | |
class MimeAppsSpec: | |
directoryPatterns = [ | |
'$XDG_DATA_DIRS/applications/', | |
'$XDG_DATA_HOME/applications/', | |
'$XDG_CONFIG_DIRS/', | |
'$XDG_CONFIG_HOME/', | |
] | |
defaultVariableValues = { | |
'XDG_DATA_HOME': '$HOME/.local/share', | |
'XDG_CONFIG_HOME': '$HOME/.config', | |
'XDG_DATA_DIRS': '/usr/local/share/:/usr/share/', | |
'XDG_CONFIG_DIRS': '/etc/xdg' | |
} | |
applicationsCache = {} | |
knownApplications = {} | |
def __init__(self): | |
self.findDirectories() | |
self.createBase() | |
@staticmethod | |
def resolveVariable(path): | |
try: | |
var = re.match('.*?\\$([A-Z_]+)', path).group(1) | |
except AttributeError: | |
return [path] | |
if var in os.environ and len(os.environ[var]): | |
value = os.environ[var] | |
elif var in MimeAppsSpec.defaultVariableValues: | |
value = MimeAppsSpec.defaultVariableValues[var] | |
else: | |
return [] | |
directories = list(filter(lambda x: len(x), value.split(':'))) | |
return [path.replace('$'+var, i) for i in directories] | |
def findDirectories(self): | |
self.directories = self.directoryPatterns | |
while list(filter(lambda x: re.match('.*?\\$([A-Z_]+)', x), self.directories)): | |
directories_ = [] | |
for i in map(self.resolveVariable, self.directories): | |
for j in i: | |
directories_.append(j) | |
self.directories = directories_ | |
def removeFromType(self, type, appId): | |
if type in self.applicationsCache: | |
def f(x): | |
return x.identifier != appId | |
self.applicationsCache[type] = list(filter(f, self.applicationsCache[type])) | |
def addToType(self, type, entry): | |
if type not in self.applicationsCache: | |
self.applicationsCache[type] = [] | |
else: | |
self.removeFromType(type, entry.identifier) | |
self.applicationsCache[type] = [entry] + self.applicationsCache[type] | |
def addApplication(self, entry): | |
for type in self.applicationsCache: | |
self.removeFromType(type, entry.identifier) | |
self.knownApplications[entry.identifier] = entry | |
for type in entry.types: | |
self.addToType(type, entry) | |
def processDesktopFile(self, baseDirectory, relative): | |
entry = DesktopEntry(baseDirectory, relative) | |
self.addApplication(entry) | |
def processDesktopInDirectory(self, baseDirectory, relative): | |
try: | |
filenames = os.listdir(baseDirectory + '/' + relative) | |
except FileNotFoundError: | |
return | |
for filename in filenames: | |
if filename.endswith('.desktop'): | |
self.processDesktopFile(baseDirectory, relative + '/' + filename) | |
def processMimeapps(self, path): | |
try: | |
lines = open(path).readlines() | |
except FileNotFoundError: | |
return | |
added = False | |
removed = False | |
for line in lines: | |
if line == '' or line[0] == '#' or isblank(line[0]): | |
continue | |
line = line.strip() | |
if line == '[Added Associations]': | |
added = True | |
removed = False | |
elif line == '[Removed Associations]': | |
added = False | |
removed = True | |
elif line.startswith('['): | |
added = False | |
removed = False | |
elif (line.count('=') > 0): | |
type, appIds = line.split('=', maxsplit=1) | |
appIds = appIds.split(';') | |
appIds = list(filter(lambda x: x, appIds)) | |
appIds.reverse() | |
if added: | |
for appId in appIds: | |
if appId in self.knownApplications: | |
self.addToType(type, self.knownApplications[appId]) | |
elif removed: | |
for appId in appIds: | |
self.removeFromType(type, appId) | |
def processDirectory(self, baseDirectory, relative): | |
directory = baseDirectory + '/' + relative | |
try: | |
subdirectories = os.listdir(directory) | |
except NotADirectoryError: | |
return | |
except FileNotFoundError: | |
return | |
for subdirectory in subdirectories: | |
self.processDirectory(baseDirectory, relative + '/' + subdirectory) | |
if directory[-1] != '/': | |
directory += '/' | |
self.processDesktopInDirectory(baseDirectory, relative) | |
self.processMimeapps(directory + 'mimeapps.list') | |
def createBase(self): | |
for directory in self.directories: | |
self.processDirectory(directory, '') | |
def appsForMime(self, type): | |
return self.applicationsCache[type] | |
if __name__ == '__main__': | |
base = MimeAppsSpec() | |
for i in base.appsForMime('application/pdf'): | |
print(i) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment