gem selector sketch

This commit is contained in:
Vinicius Moreira
2019-09-11 16:00:02 -03:00
parent 696b6169ac
commit ba5e1da11b
79 changed files with 4117 additions and 112 deletions

View File

@@ -0,0 +1,3 @@
import os
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))

View File

@@ -0,0 +1,2 @@
FLATHUB_URL = 'https://flathub.org'
FLATHUB_API_URL = FLATHUB_URL + '/api/v1'

View File

@@ -0,0 +1,208 @@
from datetime import datetime
from typing import List, Set, Type
from bauh.api.abstract.controller import SearchResult, SoftwareManager, ApplicationContext
from bauh.api.abstract.disk import DiskCacheLoader
from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.abstract.model import PackageHistory, PackageUpdate, SoftwarePackage, PackageSuggestion
from bauh.api.abstract.view import MessageType
from bauh.commons.html import strip_html
from bauh.commons.system import SystemProcess, ProcessHandler
from bauh.gems.flatpak import flatpak, suggestions
from bauh.gems.flatpak.model import FlatpakApplication
from bauh.gems.flatpak.worker import FlatpakAsyncDataLoader, FlatpakUpdateLoader
class FlatpakManager(SoftwareManager):
def __init__(self, context: ApplicationContext):
super(FlatpakManager, self).__init__(context=context)
self.i18n = context.i18n
self.api_cache = context.cache_factory.new()
context.disk_loader_factory.map(FlatpakApplication, self.api_cache)
def get_managed_types(self) -> Set["type"]:
return {FlatpakApplication}
def _map_to_model(self, app_json: dict, installed: bool, disk_loader: DiskCacheLoader) -> FlatpakApplication:
app = FlatpakApplication(arch=app_json.get('arch'),
branch=app_json.get('branch'),
origin=app_json.get('origin'),
runtime=app_json.get('runtime'),
ref=app_json.get('ref'),
commit=app_json.get('commit'),
id=app_json.get('id'),
name=app_json.get('name'),
version=app_json.get('version'),
latest_version=app_json.get('latest_version'))
app.installed = installed
api_data = self.api_cache.get(app_json['id'])
expired_data = api_data and api_data.get('expires_at') and api_data['expires_at'] <= datetime.utcnow()
if not api_data or expired_data:
if not app_json['runtime']:
if disk_loader:
disk_loader.fill(app) # preloading cached disk data
FlatpakAsyncDataLoader(app=app, api_cache=self.api_cache, manager=self, context=self.context).start()
else:
app.fill_cached_data(api_data)
return app
def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1) -> SearchResult:
res = SearchResult([], [], 0)
apps_found = flatpak.search(words)
if apps_found:
already_read = set()
installed_apps = self.read_installed(disk_loader=disk_loader).installed
if installed_apps:
for app_found in apps_found:
for installed_app in installed_apps:
if app_found['id'] == installed_app.id:
res.installed.append(installed_app)
already_read.add(app_found['id'])
if len(apps_found) > len(already_read):
for app_found in apps_found:
if app_found['id'] not in already_read:
res.new.append(self._map_to_model(app_found, False, disk_loader))
res.total = len(res.installed) + len(res.new)
return res
def read_installed(self, disk_loader: DiskCacheLoader, limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None) -> SearchResult:
installed = flatpak.list_installed()
models = []
if installed:
available_updates = flatpak.list_updates_as_str()
for app_json in installed:
model = self._map_to_model(app_json=app_json, installed=True, disk_loader=disk_loader)
model.update = app_json['id'] in available_updates
models.append(model)
return SearchResult(models, None, len(models))
def downgrade(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher) -> bool:
watcher.change_progress(10)
watcher.change_substatus(self.i18n['flatpak.downgrade.commits'])
commits = flatpak.get_app_commits(pkg.ref, pkg.origin)
commit_idx = commits.index(pkg.commit)
# downgrade is not possible if the app current commit in the first one:
if commit_idx == len(commits) - 1:
watcher.show_message(self.i18n['flatpak.downgrade.impossible.title'], self.i18n['flatpak.downgrade.impossible.body'], MessageType.WARNING)
return False
commit = commits[commit_idx + 1]
watcher.change_substatus(self.i18n['flatpak.downgrade.reverting'])
watcher.change_progress(50)
success = ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.downgrade(pkg.ref, commit, root_password), success_phrase='Updates complete.'))
watcher.change_progress(100)
return success
def clean_cache_for(self, pkg: FlatpakApplication):
super(FlatpakManager, self).clean_cache_for(pkg)
self.api_cache.delete(pkg.id)
def update(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher) -> bool:
return ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.update(pkg.ref)))
def uninstall(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher) -> bool:
return ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.uninstall(pkg.ref)))
def get_info(self, app: FlatpakApplication) -> dict:
app_info = flatpak.get_app_info_fields(app.id, app.branch)
app_info['name'] = app.name
app_info['type'] = 'runtime' if app.runtime else 'app'
app_info['description'] = strip_html(app.description)
return app_info
def get_history(self, pkg: FlatpakApplication) -> PackageHistory:
commits = flatpak.get_app_commits_data(pkg.ref, pkg.origin)
status_idx = 0
for idx, data in enumerate(commits):
if data['commit'] == pkg.commit:
status_idx = idx
break
return PackageHistory(pkg=pkg, history=commits, pkg_status_idx=status_idx)
def install(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher) -> bool:
return ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.install(pkg.id, pkg.origin)))
def is_enabled(self):
return flatpak.is_installed()
def requires_root(self, action: str, pkg: FlatpakApplication):
return action == 'downgrade'
def prepare(self):
pass
def list_updates(self) -> List[PackageUpdate]:
updates = []
installed = flatpak.list_installed(extra_fields=False)
if installed:
available_updates = flatpak.list_updates_as_str()
if available_updates:
loaders = None
for app_json in installed:
if app_json['id'] in available_updates:
loader = FlatpakUpdateLoader(app=app_json, http_client=self.context.http_client)
loader.start()
if loaders is None:
loaders = []
loaders.append(loader)
if loaders:
for loader in loaders:
loader.join()
app = loader.app
updates.append(PackageUpdate(pkg_id='{}:{}'.format(app['id'], app['branch']),
pkg_type='flatpak',
version=app.get('version')))
return updates
def list_warnings(self) -> List[str]:
if flatpak.is_installed():
if not flatpak.has_remotes_set():
return [self.i18n['flatpak.notification.no_remotes']]
def list_suggestions(self, limit: int) -> List[PackageSuggestion]:
res = []
sugs = [(i, p) for i, p in suggestions.ALL.items()]
sugs.sort(key=lambda t: t[1].value, reverse=True)
for sug in sugs:
if limit <= 0 or len(res) < limit:
app_json = flatpak.search(sug[0], app_id=True)
if app_json:
res.append(PackageSuggestion(self._map_to_model(app_json[0], False, None), sug[1]))
else:
break
return res

