tis-microsoft-photos-dlc-main icon

Photos Add-on

Silent install package for Photos Add-on

2021.39122.10110.0-65

  • package: tis-microsoft-photos-dlc-main
  • name: Photos Add-on
  • version: 2021.39122.10110.0-65
  • categories: Media
  • maintainer: WAPT Team,Tranquil IT,Jimmy PELÉ,Pierre COSSON
  • licence: proprietary_free,wapt_public
  • locale: all
  • target_os: windows
  • impacted_process: Photos.DLC.Main
  • architecture: x64,arm
  • signature_date:
  • size: 514.02 Mo
  • installed_size: 276.46 Mo

package           : tis-microsoft-photos-dlc-main
version           : 2021.39122.10110.0-65
architecture      : x64,arm
section           : base
priority          : optional
name              : Photos Add-on
categories        : Media
maintainer        : WAPT Team,Tranquil IT,Jimmy PELÉ,Pierre COSSON
description       : In Microsoft Photos, now you can create your own videos from photos and video clips, and use themes, text, filters, and even 3D effects to tell your story with style
depends           : 
conflicts         : 
maturity          : PROD
locale            : all
target_os         : windows
min_wapt_version  : 2.4
sources           : https://apps.microsoft.com/store/detail/9PGGJ4LF6SPV
installed_size    : 276455989
impacted_process  : Photos.DLC.Main
description_fr    : Dans Microsoft Photos, vous pouvez désormais créer vos propres vidéos à partir de photos et de clips vidéo, et utiliser des thèmes, du texte, des filtres et même des effets 3D pour raconter votre histoire avec style
description_pl    : W aplikacji Microsoft Photos można teraz tworzyć własne filmy wideo ze zdjęć i klipów wideo oraz używać motywów, tekstu, filtrów, a nawet efektów 3D, aby opowiedzieć swoją historię ze stylem
description_de    : In Microsoft Fotos können Sie jetzt Ihre eigenen Videos aus Fotos und Videoclips erstellen und Themen, Text, Filter und sogar 3D-Effekte verwenden, um Ihre Geschichte mit Stil zu erzählen
description_es    : En Microsoft Fotos, ahora puedes crear tus propios vídeos a partir de fotos y videoclips, y utilizar temas, texto, filtros e incluso efectos 3D para contar tu historia con estilo
description_pt    : No Microsoft Photos, agora pode criar os seus próprios vídeos a partir de fotografias e clips de vídeo e utilizar temas, texto, filtros e até efeitos 3D para contar a sua história com estilo
description_it    : In Microsoft Photos, ora è possibile creare i propri video da foto e videoclip e utilizzare temi, testi, filtri e persino effetti 3D per raccontare la propria storia con stile
description_nl    : In Microsoft Foto's kun je nu je eigen video's maken van foto's en videoclips en thema's, tekst, filters en zelfs 3D-effecten gebruiken om je verhaal in stijl te vertellen
description_ru    : В Microsoft Photos теперь можно создавать собственные видеоролики из фотографий и видеоклипов, использовать темы, текст, фильтры и даже 3D-эффекты, чтобы стильно рассказать свою историю
audit_schedule    : 
editor            : 
keywords          : 
licence           : proprietary_free,wapt_public
homepage          : 
package_uuid      : 8a926749-c41a-42c7-b662-0d0bc53f5c67
valid_from        : 
valid_until       : 
forced_install_on : 
changelog         : 
min_os_version    : 10.0.15063
max_os_version    : 
icon_sha256sum    : 03c7e27fb44ef99dd8440cfd18e881e272766fdc4cedd1c7b2ba09076713d771
signer            : Tranquil IT
signer_fingerprint: 8c5127a75392be9cc9afd0dbae1222a673072c308c14d88ab246e23832e8c6bb
signature         : LDkclrSYoP15TH4zJuo9W0H0ptZ/4OKz9E/oaewUMNef3B1T2CHNRo+nmO94jIzfB4LxSbtQWCyJMaRgiLdL0QQC2MtcS7ym5nsdmOo6hhhpzJF/cLGLRQRXBVLNkn8C1CneyZ/LZXE/Fxe+HQP9Vp99rcrkzzpEO17zgUkE0scVwo3DgfpX2VST5BR/n9Ebf5h7ffKJZ8szqTPqsqPTYlTuMNE8Idbs3IioQRuVFBslaqcZ5KWx52Y/5w1LU0j4RkeXYX6PmlvmmFOyelyURrXfgkrw9zqvb/zM0ELH0FapjVUa0ez5VByqzVi3oGjVJ6L5KZD4THMACFUuE8fCJQ==
signature_date    : 2024-05-05T10:02:41.604037
signed_attributes : package,version,architecture,section,priority,name,categories,maintainer,description,depends,conflicts,maturity,locale,target_os,min_wapt_version,sources,installed_size,impacted_process,description_fr,description_pl,description_de,description_es,description_pt,description_it,description_nl,description_ru,audit_schedule,editor,keywords,licence,homepage,package_uuid,valid_from,valid_until,forced_install_on,changelog,min_os_version,max_os_version,icon_sha256sum,signer,signer_fingerprint,signature_date,signed_attributes

# -*- coding: utf-8 -*-
from setuphelpers import *


