source: lliurex-store/trunk/fuentes/python3-lliurex-store.install/usr/share/lliurexstore/plugins/appImageManager.py

Last change on this file was 8703, checked in by Juanma, 5 months ago

sync with git

File size: 23.1 KB
Line 
1#The name of the main class must match the file name in lowercase
2import re
3import urllib
4from urllib.request import Request
5from urllib.request import urlretrieve
6import shutil
7import json
8import os
9import sys
10import threading
11import queue
12import time
13import random
14import gi
15from gi.repository import Gio
16gi.require_version('AppStreamGlib', '1.0')
17from gi.repository import AppStreamGlib as appstream
18from bs4 import BeautifulSoup
19# -*- coding: utf-8 -*-
20#from subprocess import call
21
22class appimagemanager:
23        def __init__(self):
24                self.dbg=False
25                self.progress=0
26                self.partial_progress=0
27                self.plugin_actions={'install':'appimage','remove':'appimage','pkginfo':'appimage','load':'appimage'}
28                self.result={}
29                self.result['data']={}
30                self.result['status']={}
31                self.cache_dir=os.getenv("HOME")+"/.cache/lliurex-store"
32                self.icons_dir=self.cache_dir+"/icons"
33                self.cache_xmls=self.cache_dir+"/xmls/appimage"
34                self.appimage_dir=os.getenv("HOME")+"/Applications"
35                #Prevent appimage desktop integration
36                if not os.path.isfile("%s/.local/share/appimagekit/no_desktopintegration"%os.environ['HOME']):
37                        if not os.path.isdir("%s/.local/share/appimagekit/"%os.environ['HOME']):
38                                os.makedirs("%s/.local/share/appimagekit/"%os.environ['HOME'])
39                        try:
40                                f=open("%s/.local/share/appimagekit/no_desktopintegration"%os.environ['HOME'],'w')
41                                f.close()
42                        except Exception as e:
43                                self._debug("Couldn't create %s"%"%s/.local/share/appimagekit/no_desktopintegration"%os.environ['HOME'])
44                                self._debug("Reason: 5s"%e)
45                #To get the description of an app we must go to a specific url defined in url_info.
46                #$(appname) we'll be replaced with the appname so the url matches the right one.
47                #If other site has other url naming convention it'll be mandatory to define it with the appropiate replacements
48                self.repos={'appimagehub':{'type':'json','url':'https://appimage.github.io/feed.json','url_info':''}}
49                #Appimges not stored in a repo must be listed in this file, providing the download url and the info url (if there's any)
50                self.external_appimages="/usr/share/lliurex-store/files/external_appimages.json"
51                self.locale=['ca_ES@valencia','ca@valencia','qcv','ca','ca_ES','es_ES','es','en_US','en_GB','en','C']
52                self.disabled=False
53                self.icon_cache_enabled=True
54                self.image_cache_enabled=True
55                self.cache_last_update=self.cache_xmls+'/.appimage.lu'
56                self.apps_for_store=queue.Queue()
57        #def __init__
58
59        def set_debug(self,dbg=True):
60                self.dbg=dbg
61                #self._debug ("Debug enabled")
62        #def set_debug
63
64        def _debug(self,msg=''):
65                if self.dbg:
66                        print ('DEBUG appimage: %s'%msg)
67        #def debug
68
69        def register(self):
70                return(self.plugin_actions)
71        #def register
72
73        def enable(self,state=False):
74                self.disable=state
75        #def enable
76
77        def execute_action(self,action,applist=None,store=None):
78                if store:
79                        self.store=store
80                else:
81                        self.store=appstream.Store()
82                self.appimage_store=appstream.Store()
83                self.progress=0
84                self.result['status']={'status':-1,'msg':''}
85                self.result['data']=[]
86                self.threads=[]
87                dataList=[]
88                if self.disabled:
89                        self._set_status(9)
90                        self.result['data']=self.store
91                else:
92                        self._chk_installDir()
93                        if action=='load':
94                                self._load_appimage_store(self.store)
95                                #self._debug("Ending threads...")
96                                while not self.apps_for_store.empty():
97                                        app=self.apps_for_store.get()
98                                        self.store.add_app(app)
99                                self.result['data']=self.store
100                        else:
101                                for app_info in applist:
102                                        self.partial_progress=0
103                                        if action=='install':
104                                                dataList.append(self._install_appimage(app_info))
105                                        if action=='remove':
106                                                dataList.append(self._remove_appimage(app_info))
107                                        if action=='pkginfo':
108                                                dataList.append(self._get_info(app_info))
109                                        self.progress+=int(self.partial_progress/len(applist))-1
110                                self.result['data']=list(dataList)
111                self.progress=100
112                return(self.result)
113        #def execute_action
114
115        def _set_status(self,status,msg=''):
116                self.result['status']={'status':status,'msg':msg}
117        #def _set_status
118
119        def _callback(self,partial_size=0,total_size=0):
120                limit=99
121                if partial_size!=0 and total_size!=0:
122                        inc=round(partial_size/total_size,2)*100
123                        self.progress=inc
124                else:
125                        inc=1
126                        margin=limit-self.progress
127                        inc=round(margin/limit,3)
128                        self.progress=(self.progress+inc)
129                if (self.progress>limit):
130                        self.progress=limit
131        #def _callback
132
133        def _chk_installDir(self):
134                msg_status=True
135                if not os.path.isdir(self.appimage_dir):
136                        try:
137                                os.makedirs(self.appimage_dir)
138                        except:
139                                msg_status=False
140                return msg_status                               
141        #def _chk_installDir
142
143        def _install_appimage(self,app_info):
144                app_info=self._get_info(app_info,force=True)
145                #self._debug("Installing %s"%app_info)
146                if app_info['state']=='installed':
147                        self._set_status(4)
148                else:
149                        if 'appimage' in app_info['channel_releases'].keys():
150                                appimage_url=app_info['channel_releases']['appimage'][0]
151                        else:
152                                #self._debug("No url in: %s"%app_info['channel_releases'])
153                                pass
154                        #self._debug("Downloading "+appimage_url)
155                        dest_path=self.appimage_dir+'/'+app_info['package']
156                        if appimage_url:
157                                try:
158                                        req=Request(appimage_url, headers={'User-Agent':'Mozilla/5.0'})
159                                        with urllib.request.urlopen(req) as response, open(dest_path, 'wb') as out_file:
160                                                bf=16*1024
161                                                acumbf=0
162                                                app_size=int(response.info()['Content-Length'])
163                                                while True:
164                                                        if acumbf>=app_size:
165                                                            break
166                                                        shutil.copyfileobj(response, out_file,bf)
167                                                        acumbf=acumbf+bf
168                                                        self._callback(acumbf,app_size)
169                                        st = os.stat(dest_path)
170                                        os.chmod(dest_path, st.st_mode | 0o755)
171                                        self._set_status(0)
172                                except Exception as e:
173                                        print(e)
174                                        self._set_status(5)
175                        else:
176                                self._set_status(12)
177                return app_info
178        #def _install_appimage
179
180        def _remove_appimage(self,app_info):
181                #self._debug("Removing "+app_info['package'])
182                if os.path.isfile(self.appimage_dir+'/'+app_info['package']):
183                        try:
184                                call([self.appimage_dir+"/"+app_info['package'], "--remove-appimage-desktop-integration"])
185                        except:
186                                pass
187                        try:
188                                os.remove(self.appimage_dir+"/"+app_info['package'])
189                                self._set_status(0)
190                        except:
191                                self._set_status(6)
192                return(app_info)
193        #def _remove_appimage
194
195        def _load_appimage_store(self,store):
196                #Look if cache is up-to-date
197                sw_update_cache=True
198                if os.path.isfile(self.cache_last_update):
199                        epoch_time=time.time()
200                        fcache=open(self.cache_last_update,'r')
201                        fcache_update=fcache.read()
202                        if not fcache_update:
203                                fcache_update=0
204                        if int(epoch_time)-int(fcache_update)<86400:
205                                if os.listdir(os.path.dirname(self.cache_xmls)):
206                                        #self._debug("Loading appimage from cache")
207                                        sw_update_cache=False
208                if sw_update_cache:
209                        self._get_bundles_catalogue()
210                        self._get_external_catalogue()
211                        fcache=open(self.cache_last_update,'w')
212                        fcache.write(str(int(time.time())))
213                if os.path.exists(self.cache_xmls):
214                        #self._debug("Loading appimage catalog")
215                        store=self._generic_file_load(self.cache_xmls,store)
216                return(store)
217        #def load_bundles_catalog(self)
218       
219        def _generic_file_load(self,target_path,store):
220                icon_path='/usr/share/icons/hicolor/128x128'
221                if not os.path.isdir(target_path):
222                        os.makedirs(target_path)
223                files=os.listdir(target_path)
224                for target_file in os.listdir(target_path):
225                        if target_file.endswith('.xml'):
226                                store_path=Gio.File.new_for_path(target_path+'/'+target_file)
227                                #self._debug("Adding file "+target_path+'/'+target_file)
228                                try:
229                                        store.from_file(store_path,icon_path,None)
230                                except Exception as e:
231                                        #self._debug("Couldn't add file "+target_file+" to store")
232                                        #self._debug("Reason: "+str(e))
233                                        pass
234                return(store)
235        #def _generic_file_load
236
237        def _get_bundles_catalogue(self):
238                applist=[]
239                appdict={}
240                all_apps=[]
241                outdir=self.cache_xmls
242                #Load repos
243                for repo_name,repo_info in self.repos.items():
244                        if not os.path.isdir(self.cache_xmls):
245                                try:
246                                        os.makedirs(self.cache_xmls)
247                                except:
248                                        #self._debug("appImage catalogue could not be fetched: Permission denied")
249                                        pass
250                        #self._debug("Fetching repo %s"%repo_info['url'])
251                        if repo_info['type']=='json':
252                                applist=self._process_appimage_json(self._fetch_repo(repo_info['url']),repo_name)
253
254                        #self._debug("Fetched repo "+repo_info['url'])
255                        self._th_generate_xml_catalog(applist,outdir,repo_info['url_info'],repo_info['url'],repo_name)
256                        all_apps.extend(applist)
257                return True
258
259        def _get_external_catalogue(self):
260                applist=[]
261                all_apps=[]
262                outdir=self.cache_xmls
263                #Load external apps
264                for app_name,app_info in self._get_external_appimages().items():
265                        if os.path.isdir(self.cache_xmls):
266                                appinfo=self._init_appinfo()
267                                if 'name' in app_info.keys():
268                                        appinfo['name']=app_info['name']
269                                else:
270                                        appinfo['name']=app_info['url'].split('/')[-1]
271                                appinfo['package']=app_info['url'].split('/')[-1]
272                                if 'homepage' in app_info.keys():
273                                        appinfo['homepage']=app_info['homepage']
274                                else:
275                                        appinfo['homepage']='/'.join(app_info['url'].split('/')[0:-1])
276                                appinfo['installerUrl']=app_info['url']
277                                if 'description' in app_info.keys():
278                                        if type(app_info['description'])==type({}):
279                                                for lang in app_info['description']:
280                                                        appinfo['description'].update({lang:app_info['description'][lang]})
281                                        else:
282                                                appinfo['description'].update({"C":appimage['description']})
283                                if 'categories' in app_info.keys():
284                                        appinfo['categories']=app_info['categories']
285                                if 'keywords' in app_info.keys():
286                                        appinfo['keywords']=app_info['keywords']
287                                if 'version' in app_info.keys():
288                                        appinfo['reywords']=app_info['keywords']
289                                #self._debug("Fetching external appimage %s"%app_info['url'])
290                                appinfo['bundle']='appimage'
291                                #self._debug("External:\n%s\n-------"%appinfo)
292                                applist.append(appinfo)
293                        else:
294                                #self._debug("External appImage could not be fetched: Permission denied")
295                                pass
296                self._th_generate_xml_catalog(applist,outdir,app_info['url_info'],app_info['url'],app_name)
297                #self._debug("Fetched appimage "+app_info['url'])
298                all_apps.extend(applist)
299                #self._debug("Removing old entries...")
300#               self._clean_bundle_catalogue(all_apps,outdir)
301                return(True)
302        #def _get_bundles_catalogue
303       
304        def _fetch_repo(self,repo):
305                content=''
306                req=Request(repo, headers={'User-Agent':'Mozilla/5.0'})
307                with urllib.request.urlopen(req) as f:
308                        content=(f.read().decode('utf-8'))
309               
310                return(content)
311        #def _fetch_repo
312       
313        def _get_external_appimages(self):
314                external_appimages={}
315                if os.path.isfile(self.external_appimages):
316                        try:
317                                with open(self.external_appimages) as appimages:
318                                        external_appimages=json.load(appimages)
319                        except:
320                                #self._debug("Can't load %s"%self.external_appimages)
321                                pass
322                return external_appimages
323        #def _get_external_appimages
324       
325        def _process_appimage_json(self,data,repo_name):
326                applist=[]
327                json_data=json.loads(data)
328                if 'items' in json_data.keys():
329                        for appimage in json_data['items']:
330                                appinfo=self._th_process_appimage(appimage)
331                                if appinfo:
332                                        applist.append(appinfo)
333                return (applist)
334        #_process_appimage_json
335
336        def _th_process_appimage(self,appimage):
337                appinfo=None
338                releases=[]
339                if 'links' in appimage.keys():
340                        if appimage['links']:
341                                appinfo=self.load_json_appinfo(appimage)
342                return(appinfo)
343        #def _th_process_appimage
344
345        def load_json_appinfo(self,appimage):
346                #self._debug(appimage)
347                appinfo=self._init_appinfo()
348                appinfo['name']=appimage['name']
349                appinfo['package']=appimage['name']
350                if 'license' in appimage.keys():
351                        appinfo['license']=appimage['license']
352                appinfo['summary']=''
353                if 'description' in appimage.keys():
354                        if type(appimage['description'])==type({}):
355                                for lang in appinfo['description'].keys():
356                                        appinfo['description'].update({lang:appimage['description'][lang]})
357                        else:
358                                appinfo['description']={"C":appimage['description']}
359                if 'categories' in appimage.keys():
360                        appinfo['categories']=appimage['categories']
361                if 'icon' in appimage.keys():
362                        appinfo['icon']=appimage['icon']
363                if 'icons' in appimage.keys():
364                        #self._debug("Loading icon %s"%appimage['icons'])
365                        if appimage['icons']:
366                                #self._debug("Loading icon %s"%appimage['icons'][0])
367                                appinfo['icon']=appimage['icons'][0]
368                if 'screenshots' in appimage.keys():
369                        appinfo['thumbnails']=appimage['screenshots']
370                if 'links' in appimage.keys():
371                        if appimage['links']:
372                                for link in appimage['links']:
373                                        if 'url' in link.keys() and link['type']=='Download':
374                                                appinfo['installerUrl']=link['url']
375                if 'authors' in appimage.keys():
376                        if appimage['authors']:
377                                for author in appimage['authors']:
378                                        if 'url' in author.keys():
379                                                #self._debug("Author: %s"%author['url'])
380                                                appinfo['homepage']=author['url']
381                else:
382                        appinfo['homepage']='/'.join(appinfo['installerUrl'].split('/')[0:-1])
383                appinfo['bundle']=['appimage']
384                return appinfo
385        #def load_json_appinfo
386
387        def _th_generate_xml_catalog(self,applist,outdir,info_url,repo,repo_name):
388                maxconnections = 2
389                threads=[]
390                semaphore = threading.BoundedSemaphore(value=maxconnections)
391                random_applist = list(applist)
392                random.shuffle(random_applist)
393                for app in applist:
394                        th=threading.Thread(target=self._th_write_xml, args = (app,outdir,info_url,repo,repo_name,semaphore))
395                        threads.append(th)
396                        th.start()
397                for thread in threads:
398                        thread.join()
399        #def _th_generate_xml_catalog
400
401        def     _th_write_xml(self,appinfo,outdir,info_url,repo,repo_name,semaphore):
402                semaphore.acquire()
403                self._add_appimage(appinfo)
404                semaphore.release()
405        #def _th_write_xml
406
407        def _add_appimage(self,appinfo):
408                #Search in local store for the app
409                sw_new=True
410                app=appstream.App()
411                app_orig=self.store.get_app_by_pkgname(appinfo['name'].lower())
412                if not app_orig:
413                        app_orig=self.store.get_app_by_id(appinfo['name'].lower()+".desktop")
414                if app_orig:
415                        #self._debug("Extending app %s"%appinfo['package'])
416                        if appinfo['icon']:
417                                #self._debug("Icon: %s"%appinfo['icon'])
418                                app=self._copy_app_from_appstream(app_orig,app,copy_icon=False)
419                        else:
420                                app=self._copy_app_from_appstream(app_orig,app,copy_icon=True)
421                        sw_new=False
422                else:
423                        #self._debug("Generating new %s"%appinfo['package'])
424                        pass
425                if appinfo['name'].lower().endswith('.appimage'):
426                        app.set_id("appimagehub.%s"%appinfo['name'].lower())
427                        app.set_name("C",appinfo['name'])
428                else:
429                        app.set_id("appimagehub.%s"%appinfo['name'].lower()+'.appimage')
430                        app.set_name("C",appinfo['name']+".appimage")
431                if appinfo['package'].lower().endswith('.appimage'):
432                        app.add_pkgname(appinfo['package'].lower())
433                else:
434                        app.add_pkgname(appinfo['package'].lower()+".appimage")
435                app.set_id_kind=appstream.IdKind.DESKTOP
436
437                if appinfo['license']:
438                        app.set_project_license(appinfo['license'])
439                bundle=appstream.Bundle()
440                bundle.set_kind(bundle.kind_from_string('APPIMAGE'))
441                if appinfo['package'].endswith('.appimage'):
442                        bundle.set_id(appinfo['package'])
443                else:
444                        bundle.set_id(appinfo['package']+'.appimage')
445                app.add_bundle(bundle)
446                if 'keywords' in appinfo.keys():
447                        for keyword in appinfo['keywords']:
448                                app.add_keyword("C",keyword)
449                        if 'appimage' not in appinfo['keywords']:
450                                app.add_keyword("C","appimage")
451                else:
452                        app.add_keyword("C","appimage")
453                app.add_url(appstream.UrlKind.UNKNOWN,appinfo['installerUrl'])
454                app.add_url(appstream.UrlKind.HOMEPAGE,appinfo['homepage'])
455                if sw_new:
456                        app.add_keyword("C",appinfo['package'])
457                        if not appinfo['name'].endswith('.appimage'):
458                                app.set_name("C",appinfo['name']+".appimage")
459                        desc_header="This is an AppImage bundle of app %s. It hasn't been tested by our developers and comes from a 3rd party dev team. Please use it carefully."%appinfo['name']
460                        if appinfo['description']:
461                                for lang,desc in appinfo['description'].items():
462                                        desc=desc.replace('&','&amp;')
463                                        description="<p>%s</p><p>%s</p>"%(desc_header,desc)
464                                        summary=' '.join(list(desc.split(' ')[:10]))
465                                        app.set_description(lang,description)
466                                        app.set_comment(lang,summary)
467                        else:
468                                description="<p>%s</p>"%(desc_header)
469                                summary=' '.join(list(desc_header.split(' ')[:8]))
470                                app.set_description("C",description)
471                                app.set_comment("C",summary)
472
473                        if 'categories' in appinfo.keys():
474                                for category in appinfo['categories']:
475                                        app.add_category(category)
476                                if 'appimage' not in appinfo['categories']:
477                                        app.add_category("appimage")
478                        else:
479                                app.add_category("appimage")
480                if appinfo['icon']:
481                        icon=appstream.Icon()
482                        if self.icon_cache_enabled:
483                                icon.set_kind(appstream.IconKind.LOCAL)
484                                icon_fn=self._download_file(appinfo['icon'],appinfo['name'],self.icons_dir)
485                                icon.set_filename(icon_fn)
486                        else:
487                                icon.set_kind(appstream.IconKind.REMOTE)
488                                icon.set_name(pkg.get_icon())
489                                icon.set_url(pkg.get_icon())
490                        app.add_icon(icon)
491                if appinfo['thumbnails']:
492                        screenshot=appstream.Screenshot()
493                        img=appstream.Image()
494                        if not appinfo['thumbnails'][0].startswith('http'):
495                                        appinfo['screenshot']=appinfo['thumbnails'][0]
496                                        appinfo['screenshot']="https://appimage.github.io/database/%s"%appinfo['screenshot']
497                        img.set_kind(appstream.ImageKind.SOURCE)
498                        img.set_url(appinfo['screenshot'])
499                        screenshot.add_image(img)
500                        app.add_screenshot(screenshot)
501                #Adds the app to the store
502                self.apps_for_store.put(app)
503                if not os.path.isfile(self.cache_xmls+'/'+app.get_id_filename()):
504                        xml_path='%s/%s.xml'%(self.cache_xmls,app.get_id_filename())
505                        gioFile=Gio.File.new_for_path(xml_path)
506                        app.to_file(gioFile)
507                        #Fix some things in app_file...
508                        xml_file=open(xml_path,'r',encoding='utf-8')
509                        xml_data=xml_file.readlines()
510                        xml_file.close()
511                        #self._debug("fixing %s"%xml_path)
512                        try:
513                                xml_data[0]=xml_data[0]+"<components origin=\"%s\">\n"%app.get_origin()
514                                xml_data[-1]=xml_data[-1]+"\n"+"</components>"
515                        except:
516                                pass
517                        xml_file=open(xml_path,'w')
518                        xml_file.writelines(xml_data)
519                        xml_file.close()
520        #def _add_appimage
521
522        def _copy_app_from_appstream(self,app_orig,app,copy_icon=True):
523                desc_header="This is an AppImage bundle of app %s. It hasn't been tested by our developers and comes from a 3rd party dev team. Please use it carefully."%app_orig.get_pkgnames()[0]
524                app.set_id("appimage."+app_orig.get_id())
525                for category in app_orig.get_categories():
526                        app.add_category(category)
527                app.add_category("appimage")
528                for screenshot in app_orig.get_screenshots():
529                        app.add_screenshot(screenshot)
530                if copy_icon:
531                        for icon in app_orig.get_icons():
532                                app.add_icon(icon)
533                for localeItem in self.locale:
534                        if app_orig.get_name(localeItem):
535                                app.set_name(localeItem,app_orig.get_name(localeItem)+".appimage")
536                        if app_orig.get_description(localeItem):
537                                app.set_description(localeItem,"<p>%s</p><p>%s</p>"%(desc_header,app_orig.get_description(localeItem)))
538                        if app_orig.get_comment(localeItem):
539                                app.set_comment(localeItem,app_orig.get_comment(localeItem))
540                app.set_origin(app_orig.get_origin())
541                return app
542        #def _copy_app_from_appstream
543
544        def _clean_bundle_catalogue(self,applist,outdir):
545                xml_files_list=[]
546                applist=[item.lower() for item in applist]
547                for xml_file in os.listdir(outdir):
548                        if xml_file.endswith('.xml'):
549                                xml_files_list.append(xml_file.lower().replace('.xml','appimage'))
550       
551                if xml_files_list:
552                        xml_discard_list=list(set(xml_files_list).difference(applist))
553                        for discarded_file in xml_discard_list:
554                                os.remove(outdir+'/'+discarded_file.replace('appimage','.xml'))
555        #def _clean_bunlde_catalogue
556
557        def _download_file(self,url,app_name,dest_dir):
558                target_file=dest_dir+'/'+app_name+".png"
559                if not url.startswith('http'):
560                        url="https://appimage.github.io/database/%s"%url
561                if not os.path.isfile(target_file):
562                        if not os.path.isfile(target_file):
563                                #self._debug("Downloading %s to %s"%(url,target_file))
564                                try:
565                                        with urllib.request.urlopen(url) as response, open(target_file, 'wb') as out_file:
566                                                bf=16*1024
567                                                acumbf=0
568                                                file_size=int(response.info()['Content-Length'])
569                                                while True:
570                                                        if acumbf>=file_size:
571                                                            break
572                                                        shutil.copyfileobj(response, out_file,bf)
573                                                        acumbf=acumbf+bf
574                                        st = os.stat(target_file)
575                                except Exception as e:
576                                        #self._debug("Unable to download %s"%url)
577                                        #self._debug("Reason: %s"%e)
578                                        target_file=''
579                return(target_file)
580        #def _download_file
581       
582        def _chk_bundle_dir(self,outdir):
583                msg_status=True
584                if not os.path.isdir(outdir):
585                        try:
586                                os.makedirs(outdir)
587                        except Exception as e:
588                                msg_status=False
589                                print(e)
590                return(os.access(outdir,os.W_OK|os.R_OK|os.X_OK|os.F_OK))
591        #def _chk_bundle_dir
592       
593        def _init_appinfo(self):
594                appInfo={'appstream_id':'',\
595                'id':'',\
596                'name':'',\
597                'version':'',\
598                'channel_releases':{},\
599                'component':'',\
600                'package':'',\
601                'license':'',\
602                'summary':'',\
603                'description':{},\
604                'categories':[],\
605                'icon':'',\
606                'screenshot':'',\
607                'thumbnails':[],\
608                'video':'',\
609                'homepage':'',\
610                'installerUrl':'',\
611                'state':'',\
612                'depends':'',\
613                'kudos':'',\
614                'suggests':'',\
615                'extraInfo':'',\
616                'size':'',\
617                'bundle':'',\
618                'updatable':'',\
619                }
620                return(appInfo)
621        #def _init_appinfo
622       
623        def _get_info(self,app_info,force=False):
624                #self._debug("Searching for %s in %s"%(app_info['package'],self.appimage_dir))
625                app_info['state']='available'
626                if os.path.isfile(self.appimage_dir+'/'+app_info['package']):
627                        app_info['state']='installed'
628                if not app_info['size'] or force:
629                        if app_info['installerUrl']:
630                                #self._debug("installer: %s"%app_info['installerUrl'])
631                                app_info['channel_releases']={'appimage':[]}
632                                app_info['channel_releases']['appimage']=self._get_releases(app_info)
633                        #Get size
634                        app_info['size']="0"
635                        app_info['version']='unknown'
636                        if 'appimage' in app_info['channel_releases'].keys():
637                                if len(app_info['channel_releases']['appimage'])>0:
638                                        if app_info['channel_releases']['appimage'][0]:
639                                                appimage_url=app_info['channel_releases']['appimage'][0]
640                                                dest_path=self.appimage_dir+'/'+app_info['package']
641                                                if appimage_url:
642                                                        try:
643                                                                with urllib.request.urlopen(appimage_url) as response:
644                                                                        app_info['size']=str((response.info()['Content-Length']))
645                                                        except:
646                                                                app_info['size']="0"
647                                        #Version (unaccurate aprox)
648                                        app_info['version']=app_info['channel_releases']['appimage'][0].split('/')[-2]
649
650                self._set_status(0)
651                self.partial_progress=100
652                return(app_info)
653        #def _get_info
654
655        def _get_releases(self,app_info):
656                releases=[]
657                releases_page=''
658                #self._debug("Info url: %s"%app_info['installerUrl'])
659                url_source=""
660                try:
661                        if 'github' in app_info['installerUrl']:
662                                releases_page="https://github.com"
663                        if 'gitlab' in app_info['installerUrl']:
664                                releases_page="https://gitlab.com"
665                        if 'opensuse' in app_info['installerUrl'].lower():
666                                releases_page=""
667                                url_source="opensuse"
668#                               app_info['installerUrl']=app_info['installerUrl']+"/download"
669
670                        if (url_source or releases_page) and not app_info['installerUrl'].lower().endswith(".appimage"):
671                                content=''
672                                with urllib.request.urlopen(app_info['installerUrl']) as f:
673                                        try:
674                                                content=f.read().decode('utf-8')
675                                        except:
676                                                #self._debug("UTF-8 failed")
677                                                pass
678                                        soup=BeautifulSoup(content,"html.parser")
679                                        package_a=soup.findAll('a', attrs={ "href" : re.compile(r'.*\.[aA]pp[iI]mage$')})
680
681                                        for package_data in package_a:
682                                                if url_source=="opensuse":
683                                                        package_name=package_data.findAll('a', attrs={"class" : "mirrorbrain-btn"})
684                                                else:
685                                                        package_name=package_data.findAll('strong', attrs={ "class" : "pl-1"})
686                                                package_link=package_data['href']
687                                                if releases_page or url_source:
688                                                        package_link=releases_page+package_link
689                                                        releases.append(package_link)
690                                                        #self._debug("Link: %s"%package_link)
691                        if releases==[]:
692                                releases=[app_info['installerUrl']]
693                except Exception as e:
694                        #self._debug(e)
695                        pass
696                #self._debug(releases)
697                return releases
698        #def _get_releases
699       
Note: See TracBrowser for help on using the repository browser.