245
bauh/gems/flatpak/flatpak.py Executable file
View File

@@ -0,0 +1,245 @@
import re
import subprocess
from typing import List
from bauh.api.exception import NoInternetException
from bauh.commons.system import new_subprocess, run_cmd, new_root_subprocess
BASE_CMD = 'flatpak'
def app_str_to_json(line: str, version: str, extra_fields: bool = True) -> dict:
app_array = line.split('\t')
if version >= '1.3.0':
app = {'name': app_array[0],
'id': app_array[1],
'version': app_array[2],
'branch': app_array[3]}
elif '1.0' <= version < '1.1':
ref_data = app_array[0].split('/')
app = {'id': ref_data[0],
'arch': ref_data[1],
'name': ref_data[0].split('.')[-1],
'ref': app_array[0],
'options': app_array[1],
'branch': ref_data[2],
'version': None}
elif '1.2' <= version < '1.3':
app = {'id': app_array[1],
'name': app_array[1].strip().split('.')[-1],
'version': app_array[2],
'branch': app_array[3],
'arch': app_array[4],
'origin': app_array[5]}
else:
raise Exception('Unsupported version')
if extra_fields:
extra_fields = get_app_info_fields(app['id'], app['branch'], ['origin', 'arch', 'ref', 'commit'], check_runtime=True)
app.update(extra_fields)
return app
def get_app_info_fields(app_id: str, branch: str, fields: List[str] = [], check_runtime: bool = False):
info = re.findall(r'\w+:\s.+', get_app_info(app_id, branch))
data = {}
fields_to_retrieve = len(fields) + (1 if check_runtime and 'ref' not in fields else 0)
for field in info:
if fields and fields_to_retrieve == 0:
break
field_val = field.split(':')
field_name = field_val[0].lower()
if not fields or field_name in fields or (check_runtime and field_name == 'ref'):
data[field_name] = field_val[1].strip()
if fields:
fields_to_retrieve -= 1
if check_runtime and field_name == 'ref':
data['runtime'] = data['ref'].startswith('runtime/')
return data
def is_installed():
version = get_version()
return False if version is None else True
def get_version():
res = run_cmd('{} --version'.format(BASE_CMD), print_error=False)
return res.split(' ')[1].strip() if res else None
def get_app_info(app_id: str, branch: str):
return run_cmd('{} info {} {}'.format(BASE_CMD, app_id, branch))
def list_installed(extra_fields: bool = True) -> List[dict]:
apps_str = run_cmd('{} list'.format(BASE_CMD))
if apps_str:
version = get_version()
app_lines = apps_str.split('\n')
return [app_str_to_json(line, version, extra_fields=extra_fields) for line in app_lines if line]
return []
def update(app_ref: str):
"""
Updates the app reference
:param app_ref:
:return:
"""
return new_subprocess([BASE_CMD, 'update', '-y', app_ref])
def uninstall(app_ref: str):
"""
Removes the app by its reference
:param app_ref:
:return:
"""
return new_subprocess([BASE_CMD, 'uninstall', app_ref, '-y'])
def list_updates_as_str():
return run_cmd('{} update'.format(BASE_CMD), ignore_return_code=True)
def downgrade(app_ref: str, commit: str, root_password: str) -> subprocess.Popen:
return new_root_subprocess([BASE_CMD, 'update', '--commit={}'.format(commit), app_ref, '-y'], root_password)
def get_app_commits(app_ref: str, origin: str) -> List[str]:
log = run_cmd('{} remote-info --log {} {}'.format(BASE_CMD, origin, app_ref))
if log:
return re.findall(r'Commit+:\s(.+)', log)
else:
raise NoInternetException()
def get_app_commits_data(app_ref: str, origin: str) -> List[dict]:
log = run_cmd('{} remote-info --log {} {}'.format(BASE_CMD, origin, app_ref))
if not log:
raise NoInternetException()
res = re.findall(r'(Commit|Subject|Date):\s(.+)', log)
commits = []
commit = {}
for idx, data in enumerate(res):
commit[data[0].strip().lower()] = data[1].strip()
if (idx + 1) % 3 == 0:
commits.append(commit)
commit = {}
return commits
def search(word: str, app_id: bool = False) -> List[dict]:
cli_version = get_version()
res = run_cmd('{} search {}'.format(BASE_CMD, word))
found = []
split_res = res.split('\n')
if split_res and split_res[0].lower() != 'no matches found':
for info in split_res:
if info:
info_list = info.split('\t')
if cli_version >= '1.3.0':
id_ = info_list[2].strip()
if app_id and id_ != word:
continue
version = info_list[3].strip()
app = {
'name': info_list[0].strip(),
'description': info_list[1].strip(),
'id': id_,
'version': version,
'latest_version': version,
'branch': info_list[4].strip(),
'origin': info_list[5].strip(),
'runtime': False,
'arch': None, # unknown at this moment,
'ref': None # unknown at this moment
}
elif cli_version >= '1.2.0':
id_ = info_list[1].strip()
if app_id and id_ != word:
continue
desc = info_list[0].split('-')
version = info_list[2].strip()
app = {
'name': desc[0].strip(),
'description': desc[1].strip(),
'id': id_,
'version': version,
'latest_version': version,
'branch': info_list[3].strip(),
'origin': info_list[4].strip(),
'runtime': False,
'arch': None, # unknown at this moment,
'ref': None # unknown at this moment
}
else:
id_ = info_list[0].strip()
if app_id and id_ != word:
continue
version = info_list[1].strip()
app = {
'name': '',
'description': info_list[4].strip(),
'id': id_,
'version': version,
'latest_version': version,
'branch': info_list[2].strip(),
'origin': info_list[3].strip(),
'runtime': False,
'arch': None, # unknown at this moment,
'ref': None # unknown at this moment
}
found.append(app)
if app_id and len(found) > 0:
break
return found
def install(app_id: str, origin: str):
return new_subprocess([BASE_CMD, 'install', origin, app_id, '-y'])
def set_default_remotes():
run_cmd('{} remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo'.format(BASE_CMD))
def has_remotes_set() -> bool:
return bool(run_cmd('{} remotes'.format(BASE_CMD)).strip())