appx_package_name = "Microsoft.Windows.Photos.DLC.Main"
main_appx_package_name = "Microsoft.Windows.Photos"
appx_dir = makepath(programfiles, "WindowsAppsInstallers")
remove_sources = False  # remove_sources = True may prevent multi-user installations.


def install():
    # Declare local variables
    bins_dir = control.package.split("-", 1)[1]
    appx_bins_dir = makepath(appx_dir, bins_dir)

    # Remove from old path
    for appx_file in glob.glob(makepath(appx_dir, "%s_*" % appx_package_name)):
        remove_file(appx_file)
    if isdir(makepath(appx_dir, "microsoft-windows-photos-dlc-main")):
        remove_tree(makepath(appx_dir, "microsoft-windows-photos-dlc-main"))

    # Placing bins for session-setup installs
    if isdir(appx_bins_dir):
        remove_tree(appx_bins_dir)
    mkdirs(appx_bins_dir)
    print(f"Creating: {appx_bins_dir}")
    for f in glob.glob(bins_dir + "/*"):
        filecopyto(f, appx_bins_dir)
    for f in glob.glob(appx_bins_dir + "/*"):
        fname = f.split(os.sep)[-1]
        if control.architecture == "all":
            if "x86" in glob.glob(makepath(appx_bins_dir, f"{appx_package_name}_*"))[0].split(os.sep)[-1]:
                if not "x86" in fname and not "neutral" in fname:
                    remove_file(f)
            else:
                if not get_host_architecture() in fname and not "neutral" in fname:
                    remove_file(f)
        else:
            if not get_host_architecture() in fname and not "neutral" in fname:
                remove_file(f)


def session_setup():
    # Declare local variables
    bins_dir = control.package.split("-", 1)[1]
    appx_bins_dir = makepath(appx_dir, bins_dir)
    try:
        bin_path = glob.glob(makepath(appx_bins_dir, f"{appx_package_name}_*"))[0]
    except:
        error("Unable to find sources.\nPlease reinstall this package or change remove_sources to False if relevant.")
    dependencies_pathes = ",".join([f'"{a}"' for a in glob.glob(makepath(appx_bins_dir, "*")) if not appx_package_name in a])
    add_appx_cmd = f'Add-AppxPackage -Path "{bin_path}"'
    if dependencies_pathes:
        add_appx_cmd += f" -DependencyPath {dependencies_pathes}"

    # Installing the UWP application in the user environment if needed
    if main_appx_package_name:
        main_appx_package = run_powershell(f'Get-AppxPackage -Name "{main_appx_package_name}"')
        appx_dependencies = None if main_appx_package is None else main_appx_package.get("Dependencies", None)
        appx_dependency = None
        for appx in appx_dependencies:
            if not appx["IsResourcePackage"] and appx["Name"] == appx_package_name:
                appx_dependency = appx
                break
        appx_version = None if appx_dependency is None else appx_dependency.get("Version", None)
    else:
        appx_package = run_powershell(f'Get-AppxPackage -Name "{appx_package_name}"')
        appx_version = None if appx_package is None else appx_package.get("Version", None)

    if Version(appx_version) < Version(control.get_software_version()) or force:
        print(f"Installing: {bin_path.split(os.sep)[-1]}")
        killalltasks(ensure_list(control.impacted_process))
        run_powershell(add_appx_cmd, output_format="text")
    else:
        print(f"{appx_package_name} ({appx_version}) is installed and up-to-date")


def uninstall():
    # Declare local variables
    bins_dir = control.package.split("-", 1)[1]
    appx_bins_dir = makepath(appx_dir, bins_dir)

    if main_appx_package_name:
        print(f'You must uninstall: "{main_appx_package_name}" to remove this dependency.')
    else:
        print(f'You must uninstall: the "main_appx_package_name" (not specified) to remove this dependency.')

    if isdir(appx_bins_dir):
        print("Removing: %s" % (appx_bins_dir))
        remove_tree(appx_bins_dir)
    if dir_is_empty(appx_dir):
        print("Removing: %s since it is empty" % (appx_dir))
        remove_tree(appx_dir)

# -*- coding: utf-8 -*-
from setuphelpers import *
import requests
import re
import json
import os
import sys

sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
from setupdevhelpers import *
import xmltodict


