utils.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. from games.constants import (
  2. ENGLISH_PATCHED_KEYWORDS,
  3. HACK_KEYWORDS,
  4. REGION_KEYWORDS,
  5. UNDUB_KEYWORDS,
  6. )
  7. import os
  8. import xml.etree.ElementTree as ET
  9. import subprocess
  10. from dateutil import parser
  11. from django.conf import settings
  12. from .models import Developer, Game, GameSystem, Genre, Publisher, GameCollection
  13. import logging
  14. logger = logging.Logger(__name__)
  15. def update_media_root_for_import(file_path):
  16. """Given a file path, re-write it for our app MEDIA_ROOT"""
  17. suffix = ""
  18. if file_path:
  19. suffix = file_path.split("/media/")[-1]
  20. return suffix
  21. def import_gamelist_file_to_db_for_system(
  22. game_system_slug, file_path=None, full_scan=False
  23. ):
  24. imported_games = []
  25. not_imported_games = []
  26. if not file_path:
  27. file_path = os.path.join(settings.ROMS_DIR, game_system_slug, "gamelist.xml")
  28. if not os.path.exists(file_path):
  29. logger.info(
  30. "File path for {game_system_slug} had no gamelist.xml file, run a scraper first!"
  31. )
  32. return
  33. gamelist = ET.parse(file_path)
  34. game_system = GameSystem.objects.filter(retropie_slug=game_system_slug).first()
  35. if not game_system:
  36. defaults = settings.GAME_SYSTEM_DEFAULTS.get(game_system_slug, None)
  37. full_system_name = game_system_slug
  38. if defaults:
  39. full_system_name = defaults.get("name", game_system_slug)
  40. game_system = GameSystem.objects.create(
  41. name=full_system_name, retropie_slug=game_system_slug
  42. )
  43. games = gamelist.findall("game")
  44. for game in games:
  45. name = game.find("name").text
  46. game_path = game.find("path").text.lower()
  47. try:
  48. obj, created = Game.objects.get_or_create(
  49. name=name, game_system=game_system
  50. )
  51. except Game.MultipleObjectsReturned:
  52. logger.warning(
  53. f"While importing {name} for {game_system}, duplicate entry found"
  54. )
  55. print(f"While importing {name} for {game_system}, duplicate entry found")
  56. continue
  57. if not created and not full_scan:
  58. not_imported_games.append(game)
  59. logger.info(f"Found game {game} and not doing full scan, so skipping")
  60. continue
  61. english_patched = game_path.lower() in ENGLISH_PATCHED_KEYWORDS
  62. undub = game_path.lower() in UNDUB_KEYWORDS
  63. hack = game_path.lower() in HACK_KEYWORDS
  64. region = Game.Region.X.name
  65. if any(us in game_path for us in REGION_KEYWORDS["US"]):
  66. region = Game.Region.US.name
  67. if any(jp in game_path for jp in REGION_KEYWORDS["JP"]):
  68. region = Game.Region.JP.name
  69. if any(eu in game_path for eu in REGION_KEYWORDS["EU"]):
  70. region = Game.Region.EU.name
  71. release_date_str = game.find("releasedate").text
  72. developer_str = game.find("developer").text
  73. publisher_str = game.find("publisher").text
  74. genres_str = game.find("genre").text
  75. rating_str = game.find("rating").text
  76. players_str = "1"
  77. if game.find("players"):
  78. players_str = game.find("players").text
  79. try:
  80. kid_game = game.find("kidgame").text == "true"
  81. except AttributeError:
  82. kid_game = False
  83. genre_list = []
  84. if genres_str:
  85. genre_str_list = genres_str.split(", ")
  86. for genre_str in genre_str_list:
  87. genre, _created = Genre.objects.get_or_create(name=genre_str)
  88. genre_list.append(genre)
  89. players = int(players_str) if players_str else 1
  90. rating = float(rating_str) if rating_str else None
  91. publisher = None
  92. if publisher_str:
  93. publisher, _created = Publisher.objects.get_or_create(name=publisher_str)
  94. developer = None
  95. if developer_str:
  96. developer, _created = Developer.objects.get_or_create(name=developer_str)
  97. release_date = parser.parse(release_date_str) if release_date_str else None
  98. description = game.find("desc").text
  99. screenshot_path = update_media_root_for_import(game.find("image").text)
  100. rom_path = update_media_root_for_import(game.find("path").text)
  101. video_path_elem = game.find("video")
  102. video_path = ""
  103. if video_path_elem:
  104. video_path = update_media_root_for_import(video_path_elem.text)
  105. marquee_path = update_media_root_for_import(game.find("marquee").text)
  106. obj.game_system = game_system
  107. obj.developer = developer
  108. obj.publisher = publisher
  109. obj.players = players
  110. obj.description = description
  111. obj.release_date = release_date
  112. obj.rating = rating
  113. obj.genre.set(genre_list)
  114. obj.screenshot = screenshot_path
  115. obj.rom_file = rom_path
  116. obj.video = video_path
  117. obj.marquee = marquee_path
  118. obj.kid_game = kid_game
  119. obj.english_patched = english_patched
  120. obj.hack = hack
  121. obj.undub = undub
  122. obj.region = region
  123. obj.save()
  124. imported_games.append(game)
  125. return {"imported": imported_games, "not_imported": not_imported_games}
  126. def export_gamelist_file_to_path_for_system(game_system_slug, file_path=None):
  127. exported_games = []
  128. game_system = GameSystem.objects.get(retropie_slug=game_system_slug)
  129. if not file_path:
  130. file_path = f"/tmp/{game_system_slug}-gamelist.xml"
  131. # file_path = os.path.join(settings.ROMS_DIR, game_system_slug, "gamelist.xml")
  132. root = ET.Element("gameList")
  133. tree = ET.ElementTree(root)
  134. tree.write(file_path)
  135. games = Game.objects.filter(game_system=game_system)
  136. for game in games:
  137. game_node = ET.SubElement(root, "game")
  138. genre_str = ", ".join(game.genre.all().values_list("name", flat=True))
  139. release_date_str = ""
  140. if game.release_date:
  141. release_date_str = game.release_date.strftime("%Y%m%dT00000")
  142. ET.SubElement(game_node, "path").text = (
  143. game.rom_file.path if game.rom_file else ""
  144. )
  145. ET.SubElement(game_node, "name").text = game.name
  146. ET.SubElement(game_node, "thumbnail").text = ""
  147. ET.SubElement(game_node, "image").text = (
  148. game.screenshot.path if game.screenshot else ""
  149. )
  150. ET.SubElement(game_node, "marquee").text = (
  151. game.marquee.path if game.marquee else ""
  152. )
  153. ET.SubElement(game_node, "video").text = game.video.path if game.video else ""
  154. ET.SubElement(game_node, "rating").text = str(game.rating)
  155. ET.SubElement(game_node, "desc").text = game.description
  156. ET.SubElement(game_node, "releasedate").text = release_date_str
  157. ET.SubElement(game_node, "developer").text = str(game.developer)
  158. ET.SubElement(game_node, "publisher").text = str(game.publisher)
  159. ET.SubElement(game_node, "genre").text = genre_str
  160. ET.SubElement(game_node, "players").text = str(game.players)
  161. if game.kid_game:
  162. ET.SubElement(game_node, "kidgame").text = "true"
  163. exported_games.append(game)
  164. tree = ET.ElementTree(root)
  165. tree.write(file_path, xml_declaration=True, encoding="utf-8")
  166. return exported_games, file_path
  167. def skyscrape_console(game_system_slug):
  168. scraper_config = settings.SCRAPER_CONFIG_FILE
  169. scraper_binary = settings.SCRAPER_BIN_PATH
  170. scraper_site = settings.SCRAPER_SITE
  171. # If the config file is relative, append our base dir
  172. if scraper_config[0] != "/":
  173. scraper_config = os.path.join(settings.BASE_DIR, scraper_config)
  174. if not os.path.exists(scraper_config):
  175. logger.info(f"Config file not found at {scraper_config}")
  176. return
  177. logger.info(f"Scraping game info using configuration file from {scraper_config}")
  178. scrape_output = subprocess.run(
  179. [
  180. scraper_binary,
  181. "-c",
  182. f"{scraper_config}",
  183. "-s",
  184. f"{scraper_site}",
  185. "-f",
  186. "emulationstation",
  187. "-p",
  188. f"{game_system_slug}",
  189. ],
  190. capture_output=True,
  191. )
  192. load_output = subprocess.run(
  193. [
  194. scraper_binary,
  195. "-c",
  196. f"{scraper_config}",
  197. "-f",
  198. "emulationstation",
  199. "-p",
  200. f"{game_system_slug}",
  201. ],
  202. capture_output=True,
  203. )
  204. # TODO We should progressively pipe output to a log file instead of this
  205. # print(scrape_output)
  206. # print(load_output)
  207. print(f"Scraped info for {game_system_slug}")
  208. return scrape_output, load_output
  209. def export_collections(dryrun=True):
  210. exported = []
  211. for collection in GameCollection.objects.all():
  212. collection.export_to_file(dryrun)
  213. exported.append(collection)
  214. return exported