View File

@@ -0,0 +1,69 @@
from bauh.api.abstract.model import SoftwarePackage
from bauh.commons import resource
from bauh.gems.flatpak import ROOT_DIR
class FlatpakApplication(SoftwarePackage):
def __init__(self, id: str = None, name: str = None, version: str = None, latest_version: str = None, description: str = None,
branch: str = None, arch: str = None, origin: str = None, runtime: bool = False, ref: str = str, commit: str = str):
super(FlatpakApplication, self).__init__(id=id, name=name, version=version,
latest_version=latest_version, description=description)
self.ref = ref
self.branch = branch
self.arch = arch
self.origin = origin
self.runtime = runtime
self.commit = commit
def is_incomplete(self):
return self.description is None and self.icon_url
def has_history(self):
return self.installed
def has_info(self):
return self.installed
def can_be_downgraded(self):
return self.installed
def get_type(self):
return 'flatpak'
def get_default_icon_path(self):
return resource.get_path('img/flathub.svg', ROOT_DIR)
def get_type_icon_path(self):
return self.get_default_icon_path()
def is_application(self):
return not self.runtime
def get_disk_cache_path(self):
return super(FlatpakApplication, self).get_disk_cache_path() + '/installed/' + self.id
def get_data_to_cache(self):
return {
'description': self.description,
'icon_url': self.icon_url,
'latest_version': self.latest_version,
'version': self.version,
'name': self.name,
'categories': self.categories
}
def fill_cached_data(self, data: dict):
for attr in self.get_data_to_cache().keys():
if data.get(attr) and not getattr(self, attr):
setattr(self, attr, data[attr])
def get_command(self) -> str:
return "flatpak run {}".format(self.id)
def can_be_run(self) -> bool:
return self.installed and not self.runtime
def get_publisher(self):
return 'flathub'