def update_package():
    # Declaring local variables
    package_updated = False
    proxies = get_proxies_from_wapt_console()
    if not proxies:
        proxies = get_proxies()
    microsoft_store_url = control.sources
    if not microsoft_store_url:
        store_id = ask_input(
            control.package,
            "Enter the application ProductId or the link from the Microsoft Store (web address: https://apps.microsoft.com/store/apps)",
            microsoft_store_url,
            raise_error=True,
        ).split("/")[-1]
    else:
        store_id = microsoft_store_url.split("/")[-1]
    store_id = store_id.split("?")[0].split("#")[0].upper()
    if store_id:
        microsoft_store_url = "https://apps.microsoft.com/store/detail/%s" % store_id
        control.sources = microsoft_store_url
        control.save_control_to_wapt()
    package_prefix = control.package.split("-")[0]
    selected = None

    # check setup.py incase the package name does not match the installer file
    appx_package_name = ""
    with open("setup.py", "r", encoding="utf8") as f:
        for line in f.readlines():
            if line.startswith("appx_package_name"):
                appx_package_name = line.split("=")[1].split('"')[1]
                break
    old_appx_package_name = appx_package_name

    # Getting info from adguard api
    res = requests.post(
        "https://store.rg-adguard.net/api/GetFiles",
        "type=ProductId&url=%s&ring=RP&lang=fr-FR" % store_id,
        headers={"content-type": "application/x-www-form-urlencoded"},
        proxies=proxies,
    )
    all_files = []
    for bs_search in bs_find_all(res.text, "a"):
        if not "BlockMap" in bs_search.text and not "eappxbundle" in bs_search.text and not "emsixbundle" in bs_search.text:
            # bin_name = bs_search.text.replace("~", "_")
            bin_name = str(bs_search.text)
            if not get_file_extension(bin_name):
                continue
            version = bin_name.split("_")[1]
            download_url = bs_search["href"]
            pkg_splitted = re.split(r"_\d+\.", bin_name)[0]
            package_name = package_prefix + "-" + pkg_splitted.split("_")[0].replace(".", "-").lower()
            software_name = bin_name.split("_")[0].replace("-", " ").replace(".", " ")
            package_arch = get_uwp_filename_arch(bin_name, appx_package_name)
            file_dict = {
                "version": version,
                "bin_name": bin_name,
                "package_name": package_name,
                "software_name": software_name,
                "package_arch": package_arch,
                "download_url": download_url,
            }
            all_files.append(file_dict)
    if (
        "template-microsoft-store" in control.package
        or not appx_package_name
        or not len([a for a in all_files if appx_package_name in a["bin_name"]]) != 0
    ):
        if not all_files:
            error(f"API returned an empty list for URL: {microsoft_store_url}, please try again")
        selected = ask_grid(
            "Please select the App version that you want to package",
            all_files,
            "GRT_SELECTED",
            '{"columns":[{"propertyname":"version","datatype":"String","required":false,"readonly":false,"width":130},{"propertyname":"bin_name","datatype":"String","required":false,"readonly":false,"width":420},{"propertyname":"package_name","datatype":"String","required":false,"readonly":false,"width":190},{"propertyname":"software_name","datatype":"String","required":false,"readonly":false,"width":172},{"propertyname":"package_arch","datatype":"String","required":false,"readonly":false,"width":88},{"propertyname":"download_url","datatype":"String","required":false,"readonly":false,"width":1472}]}',
        )
        appx_package_name = selected[0]["bin_name"].split("_")[0]

    # Avoiding downloads loop and avoid keeping sources of other apps
    bins_dir = control.package.split("-", 1)[1]
    for dir_name in glob.glob("**/"):
        dir_name = dir_name.split(os.sep)[0]
        if dir_name in ["WAPT", "__pycache__"]:
            continue
        if dir_name == bins_dir:
            for f in (
                glob.glob(f"{dir_name}/*.appxbundle")
                + glob.glob(f"{dir_name}/*.msixbundle")
                + glob.glob(f"{dir_name}/*.appx")
                + glob.glob(f"{dir_name}/*.msix")
            ):
                shutil.move(f, ".")
        else:
            remove_tree(dir_name)
            if isfile("package.json"):
                remove_file("package.json")

    # Updating package.json ignored_versions list
    if isfile("package.json"):
        package_json = json_load_file("package.json")
    else:
        package_json = {"version_prefix": None, "ignored_versions": []}
    if selected:
        ignored_versions = list(
            set(
                package_json["ignored_versions"]
                + [v["version"] for v in all_files if appx_package_name in v["bin_name"] and v["version"] != selected[0]["version"]]
            )
        )
        ignored_versions.sort()
        package_json["ignored_versions"] = ignored_versions
        json_write_file("package.json", package_json)
    else:
        ignored_versions = package_json["ignored_versions"]
    version_prefix = package_json.get("version_prefix", None)

    # Downloading app files
    download_app_files(all_files, appx_package_name, proxies=proxies, ignored_versions=ignored_versions, version_prefix=version_prefix)

    # # Import xmltodict for make_uwp_app_dict()
    # if not isfile("xmltodict.py"):
    #     pip_download_xmltodict = "waptpython -m pip download xmltodict -d %s" % basedir
    #     if proxies:
    #         pip_download_xmltodict += " --proxy %s" % proxies["http"]
    #     run(pip_download_xmltodict)
    #     unzip(glob.glob("*.whl")[0], ".", "xmltodict.py")
    #     remove_file(glob.glob("*.whl")[0])

    # Keep app files
    uwp_app_dict = make_uwp_app_dict(appx_package_name)
    if not uwp_app_dict:
        # error(f'{appx_package_name} not found in "uwp_app_dict".\n\n{list(set([ua["bin_name"].split("_")[0] for ua in all_files]))}')
        error(f'"{appx_package_name}" not found in "uwp_app_dict". This is probably caused by "package_json" filters:\n{package_json}')
    newer_uwp_app = get_newer_uwp_app(uwp_app_dict)
    version = newer_uwp_app["Version"]

    # Downloading dependency files
    dependencies_to_download = []
    if newer_uwp_app["Dependencies"]:
        for dependency in newer_uwp_app["Dependencies"]:
            dependencies_to_download.append(get_newer_uwp_depency(dependency, all_files, min_version=dependency["MinVersion"]))
    if dependencies_to_download:
        for dependency_file in all_files:
            latest_bin = dependency_file["bin_name"]
            if not True in [d in latest_bin for d in dependencies_to_download]:  # ignore unecessary dependencies
                continue
            if not "all" in ensure_list(control.architecture):
                if not get_uwp_filename_arch(latest_bin) in ensure_list(control.architecture) and not "neutral" in latest_bin:
                    if isfile(latest_bin):
                        remove_file(latest_bin)
                    continue
            if latest_bin.split(".")[-1].lower().startswith("e"):  # ignore encrypted uwp apps
                continue
            download_url = dependency_file["download_url"]
            if download_url.split("/")[2].endswith("microsoft.com"):
                if not isfile(latest_bin):
                    print("Downloading: %s" % latest_bin)
                    wget(download_url, latest_bin, proxies=proxies)
                else:
                    print("Binary is present: %s" % latest_bin)
            else:
                error("ERROR: The retrieved URL will not download from microsoft's servers")

    # Applying or asking control informations once
    if not old_appx_package_name or old_appx_package_name != appx_package_name:
        # control.impacted_process and control.installed_size
        if newer_uwp_app.get("b4:Dependencies", None):
            if len(newer_uwp_app["b4:Dependencies"]) > 1:
                appx_file = sorted(
                    newer_uwp_app["b4:Dependencies"],
                    key=lambda arch: ["neutral", "x64", "x86", "arm64", "arm"].index(arch.get("Architecture")),
                )[0]["FileName"]
            else:
                appx_file = newer_uwp_app["b4:Dependencies"][0]["FileName"]
            appx_dir = appx_file.rsplit(".", 1)[0]
            unzip(newer_uwp_app["FileName"], ".", appx_file)
            unzip(appx_file)
        else:
            appx_dir = newer_uwp_app["FileName"].rsplit(".", 1)[0]
            unzip(newer_uwp_app["FileName"])
        complete_control_impacted_process(control, appx_dir)
        control.installed_size = get_size(appx_dir)
        if isdir(appx_dir):
            remove_tree(appx_dir)

        # No dependencies since it will be contained
        control.depends = ""
        control.save_control_to_wapt()

        # control.architecture
        control.architecture = get_uwp_architectures(newer_uwp_app)
        if "," in control.architecture:
            complete_control_min_wapt_version(control, "2.4", True)

        # control.min_os_version and control.max_os_version
        set_control_os_version_uwp_app(newer_uwp_app)

        # control.name
        control_name = bs_find(microsoft_store_url + "?gl=US&hl=en-US", "meta", "property", "og:title", allow_redirects=True, proxies=proxies)
        if control_name:
            control_name = control_name["content"]
        else:
            control_name = control.name
        control_name = control_name.split(" - Official app in the Microsoft Store")[0].split(" - Microsoft Apps")[0]
        control_name = complete_control_name(control, control_name)

        # control.package
        if control_name:
            control_package = package_prefix + "-" + control_name.lower()
        else:
            control_package = control.package
        complete_control_package(control, control_package, params.get("running_as_luti"), True)

        # control.description
        control_description = bs_find(
            microsoft_store_url + "?gl=US&hl=en-US", "meta", "property", "og:description", allow_redirects=True, proxies=proxies
        )
        if control_description:
            control_description = control_description["content"]
        else:
            control_description = control.description

        complete_control_description(control, control_description, params.get("running_as_luti"), "update_package", 250)

        # control.description_fr = bs_find(microsoft_store_url + "?gl=FR&hl=fr-FR", "meta", "property", "og:description", allow_redirects=True, proxies=proxies)["content"]

        # control.categories
        complete_control_categories(control)

    # Updating setup.py appx_package_name variable
    new_lines = []
    with open("setup.py", "r", encoding="utf8") as f:
        for line in f.readlines():
            if line.startswith("appx_package_name"):
                line = 'appx_package_name = "%s"\n' % appx_package_name
            new_lines.append(line)
    with open("setup.py", "w", encoding="utf8", newline="\n") as f:
        f.writelines(new_lines)

    # Get icon.png
    icon_png = []
    if "b4:Dependencies" in newer_uwp_app:
        if newer_uwp_app["b4:Dependencies"]:
            temp_appx = unzip(newer_uwp_app["FileName"], ".", newer_uwp_app["b4:Dependencies"][0]["FileName"], False)
            icon_png = unzip(newer_uwp_app["b4:Dependencies"][0]["FileName"], ".", "*.targetsize-48_altform-unplated.png", False)
            if not icon_png:
                icon_png = unzip(newer_uwp_app["b4:Dependencies"][0]["FileName"], ".", "*.targetsize-48.png", False)
            if not icon_png:
                icon_png = unzip(newer_uwp_app["b4:Dependencies"][0]["FileName"], ".", "**/PkgSmallLogo.png", False)
            if icon_png:
                shutil.move(icon_png[0], "WAPT\\icon.png")
            for f in temp_appx:
                if isfile(f):
                    remove_file(f)
    if not icon_png:
        icon_png = unzip(newer_uwp_app["FileName"], ".", "*.targetsize-48_altform-unplated.png", False)
        if not icon_png:
            icon_png = unzip(newer_uwp_app["FileName"], ".", "*.targetsize-48.png", False)
        if icon_png:
            shutil.move(icon_png[0], "WAPT\\icon.png")
    [remove_file(a) for a in glob.glob("*.png")]

    # Placing binaries in a dir ["appxbundle", "msixbundle", "appx", "msix]
    for f in glob.glob("*.part"):
        remove_file(f)
    bins_dir = control.package.split("-", 1)[1]
    if isdir(bins_dir):
        remove_tree(bins_dir)
    mkdirs(bins_dir)
    for uwp_file in glob.glob(f'{newer_uwp_app["Name"]}*{newer_uwp_app["Version"]}*.*'):
        if not isfile(makepath(bins_dir, uwp_file)):
            shutil.move(uwp_file, bins_dir)
    if uwp_app_dict[newer_uwp_app["FileName"]]["Dependencies"] is not None:
        for l in [glob.glob(a["Name"] + "_*") for a in uwp_app_dict[newer_uwp_app["FileName"]]["Dependencies"]]:
            for f in l:
                if not isfile(makepath(bins_dir, f)):
                    shutil.move(f, bins_dir)

    # Removing remaining files ["appxbundle", "msixbundle", "appx", "msix]
    for f in glob.glob("*.appxbundle") + glob.glob("*.msixbundle") + glob.glob("*.appx") + glob.glob("*.msix"):
        remove_file(f)

    # Changing version of the package and validating update-package-sources
    return complete_control_version(control, version)


