1
0
forked from Mirrors/itch-dl

Add filtering files by platform/type

This commit is contained in:
Ryszard Knop
2025-04-03 18:50:45 +02:00
parent a092532192
commit 7a833a8a5b
3 changed files with 69 additions and 15 deletions

View File

@@ -3,7 +3,7 @@ import sys
import logging import logging
import argparse import argparse
from .handlers import get_jobs_for_url_or_path from .handlers import get_jobs_for_url_or_path, preprocess_job_urls
from .downloader import drive_downloads from .downloader import drive_downloads
from .config import Settings, load_config from .config import Settings, load_config
from .keys import get_download_keys from .keys import get_download_keys
@@ -50,6 +50,11 @@ def parse_args() -> argparse.Namespace:
help="print scraped game URLs without downloading them") help="print scraped game URLs without downloading them")
parser.add_argument("--parallel", metavar="parallel", type=int, default=None, parser.add_argument("--parallel", metavar="parallel", type=int, default=None,
help="how many threads to use for downloading games (default: 1)") help="how many threads to use for downloading games (default: 1)")
parser.add_argument("--filter-files-platform", metavar="platforms", action="extend", nargs='+',
help="filter downloaded files by platform (windows, mac, linux, android, native)")
parser.add_argument("--filter-files-type", metavar="types", action="extend", nargs='+',
help="filter downloaded files by type (see wiki for valid values)")
parser.add_argument("--filter-files-glob", metavar="glob", default=None, parser.add_argument("--filter-files-glob", metavar="glob", default=None,
help="filter downloaded files with a shell-style glob/fnmatch (unmatched files are skipped)") help="filter downloaded files with a shell-style glob/fnmatch (unmatched files are skipped)")
parser.add_argument("--filter-files-regex", metavar="regex", default=None, parser.add_argument("--filter-files-regex", metavar="regex", default=None,

View File

@@ -24,6 +24,8 @@ class Settings:
urls_only: bool = False urls_only: bool = False
parallel: int = 1 parallel: int = 1
filter_files_platform: list | None = None
filter_files_type: list | None = None
filter_files_glob: str | None = None filter_files_glob: str | None = None
filter_files_regex: str | None = None filter_files_regex: str | None = None
@@ -33,6 +35,44 @@ class Settings:
verbose: bool = False verbose: bool = False
def process_platform_traits(platforms: list[str]) -> list[str] | None:
"""Converts the user-friendly platform strings into itch.io upload p_traits."""
if not platforms:
return None
trait_mapping = {
"win": "p_windows",
"lin": "p_linux",
"mac": "p_osx",
"osx": "p_osx",
"darwin": "p_osx",
"and": "p_android",
}
traits = set()
for p in platforms:
platform_trait = None
p = p.strip().lower().removeprefix("p_")
if p.startswith("native"):
p = platform.system().lower()
if p.endswith("bsd"):
logging.warning("Note: Native downloads for *BSDs are not available - Linux binaries will be used.")
p = "linux"
for key, trait in trait_mapping.items():
if p.startswith(key):
platform_trait = trait
break
if not platform_trait:
raise ValueError(f"Platform {p} not known!")
traits.add(platform_trait)
return list(traits)
def create_and_get_config_path() -> str: def create_and_get_config_path() -> str:
"""Returns the configuration directory in the appropriate """Returns the configuration directory in the appropriate
location for the current OS. The directory may not exist.""" location for the current OS. The directory may not exist."""
@@ -106,4 +146,7 @@ def load_config(args: argparse.Namespace, profile: str | None = None) -> Setting
if value := getattr(args, key): if value := getattr(args, key):
setattr(settings, key, value) setattr(settings, key, value)
# Extra handling for special settings:
settings.filter_files_platform = process_platform_traits(settings.filter_files_platform)
return settings return settings

View File

@@ -15,7 +15,7 @@ from tqdm import tqdm
from tqdm.contrib.concurrent import thread_map from tqdm.contrib.concurrent import thread_map
from .api import ItchApiClient from .api import ItchApiClient
from .utils import ItchDownloadError, get_int_after_marker_in_json from .utils import ItchDownloadError, get_int_after_marker_in_json, should_skip_item_by_glob, should_skip_item_by_regex
from .consts import ITCH_GAME_URL_REGEX from .consts import ITCH_GAME_URL_REGEX
from .config import Settings from .config import Settings
from .infobox import parse_infobox, InfoboxMetadata from .infobox import parse_infobox, InfoboxMetadata
@@ -297,29 +297,35 @@ class GameDownloader:
try: try:
os.makedirs(paths["files"], exist_ok=True) os.makedirs(paths["files"], exist_ok=True)
for upload in game_uploads: for upload in game_uploads:
if any(key not in upload for key in ("id", "filename", "storage")): if any(key not in upload for key in ("id", "filename", "type", "traits", "storage")):
errors.append(f"Upload metadata incomplete: {upload}") errors.append(f"Upload metadata incomplete: {upload}")
continue continue
logging.info(upload)
upload_id = upload["id"] upload_id = upload["id"]
file_name = upload["filename"] file_name = upload["filename"]
file_type = upload["type"]
file_traits = upload["traits"]
expected_size = upload.get("size") expected_size = upload.get("size")
upload_is_external = upload["storage"] == "external" upload_is_external = upload["storage"] == "external"
if self.settings.filter_files_glob and not fnmatch.fnmatch(file_name, self.settings.filter_files_glob): if self.settings.filter_files_type and file_type not in self.settings.filter_files_type:
logging.info( logging.info("File '%s' has ignored type '%s', skipping", file_name, file_type)
"File '%s' does not match the glob filter '%s', skipping",
file_name,
self.settings.filter_files_glob,
)
continue continue
if self.settings.filter_files_regex and not re.fullmatch(self.settings.filter_files_regex, file_name): if (
logging.info( self.settings.filter_files_platform
"File '%s' does not match the regex filter '%s', skipping", and file_type == "default"
file_name, and not any(trait in self.settings.filter_files_platform for trait in file_traits)
self.settings.filter_files_regex, ):
) # Setup for filter_files_platform is in config.py, including the trait listing.
logging.info("File '%s' not for requested platforms, skipping", file_name)
continue
if should_skip_item_by_glob("File", file_name, self.settings.filter_files_glob):
continue
if should_skip_item_by_regex("File", file_name, self.settings.filter_files_regex):
continue continue
logging.debug( logging.debug(