View File

@@ -0,0 +1,179 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="47.919052"
height="48.110374"
viewBox="0 0 12.678582 12.729203"
version="1.1"
id="svg3871"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="flathub.svg">
<defs
id="defs3865" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35"
inkscape:cx="8.4698372"
inkscape:cy="118.04149"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
showborder="false"
inkscape:window-width="1366"
inkscape:window-height="703"
inkscape:window-x="0"
inkscape:window-y="260"
inkscape:window-maximized="1"
fit-margin-bottom="-0.2"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
units="px" />
<metadata
id="metadata3868">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-12.122116,-167.33593)">
<g
transform="matrix(0.08875425,0,0,0.11490723,-57.513376,72.576168)"
id="g10674"
style="stroke-width:0.77710575">
<path
sodipodi:nodetypes="ccccccccccc"
inkscape:connector-curvature="0"
id="path10642"
d="m 856.01397,830.22631 -66.76365,43.44891 v 34.93973 l 33.38182,21.72315 33.37926,-21.72315 v -0.005 l 0.003,0.003 33.38183,21.72574 33.37925,-21.72315 v -34.93979 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:9.3252697;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<g
style="stroke-width:0.58728963"
transform="matrix(0.50221552,0,0,0.50636711,429.63205,432.90461)"
id="g10652">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect10644"
width="79.110413"
height="79.110413"
x="1289.9076"
y="279.42584"
rx="0"
ry="0"
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 915.46811,824.92983 -5e-5,68.99997 -66.46803,42.90055 5e-5,-68.99997 z"
id="path10648"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path10650"
d="m 783.00003,824.92983 5e-5,68.99997 66.46803,42.90055 -5e-5,-68.99997 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<rect
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)"
ry="0"
rx="0"
y="215.8064"
x="1226.2882"
height="79.110413"
width="79.110413"
id="rect10646"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
<g
style="stroke-width:0.58728963"
transform="matrix(0.50221552,0,0,0.50636711,463.01368,454.62849)"
id="g10662">
<rect
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)"
ry="0"
rx="0"
y="279.42584"
x="1289.9076"
height="79.110413"
width="79.110413"
id="rect10654"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect10656"
width="79.110413"
height="79.110413"
x="1226.2882"
y="215.8064"
rx="0"
ry="0"
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)" />
<path
inkscape:connector-curvature="0"
id="path10658"
d="m 915.46811,824.92983 -5e-5,68.99997 -66.46803,42.90055 5e-5,-68.99997 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 783.00003,826.54445 5e-5,68.99997 66.46803,42.90055 -5e-5,-68.99997 z"
id="path10660"
inkscape:connector-curvature="0" />
</g>
<g
style="stroke-width:0.58728963"
id="g10672"
transform="matrix(0.50221552,0,0,0.50636711,396.25042,454.62849)">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect10664"
width="79.110413"
height="79.110413"
x="1289.9076"
y="279.42584"
rx="0"
ry="0"
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)" />
<rect
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)"
ry="0"
rx="0"
y="215.8064"
x="1226.2882"
height="79.110413"
width="79.110413"
id="rect10666"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 915.46811,824.92983 -5e-5,68.99997 -66.46803,42.90055 5e-5,-68.99997 z"
id="path10668"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path10670"
d="m 783.00003,824.92983 5e-5,68.99997 66.46803,42.90055 -5e-5,-68.99997 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -0,0 +1,5 @@
flatpak.notification.no_remotes=No Flatpak remotes set. It will not be possible to search for Flatpak apps.
flatpak.downgrade.impossible.title=Error
flatpak.downgrade.impossible.body=Impossible to downgrade: the app is in its first version
flatpak.downgrade.commits=Reading package commits
flatpak.downgrade.reverting=Reverting version