def json_write_file(json_file, data, indent=4, sort_keys=False, encoding="utf-8", newline="\n"):
    """
    Write dictionary to a JSON file.

    Args:
        json_file (str): Path to the JSON file.
        data (dict): Dictionary content.
        indent (int or str): Tabulation size for indentation; default: 4 spaces.
        sort_keys (bool): Sort the keys alphabetically or not; default: False.
        encoding (str): File encoding; default: 'utf-8'.
        newline (str): Newline character(s) to use; default: 'LF'.
    """
    with open(json_file, "w", encoding=encoding, newline=newline) as write_file:
        json.dump(data, write_file, sort_keys=sort_keys, indent=indent)


def download_app_files(all_files, appx_package_name, proxies=None, ignored_versions=[], version_prefix=None):
    for app_file in all_files:
        latest_bin = app_file["bin_name"]
        if not latest_bin.startswith(f"{appx_package_name}_"):  # ignore other files
            continue
        if latest_bin.split(".")[-1].lower().startswith("e"):  # ignore encrypted UWP apps
            continue
        if version_prefix is not None and not Version(app_file["version"], len(version_prefix.split("."))) == Version(
            version_prefix, len(version_prefix.split("."))
        ):
            continue
        if any(
            Version(app_file["version"], len(ignored_version.split("."))) == Version(ignored_version, len(ignored_version.split(".")))
            for ignored_version in ignored_versions
        ):
            continue
        download_url = app_file["download_url"]
        if download_url.split("/")[2].endswith("microsoft.com"):
            if not isfile(latest_bin):
                print("Downloading: %s" % latest_bin)
                wget(download_url, latest_bin, proxies=proxies)
            else:
                print("Binary is present: %s" % latest_bin)
        else:
            error("ERROR: The retrieved URL will not download from Microsoft's servers")


