images.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. ########################################################################
  2. # Wiizard - A Wii games manager
  3. # Copyright (C) 2023 CYBERDEViL
  4. #
  5. # This file is part of Wiizard.
  6. #
  7. # Wiizard is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # Wiizard is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  19. #
  20. ########################################################################
  21. import time
  22. import os
  23. from PyQt5.QtGui import QPixmap, QIcon
  24. from PyQt5.QtCore import Qt, pyqtSignal, QObject
  25. from wiizard import globals as Global
  26. from wiizard.thread import AbstractThread, THREAD_FLAG_IS_CANCELLABLE
  27. from wiizard.scrapers.common import ScraperError, ImgRequestProperties
  28. from wiizard.requestHandler import ImageDownloadException
  29. from wiizard.const import (
  30. IMG_FRONT_COVER,
  31. IMG_FRONT_3D_COVER,
  32. IMG_DISC
  33. )
  34. from wiizard.models.settings import GameImageSettings
  35. class AppResources:
  36. """ For application icons and images
  37. """
  38. ICONS_PATH = ""
  39. IMAGES_PATH = ""
  40. ICONS = {}
  41. IMAGES = {}
  42. @staticmethod
  43. def resolvePaths():
  44. AppResources.findIconsPath()
  45. AppResources.findImagesPath()
  46. @staticmethod
  47. def findIconsPath():
  48. testFile = "wiizard_icon_32.png"
  49. testPaths = [
  50. os.path.join(os.path.dirname(os.path.abspath(__file__)), "images/"),
  51. "/usr/share/icons/wiizard/",
  52. "/usr/local/share/icons/wiizard/",
  53. os.path.expanduser("~/.local/share/icons/wiizard/")
  54. ]
  55. for path in testPaths:
  56. if os.path.exists(os.path.join(path, testFile)):
  57. AppResources.ICONS_PATH = path
  58. return path
  59. print("WARNING: Were unable to find icons location.")
  60. return None
  61. @staticmethod
  62. def getIcon(iconName):
  63. if iconName not in AppResources.ICONS:
  64. filepath = os.path.join(AppResources.ICONS_PATH, iconName)
  65. icon = QIcon(QPixmap(filepath))
  66. AppResources.ICONS.update({iconName: icon})
  67. return AppResources.ICONS[iconName]
  68. @staticmethod
  69. def findImagesPath():
  70. testFile = "default_2d_cover.png"
  71. testPaths = [
  72. os.path.join(os.path.dirname(os.path.abspath(__file__)), "images/"),
  73. "/usr/share/wiizard/",
  74. "/usr/local/share/wiizard/",
  75. os.path.expanduser("~/.local/share/wiizard/")
  76. ]
  77. for path in testPaths:
  78. if os.path.exists(os.path.join(path, testFile)):
  79. AppResources.IMAGES_PATH = path
  80. return path
  81. print("WARNING: Were unable to find images location.")
  82. return None
  83. @staticmethod
  84. def getImagePath(imageName):
  85. return os.path.join(AppResources.IMAGES_PATH, imageName)
  86. class _SharedGameImages(QObject):
  87. """ - these are cached on disk (The images, not the pixmap..)
  88. - for having only one pixmap in ram per game image
  89. - for having unified images in all views (img cover type and size)
  90. """
  91. changed = pyqtSignal()
  92. SIZE_LARGE = 160
  93. SIZE_MEDIUM = 96
  94. SIZE_SMALL = 48
  95. def __init__(self):
  96. QObject.__init__(self)
  97. self.__imgWidth = self.SIZE_LARGE
  98. self.__imgType = IMG_FRONT_3D_COVER
  99. self.__gamePixmaps = {}
  100. self.__games = [] # id6Strs (for what games this class has available images)
  101. self.remakePixmaps()
  102. @property
  103. def imgType(self):
  104. return self.__imgType
  105. def setImgSize(self, size):
  106. self.__imgWidth = size
  107. self.remakePixmaps()
  108. def setImgType(self, imgType):
  109. self.__imgType = imgType
  110. self.remakePixmaps()
  111. def getGameImage(self, id6Str):
  112. return self.__gamePixmaps[id6Str]
  113. def addGames(self, id6StrList):
  114. for id6Str in id6StrList:
  115. if id6Str not in self.__games:
  116. self.__addGameImage(id6Str)
  117. self.__games.append(id6Str)
  118. def __addGameImage(self, id6Str):
  119. path = Global.ImageCache.getImage(id6Str, self.__imgType)
  120. if path is None:
  121. self.__gamePixmaps.update({id6Str: self.defaultPixmap})
  122. else:
  123. width = self.__imgWidth
  124. pixmap = QPixmap(path).scaledToWidth(width, Qt.SmoothTransformation)
  125. self.__gamePixmaps.update({id6Str: pixmap})
  126. def remakePixmaps(self):
  127. self.defaultPixmap = None
  128. self.__gamePixmaps.clear()
  129. if self.__imgType is None:
  130. self.changed.emit()
  131. return
  132. defaultMap = {
  133. IMG_FRONT_COVER : AppResources.getImagePath("default_2d_cover.png"),
  134. IMG_FRONT_3D_COVER: AppResources.getImagePath("default_3d_cover.png"),
  135. IMG_DISC : AppResources.getImagePath("default_disc_cover.png")
  136. }
  137. width = self.__imgWidth
  138. self.defaultPixmap = QPixmap(defaultMap[self.__imgType]).scaledToWidth(width, Qt.SmoothTransformation)
  139. for id6Str in self.__games:
  140. self.__addGameImage(id6Str)
  141. self.changed.emit()
  142. class DlImagesThread(AbstractThread):
  143. progress = pyqtSignal(int)
  144. def __init__(self, dlList, requestHandler, scraper):
  145. AbstractThread.__init__(self, flags=THREAD_FLAG_IS_CANCELLABLE)
  146. self.__dlList = dlList
  147. self.__requestHandler = requestHandler
  148. self.__scraper = scraper
  149. self.failed = []
  150. def run(self):
  151. dlList = self.__dlList
  152. scraper = self.__scraper
  153. progress = 0
  154. for id6Str in dlList:
  155. try:
  156. imageLinks = self.__scraper.getGameImageLinks(id6Str)
  157. except ScraperError as err:
  158. print(err)
  159. continue
  160. imgTypeFlags = 0
  161. for imgType in dlList[id6Str]:
  162. imgTypeFlags |= imgType
  163. languages = GameImageSettings.getLanguages()
  164. requestProperties = ImgRequestProperties(imgTypeFlags,
  165. languages=languages)
  166. gameImageLinks = scraper.selectImages(imageLinks, requestProperties)
  167. if not gameImageLinks:
  168. if id6Str not in self.failed:
  169. self.failed.append(id6Str)
  170. time.sleep(1)
  171. continue
  172. for gameImageLink in gameImageLinks:
  173. imgPath = Global.ImageCache.getImagePath(id6Str, gameImageLink.type)
  174. try:
  175. self.__requestHandler.downloadImage(gameImageLink.url, imgPath)
  176. except ImageDownloadException as err:
  177. print(err) # TODO note the failed for feedback?
  178. progress += 1
  179. continue
  180. print(" -- Downloaded", imgPath)
  181. if self.cancelled is True:
  182. break
  183. progress += 1
  184. self.progress.emit(progress)
  185. time.sleep(1)
  186. if self.cancelled is True:
  187. break
  188. self.completed.emit()