Add ModDB download helper

This helper facility will seamlessly swap a "File" url (e.g. "https://www.moddb.com/games/tribes/downloads/starsiege-tribes-retro-version") into a mirror download link.

This enables installer authors to specify files to be downloaded directly from ModDB, e.g.

```
files:
    - t1_setup:
        url: https://www.moddb.com/games/tribes/downloads/starsiege-tribes-retro-version
        filename: t1retro.exe
        referer: https://www.moddb.com/games/tribes/downloads
```

Signed-off-by: Antoine Mazeas <antoine@karthanis.net>
This commit is contained in:
Antoine Mazeas 2023-01-14 01:58:26 +01:00 committed by Mathieu Comandon
parent 07eae9bb3c
commit 18c2f97002
7 changed files with 72 additions and 1 deletions

View file

@ -4,6 +4,7 @@ from gettext import gettext as _
import gi
gi.require_version('Gdk', '3.0')
gi.require_version('Gtk', '3.0')
from gi.repository import Gdk, GLib, GObject, Gtk

View file

@ -15,6 +15,7 @@ from lutris.services import SERVICES
from lutris.util.game_finder import find_linux_game_executable, find_windows_game_executable
from lutris.util.gog import convert_gog_config_to_lutris, get_gog_config_from_path, get_gog_game_path
from lutris.util.log import logger
from lutris.util.moddb import downloadhelper as moddbhelper
class LutrisInstaller: # pylint: disable=too-many-instance-attributes
@ -158,6 +159,8 @@ class LutrisInstaller: # pylint: disable=too-many-instance-attributes
# Run variable substitution on the URLs from the script
for file in self.files:
file.set_url(self.interpreter._substitute(file.url))
if moddbhelper.is_moddb_url(file.url):
file.set_url(moddbhelper.get_moddb_download_url(file.url))
if installer_file_id and self.service:
logger.info("Getting files for %s", installer_file_id)

View file

View file

@ -0,0 +1,27 @@
"""Helper functions to assist downloading files from ModDB"""
import moddb
import re
MODDB_FQDN = 'https://www.moddb.com'
MODDB_URL_MATCHER = '^https://(www\.)?moddb\.com'
def is_moddb_url(url):
return re.match(MODDB_URL_MATCHER, url.lower()) is not None
def get_moddb_download_url(moddb_permalink_url):
return MODDB_FQDN + __autoselect_moddb_mirror(__get_html_and_resolve_mirrors_list(moddb_permalink_url))._url
def __autoselect_moddb_mirror(mirrors_list):
# dumb autoselect for now: rank mirrors by capacity (lower is better), pick first (lowest load)
return sorted(mirrors_list, key=lambda m: m.capacity)[0]
def __get_html_and_resolve_mirrors_list(moddb_permalink_url):
moddb_obj = moddb.parse_page(moddb_permalink_url)
if not isinstance(moddb_obj, moddb.File):
raise RuntimeError("supplied url does not point to the page of a file hosted on moddb.com")
mirrors_list = moddb_obj.get_mirrors()
if not any(mirrors_list):
raise RuntimeError("no available mirror for the file hosted on moddb.com")
return mirrors_list

View file

@ -2,6 +2,7 @@ import os
import gi
gi.require_version('Gdk', '3.0')
gi.require_version('Gtk', '3.0')
from lutris import startup

View file

@ -45,6 +45,7 @@ setup(
'lutris.util.egs',
'lutris.util.graphics',
'lutris.util.mame',
'lutris.util.moddb',
'lutris.util.steam',
'lutris.util.steam.vdf',
'lutris.util.retroarch',
@ -65,7 +66,8 @@ setup(
'pypresence',
'PyYAML',
'requests',
'pypresence'
'pypresence',
'moddb'
],
url='https://lutris.net',
description='Video game preservation platform',

View file

@ -0,0 +1,37 @@
import unittest
from lutris.util.moddb import downloadhelper as moddb
class ModDBHelperTests(unittest.TestCase):
def test_is_moddb_url_has_www_success(self):
url = 'https://www.moddb.com/something'
self.assertTrue(moddb.is_moddb_url(url))
def test_is_moddb_url_no_slug_has_www_success(self):
url = 'https://www.moddb.com'
self.assertTrue(moddb.is_moddb_url(url))
def test_is_moddb_url_no_www_success(self):
url = 'https://moddb.com/something'
self.assertTrue(moddb.is_moddb_url(url))
def test_is_moddb_url_no_slug_no_www_success(self):
url = 'https://moddb.com'
self.assertTrue(moddb.is_moddb_url(url))
def test_is_moddb_url_other_subdomain_failure(self):
url = 'https://subdomain.moddb.com/something'
self.assertFalse(moddb.is_moddb_url(url))
def test_is_moddb_url_no_slug_other_subdomain_failure(self):
url = 'https://subdomain.moddb.com'
self.assertFalse(moddb.is_moddb_url(url))
def test_is_moddb_url_random_domain_failure(self):
url = 'https://somedomain.com/something'
self.assertFalse(moddb.is_moddb_url(url))
def test_is_moddb_url_no_slug_random_domain_failure(self):
url = 'https://somedomain.com'
self.assertFalse(moddb.is_moddb_url(url))