def get_uwp_architectures(newer_uwp_app):
    """
    Returns a comma-separated string of compatible architectures extracted from the 'control.architecture'
    within the provided 'newer_uwp_app' dictionary.

    Args:
        newer_uwp_app (dict): A dictionary representing a Universal Windows Platform (UWP) app.

    Returns:
        str: A comma-separated string of compatible architectures (e.g., "x64,arm64,x86,arm"). If no
        compatible architectures are found, it returns "all".
    """
    architecture_list = []

    if newer_uwp_app.get("b4:Dependencies", None):
        for before_dependency in newer_uwp_app.get("b4:Dependencies"):
            architecture_list.append(before_dependency["Architecture"])
        sorted_architecture_list = sorted(architecture_list, key=lambda arch: ["neutral", "x64", "x86", "arm64", "arm"].index(arch))
        control_architecture = ",".join(sorted_architecture_list)

        if control_architecture.startswith("x64,x86") or "neutral" in control_architecture:
            control_architecture = "all"

        return control_architecture
    else:
        return get_uwp_filename_arch(newer_uwp_app["FileName"], newer_uwp_app["Name"])


def get_uwp_filename_arch(appx_filename, appx_package_name=None):
    """
    Returns the architecture of a Universal Windows Platform (UWP) app based on the provided 'appx_filename'
    and optionally, the 'appx_package_name'.

    Args:
        appx_filename (str): The filename of the UWP app package.
        appx_package_name (str, optional): The package name of the UWP app. Defaults to None.

    Returns:
        str: The architecture of the UWP app, which can be "x64", "arm64", "x86", or "all" (if no specific
        architecture is detected).
    """
    if not appx_package_name:
        appx_package_name = None
    if len(glob.glob(f'{appx_package_name}*{appx_filename.split("_")[1]}*')) > 1:
        pass
    elif "arm64" in appx_filename:
        return "arm64"
    elif "arm" in appx_filename:
        return "arm"
    elif "x64" in appx_filename:
        return "x64"
    if appx_filename is not None and "x86" in appx_filename and (appx_package_name is None or not appx_package_name in appx_filename):
        return "x86"

    return "all"