View File

@@ -0,0 +1,27 @@
flatpak.info.arch=arquitectura
flatpak.info.branch=rama
flatpak.info.collection=colección
flatpak.info.commit=commit
flatpak.info.date=fecha
flatpak.info.description=descripción
flatpak.info.id=id
flatpak.info.installation=instalación
flatpak.info.installed=instalado
flatpak.info.license=licencia
flatpak.info.name=nombre
flatpak.info.origin=origen
flatpak.info.parent=padre
flatpak.info.ref=ref
flatpak.info.runtime=runtime
flatpak.info.sdk=sdk
flatpak.info.subject=tema
flatpak.info.type=tipo
flatpak.info.version=versión
flatpak.notification.no_remotes=No hay repositorios (remotes) Flatpak configurados. No será posible buscar aplicativos Flatpak.
flatpak.downgrade.impossible.title=Error
flatpak.downgrade.impossible.body=Imposible revertir la versión: el aplicativo está en su primera versión
flatpak.history.date=fecha
flatpak.history.commit=commit
flatpak.history.subject=tema
flatpak.downgrade.commits=Leyendo commits del paquete
flatpak.downgrade.reverting=Revirtiendo la versión

View File

@@ -0,0 +1,27 @@
flatpak.info.arch=arquitetura
flatpak.info.branch=ramo
flatpak.info.collection=coleção
flatpak.info.commit=commit
flatpak.info.date=data
flatpak.info.description=descrição
flatpak.info.id=id
flatpak.info.installation=instalação
flatpak.info.installed=instalado
flatpak.info.license=licença
flatpak.info.name=nome
flatpak.info.origin=origem
flatpak.info.parent=pai
flatpak.info.ref=ref
flatpak.info.runtime=runtime
flatpak.info.sdk=sdk
flatpak.info.subject=assunto
flatpak.info.type=tipo
flatpak.info.version=versão
flatpak.notification.no_remotes=Não há repositórios (remotes) Flatpak configurados. Não será possível buscar aplicativos Flatpak.
flatpak.downgrade.impossible.title=Erro
flatpak.downgrade.impossible.body=Impossível reverter a versão: o aplicativo está na sua primeira versão
flatpak.history.date=data
flatpak.history.commit=commit
flatpak.history.subject=assunto
flatpak.downgrade.commits=Lendo os commits do pacote
flatpak.downgrade.reverting=Revertendo a versão

