mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-11 12:33:53 -06:00
feat: put a .nomedia in packs unzipped on Android
The RetroArch pack lands in RetroArch Android's system folder, where MediaProvider indexed 3 842 images, sounds and videos (552 MB) into the gallery and music apps: a folder holding .nomedia is hidden with every folder below it (FileUtils.java:1587-1629, ModernMediaScanner.java:845-846, a183e2b9). A pack whose platform files an Android target, unfiltered or built for one, carries an empty .nomedia. pack_files counts it and the integrity check takes it for a marker.
This commit is contained in:
1 parent
c33114714e
commit
39bc5f020b
6 files changed
+97
-7
No files matched your search
@@ -2599,7 +2599,7 @@
|
||||
],
|
||||
"total_files": 8338,
|
||||
"total_size": 7080104487,
|
||||
"pack_files": 14921,
|
||||
"pack_files": 14922,
|
||||
"pack_size": 7222137851,
|
||||
"total_omitted": 37,
|
||||
"omitted_files": [
|
||||
|
||||
@@ -416,6 +416,17 @@ def platform_list_version(config: dict, platform: str) -> str:
|
||||
PACK_DOCUMENTS = ("README.txt", "manifest.json")
|
||||
|
||||
|
||||
def pack_markers(
|
||||
platform_name: str, target_name: str | None, platforms_dir: str
|
||||
) -> tuple[str, ...]:
|
||||
"""The empty files a pack carries for the systems it is unzipped on,
|
||||
as named before flattening: the .nomedia of a pack for Android."""
|
||||
from common import list_available_targets
|
||||
|
||||
names = [t["name"] for t in list_available_targets(platform_name, platforms_dir)]
|
||||
return (MEDIA_MARKER,) if lands_on_android(names, target_name) else ()
|
||||
|
||||
|
||||
def core_data_trees(
|
||||
config: dict,
|
||||
profiles: dict[str, dict],
|
||||
@@ -1477,6 +1488,10 @@ def generate_pack(
|
||||
),
|
||||
)
|
||||
_write_generated_member(zf, "README.txt", readme_text)
|
||||
for marker in pack_markers(platform_name, target_name, platforms_dir):
|
||||
_write_generated_member(
|
||||
zf, _flat(f"{base_dest}/{marker}" if base_dest else marker, base_dest, flatten), ""
|
||||
)
|
||||
|
||||
_report_pack_counts(
|
||||
zip_path, file_status, total_files, core_count, source,
|
||||
@@ -5147,7 +5162,7 @@ def generate_manifest(
|
||||
"total_size": total_size,
|
||||
"pack_files": (
|
||||
len(manifest_files) + len(data_sizes) + len(PACK_DOCUMENTS)
|
||||
+ len(rename_notes)
|
||||
+ len(rename_notes) + len(pack_markers(platform_name, target_name, platforms_dir))
|
||||
),
|
||||
"pack_size": total_size + sum(data_sizes),
|
||||
"total_omitted": len(omitted_by_destination),
|
||||
@@ -5719,6 +5734,8 @@ def verify_and_finalize_packs(
|
||||
return all_ok
|
||||
|
||||
from packpaths import ( # noqa: E402,F401
|
||||
MEDIA_MARKER,
|
||||
lands_on_android,
|
||||
_archive_destination,
|
||||
_path_parents,
|
||||
_has_path_conflict,
|
||||
|
||||
@@ -192,6 +192,23 @@ def _register_path(dest: str, seen_files: set[str], seen_parents: set[str]) -> N
|
||||
for parent in _path_parents(dest):
|
||||
seen_parents.add(parent)
|
||||
|
||||
# Android's media scanner hides a folder holding a .nomedia file, and every
|
||||
# folder below it (MediaProvider a183e2b9, FileUtils.java:1587-1629
|
||||
# isDirectoryHidden, ModernMediaScanner.java:845-846). RetroArch writes none
|
||||
# (libretro-common/file/file_path_io.c:194 only skips one), so a pack
|
||||
# unzipped into RetroArch Android's system folder put its samples, overlays
|
||||
# and videos in the gallery and music apps.
|
||||
MEDIA_MARKER = ".nomedia"
|
||||
|
||||
|
||||
def lands_on_android(target_names: Iterable[str], target: str | None) -> bool:
|
||||
"""Whether a pack can be unzipped on Android: its target is an Android
|
||||
one, or, unfiltered, the platform files one."""
|
||||
if target:
|
||||
return target.startswith("android")
|
||||
return any(name.startswith("android") for name in target_names)
|
||||
|
||||
|
||||
def _flat(arcname: str, prefix: str, flatten: bool) -> str:
|
||||
"""Strip base_destination prefix from ZIP arcname when flattening."""
|
||||
if flatten and prefix and arcname.startswith(prefix + "/"):
|
||||
|
||||
@@ -10,7 +10,7 @@ from collections import Counter
|
||||
|
||||
from auto_fetch import DEFAULT_BIOS_DIR
|
||||
from artifacts import _build_timestamp
|
||||
from packpaths import _has_path_conflict
|
||||
from packpaths import MEDIA_MARKER, _has_path_conflict
|
||||
from ziptools import build_zip_contents_index
|
||||
from ziptools import check_inside_zip
|
||||
from nativemode import digest_algorithm
|
||||
@@ -170,7 +170,7 @@ def verify_pack(
|
||||
# (bios/INSTRUCTIONS_x.txt, games/keen6/INSTRUCTIONS_y.txt), so
|
||||
# they are told by their own name, not by a root prefix.
|
||||
base = name.rsplit("/", 1)[-1]
|
||||
if base.startswith(("INSTRUCTIONS_", "RENAMED_")) or name in (
|
||||
if base.startswith(("INSTRUCTIONS_", "RENAMED_")) or base == MEDIA_MARKER or name in (
|
||||
"manifest.json",
|
||||
"README.txt",
|
||||
):
|
||||
@@ -539,7 +539,7 @@ def _structural_errors(zf: zipfile.ZipFile, zip_set: set) -> list[str]:
|
||||
if "GraphicMods" not in info.filename and info.filename not in (
|
||||
"manifest.json",
|
||||
"README.txt",
|
||||
):
|
||||
) and info.filename.rsplit("/", 1)[-1] != MEDIA_MARKER:
|
||||
errors.append(f"zero-byte: {info.filename}")
|
||||
|
||||
return errors
|
||||
@@ -746,7 +746,7 @@ def verify_pack_against_platform(
|
||||
is_flat = bool(base_dest) and not any(
|
||||
n.startswith(base_dest + "/")
|
||||
for n in zip_set
|
||||
if n not in ("README.txt", "manifest.json") and not n.endswith("/")
|
||||
if n not in ("README.txt", "manifest.json", MEDIA_MARKER) and not n.endswith("/")
|
||||
)
|
||||
|
||||
errors.extend(_structural_errors(zf, zip_set))
|
||||
|
||||
@@ -301,6 +301,60 @@ RECORD = {
|
||||
}
|
||||
|
||||
|
||||
class APackForAndroidHidesItsMedia(PackCountFixture):
|
||||
"""RetroArch Android writes no .nomedia, and its system folder held the
|
||||
samples, overlays and videos the gallery and music apps then showed."""
|
||||
|
||||
def _pack(self, target: str | None) -> dict[str, int]:
|
||||
targets = self.platforms / "targets"
|
||||
targets.mkdir(exist_ok=True)
|
||||
(targets / "demo.yml").write_text(yaml.dump({"platform": "demo", "targets": {
|
||||
"android-arm64-v8a": {"architecture": "aarch64", "cores": ["demo"]},
|
||||
"linux-x86_64": {"architecture": "x86_64", "cores": ["demo"]},
|
||||
}}))
|
||||
out = self.root / f"dist-{target}"
|
||||
out.mkdir()
|
||||
zip_path = builder.generate_pack(
|
||||
"demo", str(self.platforms), self.db, str(self.bios), str(out),
|
||||
emulators_dir=str(self.emulators), emu_profiles=self.profiles,
|
||||
data_registry=self.registry, offline=True, target_name=target,
|
||||
)
|
||||
with zipfile.ZipFile(zip_path) as archive:
|
||||
return {m.filename: m.file_size for m in archive.infolist()}
|
||||
|
||||
def test_the_whole_pack_and_an_android_one_carry_it(self):
|
||||
for target in (None, "android-arm64-v8a"):
|
||||
with self.subTest(target=target):
|
||||
self.assertEqual(self._pack(target).get(".nomedia"), 0)
|
||||
|
||||
def test_a_pack_for_another_target_does_not(self):
|
||||
self.assertNotIn(".nomedia", self._pack("linux-x86_64"))
|
||||
|
||||
def test_the_manifest_counts_it(self):
|
||||
members = self._pack(None)
|
||||
manifest = builder.generate_manifest(
|
||||
"demo", str(self.platforms), self.db, str(self.bios),
|
||||
str(self.platforms / "_registry.yml"),
|
||||
emulators_dir=str(self.emulators), emu_profiles=self.profiles,
|
||||
offline=True, data_registry=self.registry,
|
||||
)
|
||||
self.assertEqual(manifest["pack_files"], len(set(members) | {"manifest.json"}))
|
||||
|
||||
def test_the_integrity_check_takes_it_for_a_marker(self):
|
||||
import packverify
|
||||
|
||||
buffer = io.BytesIO()
|
||||
with zipfile.ZipFile(buffer, "w") as archive:
|
||||
archive.writestr(".nomedia", b"")
|
||||
archive.writestr("empty.bin", b"")
|
||||
with zipfile.ZipFile(buffer) as archive:
|
||||
errors = packverify._structural_errors(archive, set(archive.namelist()))
|
||||
self.assertEqual(errors, ["zero-byte: empty.bin"])
|
||||
|
||||
def test_a_platform_without_android_gets_none(self):
|
||||
self.assertEqual(builder.pack_markers("demo", None, str(self.platforms)), ())
|
||||
|
||||
|
||||
class TheTableDescribesTheRelease(unittest.TestCase):
|
||||
"""The Download link gives the last release, not what main would build.
|
||||
|
||||
|
||||
+3
-1
@@ -1466,7 +1466,9 @@ media scanner hides a folder holding one, and every folder below it
|
||||
(MediaProvider, `FileUtils.java` `isDirectoryHidden`), so the samples,
|
||||
overlays and videos of the BIOS folder stay out of the gallery and music
|
||||
apps. RetroArch writes none. A run that finds one already there leaves it,
|
||||
and `--check` and `--dry-run` write nothing.
|
||||
and `--check` and `--dry-run` write nothing. The RetroArch pack, unzipped on
|
||||
Android as on other systems, carries the same empty `.nomedia` beside
|
||||
`README.txt`. A pack built for a target other than Android does not.
|
||||
|
||||
## On a handheld { #on-a-handheld-the-retrobios-port }
|
||||
|
||||
|
||||
Reference in new issue
Block a user