def get_newer_uwp_app(uwp_app_dict, version_prefix=None, min_version=None, max_version=None, tdf_list=["Windows.Universal", "Windows.Desktop"]):
    """Get the newer Universal Windows Platform (UWP) app from a dictionary of UWP apps based on specified criteria.

    Args:
        uwp_app_dict (dict): A dictionary containing UWP app information with app IDs as keys and app details as values.
        version_prefix (str, optional): The prefix of the version number to filter the apps. Defaults to None.
        min_version (str, optional): The minimum version of the UWP app. Defaults to None.
        max_version (str, optional): The maximum version of the UWP app. Defaults to None.
        tdf_list (list, optional): A list of target device families to filter the apps. Defaults to ["Windows.Universal", "Windows.Desktop"].

    Returns:
        dict or None: The dictionary containing the details of the newer UWP app that matches the specified criteria, or None if no app is found.

    """
    newer_uwp_app = None
    newer_version = "0"

    for uwp_app in uwp_app_dict.values():
        to_skip = False  # Initialize to_skip as False and set to True only if any condition fails

        # Check version prefix
        if version_prefix is not None and not Version(uwp_app["Version"], len(version_prefix.split("."))) == Version(
            version_prefix, len(version_prefix.split("."))
        ):
            to_skip = True
            continue

        # Check target device family
        if tdf_list is not None and uwp_app["TargetDeviceFamily"]:
            for tdf in tdf_list:
                for uwp_app_tdf in uwp_app["TargetDeviceFamily"]:
                    if (
                        tdf == uwp_app_tdf["Name"]
                        and (min_version is None or Version(min_version, 3) == Version(uwp_app_tdf["MinVersion"], 3))
                        and (max_version is None or Version(max_version, 3) == Version(uwp_app_tdf["MaxVersionTested"], 3))
                    ):
                        break
                else:
                    continue
                break
            else:
                to_skip = True

        # Check max OS version
        if control.max_os_version and not Version(uwp_app["MinVersion"], 3) < Version(control.max_os_version, 3):
            to_skip = True

        if to_skip:
            continue

        # Update the newer UWP app if the current app has a higher version
        if Version(newer_version) < Version(uwp_app["Version"]):
            newer_uwp_app = uwp_app
            newer_version = newer_uwp_app["Version"]

    return newer_uwp_app


def get_newer_uwp_depency(dependency_dict, all_files_dict, version_prefix=None, min_version=None):
    """Returns a list of the bin_name of the latest required dependencies"""
    newer_version = "0"

    for uwp_app in all_files_dict:
        if not uwp_app["bin_name"].startswith(f'{dependency_dict["Name"]}_'):
            continue
        if version_prefix is not None and Version(uwp_app["version"], len(version_prefix.split("."))) != Version(
            version_prefix, len(version_prefix.split("."))
        ):
            continue

        if Version(newer_version) < Version(uwp_app["version"]):
            newer_uwp_depency = uwp_app
            newer_version = uwp_app["version"]

    return newer_uwp_depency["bin_name"].split(newer_version + "_")[0] + newer_version + "_"


