mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-08 09:44:15 +02:00
installing, uninstalling and launching
This commit is contained in:
@@ -162,20 +162,29 @@ class SoftwareManager(ABC):
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if self.context.disk_cache and pkg.supports_disk_cache():
|
if self.context.disk_cache and pkg.supports_disk_cache():
|
||||||
|
self.serialize_to_disk(pkg, icon_bytes, only_icon)
|
||||||
|
|
||||||
if not only_icon:
|
def serialize_to_disk(self, pkg: SoftwarePackage, icon_bytes: bytes, only_icon: bool):
|
||||||
Path(pkg.get_disk_cache_path()).mkdir(parents=True, exist_ok=True)
|
"""
|
||||||
data = pkg.get_data_to_cache()
|
Sames as above, but does not check if disk cache is enabled or supported by the package instance
|
||||||
|
:param pkg:
|
||||||
|
:param icon_bytes:
|
||||||
|
:param only_icon:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if not only_icon:
|
||||||
|
Path(pkg.get_disk_cache_path()).mkdir(parents=True, exist_ok=True)
|
||||||
|
data = pkg.get_data_to_cache()
|
||||||
|
|
||||||
if data:
|
if data:
|
||||||
with open(pkg.get_disk_data_path(), 'w+') as f:
|
with open(pkg.get_disk_data_path(), 'w+') as f:
|
||||||
f.write(json.dumps(data))
|
f.write(json.dumps(data))
|
||||||
|
|
||||||
if icon_bytes:
|
if icon_bytes:
|
||||||
Path(pkg.get_disk_cache_path()).mkdir(parents=True, exist_ok=True)
|
Path(pkg.get_disk_cache_path()).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
with open(pkg.get_disk_icon_path(), 'wb+') as f:
|
with open(pkg.get_disk_icon_path(), 'wb+') as f:
|
||||||
f.write(icon_bytes)
|
f.write(icon_bytes)
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def requires_root(self, action: str, pkg: SoftwarePackage):
|
def requires_root(self, action: str, pkg: SoftwarePackage):
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ class SoftwarePackage(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def can_be_run(self) -> str:
|
def can_be_run(self) -> bool:
|
||||||
"""
|
"""
|
||||||
:return: whether the app can be run via the GUI
|
:return: whether the app can be run via the GUI
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Set, Type, List
|
from typing import Set, Type, List
|
||||||
|
|
||||||
@@ -10,17 +12,18 @@ from bauh.api.abstract.controller import SoftwareManager, SearchResult
|
|||||||
from bauh.api.abstract.disk import DiskCacheLoader
|
from bauh.api.abstract.disk import DiskCacheLoader
|
||||||
from bauh.api.abstract.handler import ProcessWatcher
|
from bauh.api.abstract.handler import ProcessWatcher
|
||||||
from bauh.api.abstract.model import SoftwarePackage, PackageHistory, PackageUpdate, PackageSuggestion
|
from bauh.api.abstract.model import SoftwarePackage, PackageHistory, PackageUpdate, PackageSuggestion
|
||||||
|
from bauh.api.abstract.view import MessageType
|
||||||
from bauh.api.constants import HOME_PATH
|
from bauh.api.constants import HOME_PATH
|
||||||
from bauh.commons.html import bold
|
from bauh.commons.html import bold
|
||||||
from bauh.commons.system import SystemProcess, new_subprocess, ProcessHandler
|
from bauh.commons.system import SystemProcess, new_subprocess, ProcessHandler, run_cmd
|
||||||
from bauh.gems.appimage import query, INSTALLATION_PATH
|
from bauh.gems.appimage import query, INSTALLATION_PATH
|
||||||
from bauh.gems.appimage.model import AppImage
|
from bauh.gems.appimage.model import AppImage
|
||||||
|
|
||||||
DB_PATH = '{}/{}'.format(HOME_PATH, '.cache/bauh/appimage/appimage.db')
|
DB_PATH = '{}/{}'.format(HOME_PATH, '.local/share/bauh/appimage/appimage.db')
|
||||||
DESKTOP_ENTRIES_PATH = '{}/.local/share/applications'.format(HOME_PATH)
|
DESKTOP_ENTRIES_PATH = '{}/.local/share/applications'.format(HOME_PATH)
|
||||||
|
|
||||||
RE_DESKTOP_EXEC = re.compile(r'Exec\s+=\s+.+\n')
|
RE_DESKTOP_EXEC = re.compile(r'Exec\s*=\s*.+\n')
|
||||||
RE_DESKTOP_ICON = re.compile(r'Icon\s+=\s+.+\n')
|
RE_DESKTOP_ICON = re.compile(r'Icon\s*=\s*.+\n')
|
||||||
RE_ICON_ENDS_WITH = re.compile(r'.+\.(png|svg)$')
|
RE_ICON_ENDS_WITH = re.compile(r'.+\.(png|svg)$')
|
||||||
|
|
||||||
|
|
||||||
@@ -54,12 +57,28 @@ class AppImageManager(SoftwareManager):
|
|||||||
res.new.append(app)
|
res.new.append(app)
|
||||||
finally:
|
finally:
|
||||||
connection.close()
|
connection.close()
|
||||||
|
else:
|
||||||
|
self.logger.warning('Could not get a connection from the local database at {}'.format(DB_PATH))
|
||||||
|
|
||||||
res.total = len(res.installed) + len(res.new)
|
res.total = len(res.installed) + len(res.new)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def read_installed(self, disk_loader: DiskCacheLoader, limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None, internet_available: bool = None) -> SearchResult:
|
def read_installed(self, disk_loader: DiskCacheLoader, limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None, internet_available: bool = None) -> SearchResult:
|
||||||
return SearchResult([], [], 0)
|
res = SearchResult([], [], 0)
|
||||||
|
|
||||||
|
if os.path.exists(INSTALLATION_PATH):
|
||||||
|
installed = run_cmd('ls {}*/data.json'.format(INSTALLATION_PATH), print_error=False)
|
||||||
|
|
||||||
|
if installed:
|
||||||
|
for path in installed.split('\n'):
|
||||||
|
if path:
|
||||||
|
with open(path) as f:
|
||||||
|
app = AppImage(installed=True, **json.loads(f.read()))
|
||||||
|
|
||||||
|
res.installed.append(app)
|
||||||
|
|
||||||
|
res.total = len(res.installed)
|
||||||
|
return res
|
||||||
|
|
||||||
def downgrade(self, pkg: AppImage, root_password: str, watcher: ProcessWatcher) -> bool:
|
def downgrade(self, pkg: AppImage, root_password: str, watcher: ProcessWatcher) -> bool:
|
||||||
pass
|
pass
|
||||||
@@ -68,7 +87,18 @@ class AppImageManager(SoftwareManager):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def uninstall(self, pkg: AppImage, root_password: str, watcher: ProcessWatcher) -> bool:
|
def uninstall(self, pkg: AppImage, root_password: str, watcher: ProcessWatcher) -> bool:
|
||||||
pass
|
if os.path.exists(pkg.get_disk_cache_path()):
|
||||||
|
handler = ProcessHandler(watcher)
|
||||||
|
|
||||||
|
if not handler.handle(SystemProcess(new_subprocess(['rm', '-rf', pkg.get_disk_cache_path()]))):
|
||||||
|
watcher.show_message(title=self.i18n['error'], body=self.i18n['appimage.uninstall.error.remove_folder'].format(bold(pkg.get_disk_cache_path())))
|
||||||
|
return False
|
||||||
|
|
||||||
|
de_path = self._gen_desktop_entry_path(pkg)
|
||||||
|
if os.path.exists(de_path):
|
||||||
|
os.remove(de_path)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def get_managed_types(self) -> Set[Type[SoftwarePackage]]:
|
def get_managed_types(self) -> Set[Type[SoftwarePackage]]:
|
||||||
return {AppImage}
|
return {AppImage}
|
||||||
@@ -91,6 +121,12 @@ class AppImageManager(SoftwareManager):
|
|||||||
if f.endswith('.desktop'):
|
if f.endswith('.desktop'):
|
||||||
return f
|
return f
|
||||||
|
|
||||||
|
def _find_appimage_file(self, folder: str) -> str:
|
||||||
|
for r, d, files in os.walk(folder):
|
||||||
|
for f in files:
|
||||||
|
if f.lower().endswith('.appimage'):
|
||||||
|
return '{}/{}'.format(folder, f)
|
||||||
|
|
||||||
def _find_icon_file(self, folder: str) -> str:
|
def _find_icon_file(self, folder: str) -> str:
|
||||||
for r, d, files in os.walk(folder):
|
for r, d, files in os.walk(folder):
|
||||||
for f in files:
|
for f in files:
|
||||||
@@ -112,49 +148,48 @@ class AppImageManager(SoftwareManager):
|
|||||||
permission_given = handler.handle(SystemProcess(new_subprocess(['chmod', 'a+x', file_path])))
|
permission_given = handler.handle(SystemProcess(new_subprocess(['chmod', 'a+x', file_path])))
|
||||||
|
|
||||||
if permission_given:
|
if permission_given:
|
||||||
watcher.change_substatus('Reading content from {}'.format(bold(file_name)))
|
|
||||||
extracted = handler.handle(SystemProcess(new_subprocess([file_name, '--appimage-extract'], cwd=out_dir)))
|
|
||||||
|
|
||||||
if extracted:
|
watcher.change_substatus(self.i18n['appimage.install.extract'].format(bold(file_name)))
|
||||||
watcher.change_substatus('Generating desktop entry')
|
|
||||||
extracted_folder = '{}/{}'.format(out_dir, 'squashfs-root')
|
|
||||||
|
|
||||||
if os.path.exists(extracted_folder):
|
handler.handle(SystemProcess(new_subprocess([file_path, '--appimage-extract'], cwd=out_dir)))
|
||||||
desktop_entry = self._find_desktop_file(extracted_folder)
|
|
||||||
|
|
||||||
if desktop_entry:
|
watcher.change_substatus(self.i18n['appimage.install.desktop_entry'])
|
||||||
with open(desktop_entry) as f:
|
extracted_folder = '{}/{}'.format(out_dir, 'squashfs-root')
|
||||||
de_content = f.read()
|
|
||||||
|
|
||||||
de_content = RE_DESKTOP_EXEC.sub('Exec='.format(file_path), de_content)
|
if os.path.exists(extracted_folder):
|
||||||
|
desktop_entry = self._find_desktop_file(extracted_folder)
|
||||||
|
|
||||||
extracted_icon = self._find_icon_file(extracted_folder)
|
with open('{}/{}'.format(extracted_folder, desktop_entry)) as f:
|
||||||
|
de_content = f.read()
|
||||||
|
|
||||||
if extracted_icon:
|
de_content = RE_DESKTOP_EXEC.sub('Exec={}\n'.format(file_path), de_content)
|
||||||
icon_path = out_dir + '/' + extracted_icon.split('/')[-1]
|
|
||||||
shutil.copy(icon_path, icon_path)
|
|
||||||
de_content = RE_DESKTOP_ICON.sub('Icon='.format(icon_path), de_content)
|
|
||||||
|
|
||||||
Path(DESKTOP_ENTRIES_PATH).mkdir(parents=True, exist_ok=True)
|
extracted_icon = self._find_icon_file(extracted_folder)
|
||||||
|
|
||||||
with open('{}/bauh_appimage_{}.desktop'.format(DESKTOP_ENTRIES_PATH, pkg.name.lower()), 'w+') as f:
|
if extracted_icon:
|
||||||
f.write(de_content)
|
icon_path = out_dir + '/logo.' + extracted_icon.split('.')[-1]
|
||||||
|
shutil.copy('{}/{}'.format(extracted_folder, extracted_icon), icon_path)
|
||||||
|
de_content = RE_DESKTOP_ICON.sub('Icon={}\n'.format(icon_path), de_content)
|
||||||
|
pkg.icon_path = icon_path
|
||||||
|
|
||||||
return True
|
Path(DESKTOP_ENTRIES_PATH).mkdir(parents=True, exist_ok=True)
|
||||||
else:
|
|
||||||
pass
|
with open(self._gen_desktop_entry_path(pkg), 'w+') as f:
|
||||||
# todo generate new
|
f.write(de_content)
|
||||||
else:
|
|
||||||
watcher.show_message(title=self.i18n['error'],
|
shutil.rmtree(extracted_folder)
|
||||||
body='Could not find extracted the content from {}'.format(
|
return True
|
||||||
bold(file_name)))
|
|
||||||
else:
|
else:
|
||||||
watcher.show_message(title=self.i18n['error'],
|
watcher.show_message(title=self.i18n['error'],
|
||||||
body='Could not extract content from {}'.format(bold(file_name)))
|
body='Could extract content from {}'.format(bold(file_name)),
|
||||||
|
type_=MessageType.ERROR)
|
||||||
|
|
||||||
handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir])))
|
handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir])))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _gen_desktop_entry_path(self, app: AppImage) -> str:
|
||||||
|
return '{}/bauh_appimage_{}.desktop'.format(DESKTOP_ENTRIES_PATH, app.name.lower())
|
||||||
|
|
||||||
def is_enabled(self) -> bool:
|
def is_enabled(self) -> bool:
|
||||||
return self.enabled
|
return self.enabled
|
||||||
|
|
||||||
@@ -187,5 +222,14 @@ class AppImageManager(SoftwareManager):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def launch(self, pkg: AppImage):
|
def launch(self, pkg: AppImage):
|
||||||
# TODO
|
installation_dir = pkg.get_disk_cache_path()
|
||||||
pass
|
if os.path.exists(installation_dir):
|
||||||
|
appimag_path = self._find_appimage_file(installation_dir)
|
||||||
|
|
||||||
|
if appimag_path:
|
||||||
|
subprocess.Popen([appimag_path])
|
||||||
|
else:
|
||||||
|
self.logger.error("Could not find the AppImage file of '{}' in '{}'".format(pkg.name, installation_dir))
|
||||||
|
|
||||||
|
def cache_to_disk(self, pkg: SoftwarePackage, icon_bytes: bytes, only_icon: bool):
|
||||||
|
self.serialize_to_disk(pkg, icon_bytes, only_icon)
|
||||||
|
|||||||
@@ -2,19 +2,25 @@ from typing import List
|
|||||||
|
|
||||||
from bauh.api.abstract.model import SoftwarePackage
|
from bauh.api.abstract.model import SoftwarePackage
|
||||||
from bauh.commons import resource
|
from bauh.commons import resource
|
||||||
from bauh.gems.appimage import ROOT_DIR
|
from bauh.gems.appimage import ROOT_DIR, INSTALLATION_PATH
|
||||||
|
|
||||||
|
CACHED_ATTRS = {'name', 'description', 'version', 'icon_path', 'author'}
|
||||||
|
|
||||||
|
|
||||||
class AppImage(SoftwarePackage):
|
class AppImage(SoftwarePackage):
|
||||||
|
|
||||||
def __init__(self, name: str = None, description: str = None, github: str = None, source: str = None, version: str = None,
|
def __init__(self, name: str = None, description: str = None, github: str = None, source: str = None, version: str = None,
|
||||||
url_download: str = None, url_icon: str = None, license: str = None, pictures: List[str] = None):
|
url_download: str = None, url_icon: str = None, license: str = None, author: str = None,
|
||||||
|
pictures: List[str] = None, icon_path: str = None, installed: bool = False):
|
||||||
super(AppImage, self).__init__(id=name, name=name, version=version, latest_version=version,
|
super(AppImage, self).__init__(id=name, name=name, version=version, latest_version=version,
|
||||||
icon_url=url_icon, license=license, description=description)
|
icon_url=url_icon, license=license, description=description,
|
||||||
|
installed=installed)
|
||||||
self.source = source
|
self.source = source
|
||||||
self.github = github
|
self.github = github
|
||||||
self.pictures = pictures
|
self.pictures = pictures
|
||||||
self.url_download = url_download
|
self.url_download = url_download
|
||||||
|
self.icon_path = icon_path
|
||||||
|
self.author = author
|
||||||
|
|
||||||
def can_be_installed(self):
|
def can_be_installed(self):
|
||||||
return not self.installed and self.url_download
|
return not self.installed and self.url_download
|
||||||
@@ -44,17 +50,31 @@ class AppImage(SoftwarePackage):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def get_data_to_cache(self) -> dict:
|
def get_data_to_cache(self) -> dict:
|
||||||
# TODO
|
data = {}
|
||||||
pass
|
|
||||||
|
for a in CACHED_ATTRS:
|
||||||
|
val = getattr(self, a)
|
||||||
|
if val:
|
||||||
|
data[a] = val
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
def fill_cached_data(self, data: dict):
|
def fill_cached_data(self, data: dict):
|
||||||
# TODO
|
for a in CACHED_ATTRS:
|
||||||
pass
|
val = data.get(a)
|
||||||
|
|
||||||
def can_be_run(self) -> str:
|
if val:
|
||||||
# TODO
|
setattr(self, a, val)
|
||||||
return False
|
|
||||||
|
def can_be_run(self) -> bool:
|
||||||
|
return self.installed
|
||||||
|
|
||||||
def get_publisher(self) -> str:
|
def get_publisher(self) -> str:
|
||||||
# TODO
|
return self.author
|
||||||
pass
|
|
||||||
|
def get_disk_cache_path(self) -> str:
|
||||||
|
if self.name:
|
||||||
|
return INSTALLATION_PATH + self.name.lower()
|
||||||
|
|
||||||
|
def get_disk_icon_path(self):
|
||||||
|
return self.icon_path
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
ATTRS = ('name', 'description', 'github', 'source', 'version', 'url_download', 'url_icon', 'license')
|
ATTRS = ('name', 'description', 'github', 'source', 'version', 'url_download', 'url_icon', 'license', 'author')
|
||||||
|
|
||||||
_SELECT_BASE = "SELECT {} FROM apps".format(','.join(ATTRS))
|
_SELECT_BASE = "SELECT {} FROM apps".format(','.join(ATTRS))
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
gem.appimage.label=AppImage
|
gem.appimage.label=AppImage
|
||||||
appimage.install.permission=Giving execution permission to {}
|
appimage.install.permission=Giving execution permission to {}
|
||||||
|
appimage.install.extract=Extracting the content from {}
|
||||||
|
appimage.install.desktop_entry=Generating a menu shortcut
|
||||||
|
appimage.uninstall.error.remove_folder=Could not remove the application installation directory {}
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
gem.appimage.label=AppImage
|
gem.appimage.label=AppImage
|
||||||
appimage.install.permission=Concediendo permiso de ejecución a {}
|
appimage.install.permission=Concediendo permiso de ejecución a {}
|
||||||
|
appimage.install.extract=Extrayendo el contenido de {}
|
||||||
|
appimage.install.desktop_entry=Creando un atajo en el menú
|
||||||
|
appimage.uninstall.error.remove_folder=No se pudo eliminar el directorio de instalación de la aplicación {}
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
gem.appimage.label=AppImage
|
gem.appimage.label=AppImage
|
||||||
appimage.install.permission=Concedendo permissão de execução para {}
|
appimage.install.permission=Concedendo permissão de execução para {}
|
||||||
|
appimage.install.extract=Extraindo o conteúdo de {}
|
||||||
|
appimage.install.desktop_entry=Criando um atalho no menu
|
||||||
|
appimage.uninstall.error.remove_folder=Não foi possível remover o diretóri ode instalação do aplicativo {}
|
||||||
@@ -394,8 +394,6 @@ class ArchManager(SoftwareManager):
|
|||||||
pre_download_files.append(f)
|
pre_download_files.append(f)
|
||||||
|
|
||||||
if pre_download_files:
|
if pre_download_files:
|
||||||
downloader = self.context.file_downloader.get_default_client_name()
|
|
||||||
|
|
||||||
for f in pre_download_files:
|
for f in pre_download_files:
|
||||||
fdata = f.split('::')
|
fdata = f.split('::')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user