View File

@@ -0,0 +1,15 @@
from bauh.api.abstract.model import SuggestionPriority
ALL = {
'com.spotify.Client': SuggestionPriority.TOP,
'com.skype.Client': SuggestionPriority.HIGH,
'com.dropbox.Client': SuggestionPriority.MEDIUM,
'us.zoom.Zoom': SuggestionPriority.MEDIUM,
'org.telegram.desktop': SuggestionPriority.MEDIUM,
'com.visualstudio.code': SuggestionPriority.LOW,
'org.inkscape.Inkscape': SuggestionPriority.LOW,
'org.libretro.RetroArch': SuggestionPriority.LOW,
'org.kde.kdenlive': SuggestionPriority.LOW,
'org.videolan.VLC': SuggestionPriority.LOW
}

View File

@@ -0,0 +1,96 @@
import traceback
from threading import Thread
from bauh.api.abstract.cache import MemoryCache
from bauh.api.abstract.context import ApplicationContext
from bauh.api.abstract.controller import SoftwareManager
from bauh.api.abstract.model import PackageStatus
from bauh.api.http import HttpClient
from bauh.gems.flatpak import flatpak
from bauh.gems.flatpak.constants import FLATHUB_API_URL, FLATHUB_URL
from bauh.gems.flatpak.model import FlatpakApplication
class FlatpakAsyncDataLoader(Thread):
def __init__(self, app: FlatpakApplication, manager: SoftwareManager, context: ApplicationContext, api_cache: MemoryCache):
super(FlatpakAsyncDataLoader, self).__init__(daemon=True)
self.app = app
self.manager = manager
self.http_client = context.http_client
self.api_cache = api_cache
self.persist = False
self.logger = context.logger
def run(self):
if self.app:
self.app.status = PackageStatus.LOADING_DATA
try:
res = self.http_client.get('{}/apps/{}'.format(FLATHUB_API_URL, self.app.id))
if res.status_code == 200 and res.text:
data = res.json()
if not self.app.version:
self.app.version = data.get('version')
if not self.app.name:
self.app.name = data.get('name')
self.app.description = data.get('description', data.get('summary', None))
self.app.icon_url = data.get('iconMobileUrl', None)
self.app.latest_version = data.get('currentReleaseVersion', self.app.version)
if not self.app.version and self.app.latest_version:
self.app.version = self.app.latest_version
if not self.app.installed and self.app.latest_version:
self.app.version = self.app.latest_version
if self.app.icon_url and self.app.icon_url.startswith('/'):
self.app.icon_url = FLATHUB_URL + self.app.icon_url
if data.get('categories'):
self.app.categories = [c['name'] for c in data['categories']]
loaded_data = self.app.get_data_to_cache()
self.api_cache.add(self.app.id, loaded_data)
self.persist = self.app.supports_disk_cache()
else:
self.logger.warning("Could not retrieve app data for id '{}'. Server response: {}. Body: {}".format(self.app.id, res.status_code, res.content.decode()))
except:
self.logger.error("Could not retrieve app data for id '{}'".format(self.app.id))
traceback.print_exc()
self.app.status = PackageStatus.READY
if self.persist:
self.manager.cache_to_disk(pkg=self.app, icon_bytes=None, only_icon=False)
class FlatpakUpdateLoader(Thread):
def __init__(self, app: dict, http_client: HttpClient):
super(FlatpakUpdateLoader, self).__init__(daemon=True)
self.app = app
self.http_client = http_client
def run(self):
if self.app.get('ref') is None:
self.app.update(flatpak.get_app_info_fields(self.app['id'], self.app['branch'], fields=['ref'], check_runtime=True))
else:
self.app['runtime'] = self.app['ref'].startswith('runtime/')
if not self.app['runtime']:
try:
data = self.http_client.get_json('{}/apps/{}'.format(FLATHUB_API_URL, self.app['id']))
if data and data.get('currentReleaseVersion'):
self.app['version'] = data['currentReleaseVersion']
except:
traceback.print_exc()