def make_uwp_app_dict(appx_package_name):
    ms_app_db = {}
    # for ms_app_file in (
    #     glob.glob("*.appxbundle")
    #     + glob.glob("*.msixbundle")
    #     + glob.glob("*.appx")
    #     + glob.glob("*.msix")
    # ):
    for ms_app_file in (
        glob.glob(f"{appx_package_name}*.appxbundle")
        + glob.glob(f"{appx_package_name}*.msixbundle")
        + glob.glob(f"{appx_package_name}*.appx")
        + glob.glob(f"{appx_package_name}*.msix")
    ):
        data_dict = {}
        before_dependencies = []
        sub_dependencies = []
        dependency_data_dict = None

        manifest = unzip(ms_app_file, ".", "AppxManifest.xml", False)
        bundle_manifest = unzip(ms_app_file, ".", "AppxMetadata\\AppxBundleManifest.xml", False)
        ms_app_info = {
            "FileName": ms_app_file,
        }

        if not manifest:
            if bundle_manifest:
                with open(bundle_manifest[0], encoding="utf8") as xml_file:
                    data_dict = xmltodict.parse(xml_file.read(), attr_prefix="")

                if type(data_dict["Bundle"]["Packages"]["Package"]) == dict:
                    data_dict["Bundle"]["Packages"]["Package"] = [dict(data_dict["Bundle"]["Packages"]["Package"])]

                for app_pkg in list(data_dict["Bundle"]["Packages"]["Package"]):
                    if app_pkg["Type"] == "application":
                        before_dependency_info = {
                            "FileName": app_pkg["FileName"],
                            "Version": app_pkg["Version"],
                            "Architecture": app_pkg["Architecture"],
                        }
                        before_dependencies.append(before_dependency_info)

                if before_dependencies:
                    for dependency in before_dependencies:
                        unzip(
                            ms_app_file,
                            ".",
                            makepath(
                                dependency["FileName"],
                            ),
                            False,
                        )
                        manifest = unzip(dependency["FileName"], ".", makepath("AppxManifest.xml"), False)
                        if isfile(dependency["FileName"]):
                            remove_file(dependency["FileName"])
                        with open(manifest[0], encoding="utf8") as xml_file:
                            dependency_data_dict = xmltodict.parse(xml_file.read(), attr_prefix="")

                    if dependency_data_dict["Package"].get("Dependencies"):
                        if dependency_data_dict["Package"]["Dependencies"].get("PackageDependency"):
                            sub_dependencies = list(dependency_data_dict["Package"]["Dependencies"]["PackageDependency"])
                            if type(dependency_data_dict["Package"]["Dependencies"]["PackageDependency"]) == dict:
                                sub_dependencies = [dict(dependency_data_dict["Package"]["Dependencies"]["PackageDependency"])]
                            if type(dependency_data_dict["Package"]["Dependencies"]["PackageDependency"]) == list:
                                sub_dependencies = dependency_data_dict["Package"]["Dependencies"]["PackageDependency"]
                        if dependency_data_dict["Package"]["Dependencies"].get("TargetDeviceFamily"):
                            if not "TargetDeviceFamily" in ms_app_info:
                                if type(dependency_data_dict["Package"]["Dependencies"]["TargetDeviceFamily"]) == dict:
                                    ms_app_info["TargetDeviceFamily"] = [dict(dependency_data_dict["Package"]["Dependencies"]["TargetDeviceFamily"])]
                                if type(dependency_data_dict["Package"]["Dependencies"]["TargetDeviceFamily"]) == list:
                                    ms_app_info["TargetDeviceFamily"] = dependency_data_dict["Package"]["Dependencies"]["TargetDeviceFamily"]
            else:
                error("nothing to parse")
        else:
            manifest = unzip(ms_app_file, ".", makepath("AppxManifest.xml"), False)
            with open(manifest[0], encoding="utf8") as xml_file:
                dependency_data_dict = xmltodict.parse(xml_file.read(), attr_prefix="")

        if dependency_data_dict["Package"].get("Dependencies"):
            if "PackageDependency" in dependency_data_dict["Package"]["Dependencies"]:
                sub_dependencies = list(dependency_data_dict["Package"]["Dependencies"]["PackageDependency"])
                if type(dependency_data_dict["Package"]["Dependencies"]["PackageDependency"]) == dict:
                    sub_dependencies = [dict(dependency_data_dict["Package"]["Dependencies"]["PackageDependency"])]
                if type(dependency_data_dict["Package"]["Dependencies"]["PackageDependency"]) == list:
                    sub_dependencies = dependency_data_dict["Package"]["Dependencies"]["PackageDependency"]
            if "TargetDeviceFamily" in dependency_data_dict["Package"]["Dependencies"]:
                if not "TargetDeviceFamily" in ms_app_info:
                    if type(dependency_data_dict["Package"]["Dependencies"]["TargetDeviceFamily"]) == dict:
                        ms_app_info["TargetDeviceFamily"] = [dict(dependency_data_dict["Package"]["Dependencies"]["TargetDeviceFamily"])]
                    if type(dependency_data_dict["Package"]["Dependencies"]["TargetDeviceFamily"]) == list:
                        ms_app_info["TargetDeviceFamily"] = dependency_data_dict["Package"]["Dependencies"]["TargetDeviceFamily"]
        else:
            ms_app_info.update({"Dependencies": None})

        if ms_app_info.get("TargetDeviceFamily"):
            ms_app_info.update(
                {
                    "MinVersion": ms_app_info["TargetDeviceFamily"][0]["MinVersion"],
                    "MaxVersionTested": ms_app_info["TargetDeviceFamily"][0]["MaxVersionTested"],
                }
            )
        elif dependency_data_dict is not None and dependency_data_dict["Package"].get("Prerequisites"):
            ms_app_info.update(
                {
                    "MinVersion": dependency_data_dict["Package"]["Prerequisites"]["OSMinVersion"],
                    "MaxVersionTested": dependency_data_dict["Package"]["Prerequisites"]["OSMaxVersionTested"],
                }
            )
        else:
            ms_app_info.update({"TargetDeviceFamily": None})

        if data_dict:
            ms_app_info.update(data_dict["Bundle"]["Identity"])
        else:
            ms_app_info.update(dependency_data_dict["Package"]["Identity"])
        if "ProcessorArchitecture" in ms_app_info:
            ms_app_info.update({"Architecture": ms_app_info["ProcessorArchitecture"]})
        if before_dependencies:
            ms_app_info["b4:Dependencies"] = before_dependencies
        else:
            ms_app_info.update({"b4:Dependencies": None})

        if sub_dependencies:
            ms_app_info["Dependencies"] = sub_dependencies
        else:
            ms_app_info.update({"Dependencies": None})

        ms_app_db[ms_app_file] = ms_app_info

        for xml_file in glob.glob("AppxManifest.xml") + glob.glob("AppxBundleManifest.xml"):
            remove_file(xml_file)

    return ms_app_db


def set_control_os_version_uwp_app(
    newer_uwp_app,
    force_values=False,
    priority_windows=WindowsVersions.Windows11,
    eol_windows=WindowsVersions.Windows10v2004,
    silent=False,
):
    if newer_uwp_app.get("MinVersion") and newer_uwp_app.get("MaxVersionTested"):
        control.min_os_version = ".".join(
            newer_uwp_app["MinVersion"].split(".")[: 2 if ".".join(newer_uwp_app["MinVersion"].split(".")[:3]).endswith("0") else 3]
        )
        # MaxVersionTested value seems not reliable, now asking to change control.max_os_version
        if not Version(newer_uwp_app["MaxVersionTested"], 3) <= eol_windows:  # Ignore end-of-life Windows versions
            if control.max_os_version != ".".join(newer_uwp_app["MaxVersionTested"].split(".")[:3]):
                if force_values:
                    control.max_os_version = ".".join(newer_uwp_app["MaxVersionTested"].split(".")[:3])
                else:
                    if not silent:
                        control_max_os_version = ask_input(
                            control.package,
                            f'INFO: You may wanna change "control.max_os_version" by "{".".join(newer_uwp_app["MaxVersionTested"].split(".")[:3])}" or "{newer_uwp_app["MaxVersionTested"]}"',
                            "",
                            raise_error=False,
                        )
                    if control_max_os_version or control_max_os_version == "":
                        control.max_os_version = control_max_os_version
                    else:
                        print(
                            f'INFO: You may wanna change "control.max_os_version" by "{".".join(newer_uwp_app["MaxVersionTested"].split(".")[:3])}"'
                        )
        else:
            if control.max_os_version != "":
                if force_values:
                    control.max_os_version = ""
                else:
                    if not silent:
                        control_max_os_version = ask_message(
                            control.package,
                            f'INFO: You may wanna erase "control.max_os_version    : {control.max_os_version}"',
                            3 + 32,
                            raise_error=False,
                        )
                    if control_max_os_version == 6:
                        control.max_os_version = ""
    control.save_control_to_wapt()

373656fafe3a275cc305bef69c57a1b5724034f427768a19adbfef710c42afa0 : setup.py
74abc996e1b04b842b983b9bd31c504938a27f4ee3193a161a81a13eb9897328 : __pycache__/setupdevhelpers.cpython-38.pyc
46cef52775782297be1cbfb16cff198574fc94d35afd4a67ece0908ac37c5387 : __pycache__/xmltodict.cpython-38.pyc
2ec8874c3a300ac076e2ced2ac78451d507d403d08ab0344cf39c0d0d20be819 : update_package.py
abb31038c61ded480ad2b251ca83b6058cc3c208d8f38a50663cef26fd7531fc : package.json
2c422523ff693689a84c109585cfa444143ac3b6b7a5cadf4858afc6a3cb750f : microsoft-photos-dlc-main/Microsoft.VCLibs.140.00_14.0.33519.0_arm__8wekyb3d8bbwe.appx
7cec03f6273d34be0860f3c3408491244889b23a41b43c4c51236ffbb3fd2c90 : microsoft-photos-dlc-main/Microsoft.Windows.Photos.DLC.Main_2021.39122.10110.0_neutral_~_8wekyb3d8bbwe.appxbundle
9c17b521f9d690a1f504da5108ed6eec5669eb3a8fd1331eef43e40d84e74283 : microsoft-photos-dlc-main/Microsoft.VCLibs.140.00_14.0.33519.0_x64__8wekyb3d8bbwe.appx
03c7e27fb44ef99dd8440cfd18e881e272766fdc4cedd1c7b2ba09076713d771 : WAPT/icon.png
a5a97261381e1d0ad46ee15916abec9c2631d0201f5cc50ceb0197a165a0bbbf : WAPT/certificate.crt
b0d14b867d5df755bbccd70be19f6282f0f5b6b526df62f405917e1879de1c24 : WAPT/changelog.txt
364e01205533a0c6907fb9e8a8f70353985ae81ead35b71aade9a4253184f847 : xmltodict.py
c802d056a635d7c09e516c827ce4f49551b98844183adf0aa5896ea5bdfd2a03 : setupdevhelpers.py
f3ce8c68eda75c8239d23e5632b57e8b9457a301b5bbfc89c3b6c8cf94a12678 : luti.json
ce000c8b93feac2454f55a016afee6a4a7e9394376f253b2a2c28f1e04fea4ef : WAPT/control

0-32
===
Now includes dependencies, so the tis-template-microsoft-store-dependency part is no longer needed
Auto update icon.png
Auto update control.min_os_version
Smarter popup to ask for appx_package_name
handle "Windows.Desktop" and "Windows.Universal" TargetDeviceFamily
removes encrypted uwp apps
Auto update control.max_os_version if relevant ; max_os_version fiabilized ; Ignore end-of-life Windows versions by default


0-25
===
fix rare case of incorrect glob.glob
better handling of binary suppression


0-24
===
fix silent mode
more information shown in grid_dialog
update_package() was inccorect for some architecture
prefix was forced to tis
now using full AppxPackage Name for dependencies
better handling of remove_outdated_binaries and add_depends
improve used regex
using functions for some popups, functions return associated str
More default actions on popups
Fix update_package for 2.3 with setupdevhelpers import
Fix package name change, and depencies warning not to block luti build
Warning the end-user that dependencies are required and providing instructions on how to acquire and deploy them


0-12
===
Asking for categories
Asking for description
Adding dependencies
Changing setup.py appx_package variable
Smart update_package() it do not popup anything to download updates
Microsoft_Store_Fluent_Design_icon
remove_outdated_binaries handle extensions
Removing template files when used
allow_remove option
specify arch
min_wapt_version  : 2.2
ask to change package_name
fix re.split
fix multi download on neutral apps
only ask once for description