Merge remote-tracking branch 'origin/main' into dev

This commit is contained in:
Imran Remtulla
2025-05-19 15:51:46 -04:00
28 changed files with 172 additions and 137 deletions

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Crowdsourced App Configurations", "crowdsourcedConfigsShort": "Crowdsourced App Configurations",
"allowInsecure": "Allow insecure HTTP requests", "allowInsecure": "Allow insecure HTTP requests",
"stayOneVersionBehind": "Stay one version behind latest", "stayOneVersionBehind": "Stay one version behind latest",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Refresh app details before download", "refreshBeforeDownload": "Refresh app details before download",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Name", "name": "Name",
"smartname": "Name (Smart)", "smartname": "Name (Smart)",
"sortMethod": "Sort Method", "sortMethod": "Sort Method",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Configuració de les aplicacions crowdsourcing", "crowdsourcedConfigsShort": "Configuració de les aplicacions crowdsourcing",
"allowInsecure": "Permet les sol·licituds HTTP insegures", "allowInsecure": "Permet les sol·licituds HTTP insegures",
"stayOneVersionBehind": "Roman a la versió anterior a l'última", "stayOneVersionBehind": "Roman a la versió anterior a l'última",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Actualitza les dades de l'aplicació abans de descarregar-la", "refreshBeforeDownload": "Actualitza les dades de l'aplicació abans de descarregar-la",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Nom", "name": "Nom",
"smartname": "Nom (smart)", "smartname": "Nom (smart)",
"sortMethod": "Mètode d'ordenació", "sortMethod": "Mètode d'ordenació",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Konfigurace aplikací s využitím crowdsourcingu", "crowdsourcedConfigsShort": "Konfigurace aplikací s využitím crowdsourcingu",
"allowInsecure": "Povolení nezabezpečených požadavků HTTP", "allowInsecure": "Povolení nezabezpečených požadavků HTTP",
"stayOneVersionBehind": "Zůstaňte o jednu verzi pozadu za nejnovější", "stayOneVersionBehind": "Zůstaňte o jednu verzi pozadu za nejnovější",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Obnovení údajů o aplikaci před stažením", "refreshBeforeDownload": "Obnovení údajů o aplikaci před stažením",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Název", "name": "Název",
"smartname": "Název (Smart)", "smartname": "Název (Smart)",
"sortMethod": "Metoda třídění", "sortMethod": "Metoda třídění",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Crowdsourcede app-konfigurationer", "crowdsourcedConfigsShort": "Crowdsourcede app-konfigurationer",
"allowInsecure": "Tillad usikre HTTP-anmodninger", "allowInsecure": "Tillad usikre HTTP-anmodninger",
"stayOneVersionBehind": "Forbliv én version bagud den seneste", "stayOneVersionBehind": "Forbliv én version bagud den seneste",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Opdater app-detaljer før download", "refreshBeforeDownload": "Opdater app-detaljer før download",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Navn", "name": "Navn",
"smartname": "Navn (Smart)", "smartname": "Navn (Smart)",
"sortMethod": "Sorteringsmetode", "sortMethod": "Sorteringsmetode",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Crowdsourced App-Konfigurationen", "crowdsourcedConfigsShort": "Crowdsourced App-Konfigurationen",
"allowInsecure": "Unsichere HTTP-Anfragen zulassen", "allowInsecure": "Unsichere HTTP-Anfragen zulassen",
"stayOneVersionBehind": "Eine Version hinter der neuesten Version bleiben", "stayOneVersionBehind": "Eine Version hinter der neuesten Version bleiben",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "App-Details vor dem Download aktualisieren", "refreshBeforeDownload": "App-Details vor dem Download aktualisieren",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Name", "name": "Name",
"smartname": "Name (Smart)", "smartname": "Name (Smart)",
"sortMethod": "Sortierverfahren", "sortMethod": "Sortierverfahren",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Crowdsourced App Configurations", "crowdsourcedConfigsShort": "Crowdsourced App Configurations",
"allowInsecure": "Allow insecure HTTP requests", "allowInsecure": "Allow insecure HTTP requests",
"stayOneVersionBehind": "Stay one version behind latest", "stayOneVersionBehind": "Stay one version behind latest",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Refresh app details before download", "refreshBeforeDownload": "Refresh app details before download",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Name", "name": "Name",
"smartname": "Name (Smart)", "smartname": "Name (Smart)",
"sortMethod": "Sort Method", "sortMethod": "Sort Method",

View File

@ -318,6 +318,7 @@
"crowdsourcedConfigsShort": "Crowdsourced app configurations", "crowdsourcedConfigsShort": "Crowdsourced app configurations",
"allowInsecure": "Allow insecure HTTP requests", "allowInsecure": "Allow insecure HTTP requests",
"stayOneVersionBehind": "Stay one version behind latest", "stayOneVersionBehind": "Stay one version behind latest",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Refresh app details before download", "refreshBeforeDownload": "Refresh app details before download",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Configuración de aplicaciones por crowdsourcing", "crowdsourcedConfigsShort": "Configuración de aplicaciones por crowdsourcing",
"allowInsecure": "Permitir peticiones HTTP inseguras", "allowInsecure": "Permitir peticiones HTTP inseguras",
"stayOneVersionBehind": "Mantenerse una versión por detrás de la última", "stayOneVersionBehind": "Mantenerse una versión por detrás de la última",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Actualiza los datos de la aplicación antes de descargarla", "refreshBeforeDownload": "Actualiza los datos de la aplicación antes de descargarla",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Nombre", "name": "Nombre",
"smartname": "Nombre (Smart)", "smartname": "Nombre (Smart)",
"sortMethod": "Método de clasificación", "sortMethod": "Método de clasificación",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "تنظیمات برنامه های مشارکت جمعی", "crowdsourcedConfigsShort": "تنظیمات برنامه های مشارکت جمعی",
"allowInsecure": "درخواست های HTTP ناامن را مجاز کنید", "allowInsecure": "درخواست های HTTP ناامن را مجاز کنید",
"stayOneVersionBehind": "یک نسخه از آخرین نسخه پشت سر بگذارید", "stayOneVersionBehind": "یک نسخه از آخرین نسخه پشت سر بگذارید",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "قبل از دانلود، جزئیات برنامه را بازخوانی کنید", "refreshBeforeDownload": "قبل از دانلود، جزئیات برنامه را بازخوانی کنید",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Name", "name": "Name",
"smartname": "Name (Smart)", "smartname": "Name (Smart)",
"sortMethod": "Sort Method", "sortMethod": "Sort Method",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Applications communautaires", "crowdsourcedConfigsShort": "Applications communautaires",
"allowInsecure": "Autoriser les requêtes HTTP non sécurisées", "allowInsecure": "Autoriser les requêtes HTTP non sécurisées",
"stayOneVersionBehind": "Rester une version en arrière de la dernière", "stayOneVersionBehind": "Rester une version en arrière de la dernière",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Actualiser les détails de l'application avant de la télécharger", "refreshBeforeDownload": "Actualiser les détails de l'application avant de la télécharger",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Nom", "name": "Nom",
"smartname": "Nom (Smart)", "smartname": "Nom (Smart)",
"sortMethod": "Méthode de tri", "sortMethod": "Méthode de tri",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Alkalmazáslista", "crowdsourcedConfigsShort": "Alkalmazáslista",
"allowInsecure": "Nem biztonságos HTTP-kérések engedélyezése", "allowInsecure": "Nem biztonságos HTTP-kérések engedélyezése",
"stayOneVersionBehind": "Maradjon egy verzióval a legújabb mögött", "stayOneVersionBehind": "Maradjon egy verzióval a legújabb mögött",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Az alkalmazás adatainak frissítése a letöltés előtt", "refreshBeforeDownload": "Az alkalmazás adatainak frissítése a letöltés előtt",
"tencentAppStore": "Tencent Appstore", "tencentAppStore": "Tencent Appstore",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Név", "name": "Név",
"smartname": "Név (Okos)", "smartname": "Név (Okos)",
"sortMethod": "Rendezési eljárás", "sortMethod": "Rendezési eljárás",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Konfigurasi Aplikasi Crowdsourced", "crowdsourcedConfigsShort": "Konfigurasi Aplikasi Crowdsourced",
"allowInsecure": "Izinkan permintaan HTTP yang tidak aman", "allowInsecure": "Izinkan permintaan HTTP yang tidak aman",
"stayOneVersionBehind": "Tetap satu versi di belakang versi terbaru", "stayOneVersionBehind": "Tetap satu versi di belakang versi terbaru",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Segarkan detail aplikasi sebelum mengunduh", "refreshBeforeDownload": "Segarkan detail aplikasi sebelum mengunduh",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Nama", "name": "Nama",
"smartname": "Nama (Cerdas)", "smartname": "Nama (Cerdas)",
"sortMethod": "Metode Penyortiran", "sortMethod": "Metode Penyortiran",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Configurazioni di app in crowdsourcing", "crowdsourcedConfigsShort": "Configurazioni di app in crowdsourcing",
"allowInsecure": "Consentire le richieste HTTP non sicure", "allowInsecure": "Consentire le richieste HTTP non sicure",
"stayOneVersionBehind": "Rimanere una versione indietro rispetto alla più recente", "stayOneVersionBehind": "Rimanere una versione indietro rispetto alla più recente",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Aggiornare i dettagli dell'app prima del download", "refreshBeforeDownload": "Aggiornare i dettagli dell'app prima del download",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Nome", "name": "Nome",
"smartname": "Nome (intelligente)", "smartname": "Nome (intelligente)",
"sortMethod": "Metodo di ordinamento", "sortMethod": "Metodo di ordinamento",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "クラウドソーシングによるアプリの設定", "crowdsourcedConfigsShort": "クラウドソーシングによるアプリの設定",
"allowInsecure": "安全でないHTTPリクエストを許可する", "allowInsecure": "安全でないHTTPリクエストを許可する",
"stayOneVersionBehind": "最新のバージョンから1つ前のものを使用する", "stayOneVersionBehind": "最新のバージョンから1つ前のものを使用する",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "ダウンロード前にアプリの詳細を更新する", "refreshBeforeDownload": "ダウンロード前にアプリの詳細を更新する",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "名称", "name": "名称",
"smartname": "名前(スマート)", "smartname": "名前(スマート)",
"sortMethod": "ソート方法", "sortMethod": "ソート方法",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "크라우드소싱 앱 구성", "crowdsourcedConfigsShort": "크라우드소싱 앱 구성",
"allowInsecure": "안전하지 않은 HTTP 요청 허용", "allowInsecure": "안전하지 않은 HTTP 요청 허용",
"stayOneVersionBehind": "최신 버전보다 한 버전 뒤에 머무르기", "stayOneVersionBehind": "최신 버전보다 한 버전 뒤에 머무르기",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "다운로드 전에 앱 세부 정보 새로 고침", "refreshBeforeDownload": "다운로드 전에 앱 세부 정보 새로 고침",
"tencentAppStore": "텐센트 앱 스토어", "tencentAppStore": "텐센트 앱 스토어",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "이름", "name": "이름",
"smartname": "이름(스마트)", "smartname": "이름(스마트)",
"sortMethod": "정렬 방법", "sortMethod": "정렬 방법",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "App-configuraties door menigte", "crowdsourcedConfigsShort": "App-configuraties door menigte",
"allowInsecure": "Onveilige HTTP-verzoeken toestaan", "allowInsecure": "Onveilige HTTP-verzoeken toestaan",
"stayOneVersionBehind": "Blijf een versie achter op de nieuwste", "stayOneVersionBehind": "Blijf een versie achter op de nieuwste",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Vernieuw app details voor download", "refreshBeforeDownload": "Vernieuw app details voor download",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Naam", "name": "Naam",
"smartname": "Naam (Slim)", "smartname": "Naam (Slim)",
"sortMethod": "Sorteermethode", "sortMethod": "Sorteermethode",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Baza konfiguracji", "crowdsourcedConfigsShort": "Baza konfiguracji",
"allowInsecure": "Zezwalaj na niezabezpieczone żądania HTTP", "allowInsecure": "Zezwalaj na niezabezpieczone żądania HTTP",
"stayOneVersionBehind": "Pozostań jedną wersję w tyle za najnowszą", "stayOneVersionBehind": "Pozostań jedną wersję w tyle za najnowszą",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Odśwież szczegóły aplikacji przed pobraniem", "refreshBeforeDownload": "Odśwież szczegóły aplikacji przed pobraniem",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Nazwa", "name": "Nazwa",
"smartname": "Nazwa (Smart)", "smartname": "Nazwa (Smart)",
"sortMethod": "Metoda sortowania", "sortMethod": "Metoda sortowania",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Configurações de app da comunidade", "crowdsourcedConfigsShort": "Configurações de app da comunidade",
"allowInsecure": "Permitir solicitações de HTTP inseguras", "allowInsecure": "Permitir solicitações de HTTP inseguras",
"stayOneVersionBehind": "Ficar uma versão antes da mais recente", "stayOneVersionBehind": "Ficar uma versão antes da mais recente",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Atualizar detalhes do app antes de baixar", "refreshBeforeDownload": "Atualizar detalhes do app antes de baixar",
"tencentAppStore": "Loja de Apps da Tencent", "tencentAppStore": "Loja de Apps da Tencent",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Nome", "name": "Nome",
"smartname": "Nome (inteligente)", "smartname": "Nome (inteligente)",
"sortMethod": "Método de ordenação", "sortMethod": "Método de ordenação",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Configurações de aplicações com base em crowdsourcing", "crowdsourcedConfigsShort": "Configurações de aplicações com base em crowdsourcing",
"allowInsecure": "Permitir pedidos HTTP inseguros", "allowInsecure": "Permitir pedidos HTTP inseguros",
"stayOneVersionBehind": "Manter-se uma versão atrás da mais recente", "stayOneVersionBehind": "Manter-se uma versão atrás da mais recente",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Atualizar os detalhes da aplicação antes da transferência", "refreshBeforeDownload": "Atualizar os detalhes da aplicação antes da transferência",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Nome", "name": "Nome",
"smartname": "Nome (Smart)", "smartname": "Nome (Smart)",
"sortMethod": "Método de ordenação", "sortMethod": "Método de ordenação",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Конфиги приложений с помощью краудсорсинга", "crowdsourcedConfigsShort": "Конфиги приложений с помощью краудсорсинга",
"allowInsecure": "Разрешить небезопасные HTTP-запросы", "allowInsecure": "Разрешить небезопасные HTTP-запросы",
"stayOneVersionBehind": "Не отставайте от последней версии", "stayOneVersionBehind": "Не отставайте от последней версии",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Обновляйте информацию о приложении перед загрузкой", "refreshBeforeDownload": "Обновляйте информацию о приложении перед загрузкой",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Имя", "name": "Имя",
"smartname": "Имя (умное)", "smartname": "Имя (умное)",
"sortMethod": "Метод сортировки", "sortMethod": "Метод сортировки",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Appkonfigurationer med hjälp av crowdsourcing", "crowdsourcedConfigsShort": "Appkonfigurationer med hjälp av crowdsourcing",
"allowInsecure": "Tillåt osäkra HTTP-förfrågningar", "allowInsecure": "Tillåt osäkra HTTP-förfrågningar",
"stayOneVersionBehind": "Håll dig en version bakom den senaste", "stayOneVersionBehind": "Håll dig en version bakom den senaste",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Uppdatera appdetaljerna före nedladdning", "refreshBeforeDownload": "Uppdatera appdetaljerna före nedladdning",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Namn", "name": "Namn",
"smartname": "Namn (Smart)", "smartname": "Namn (Smart)",
"sortMethod": "Sorteringsmetod", "sortMethod": "Sorteringsmetod",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Kitle Kaynaklı Uygulama Yapılandırmaları", "crowdsourcedConfigsShort": "Kitle Kaynaklı Uygulama Yapılandırmaları",
"allowInsecure": "Güvensiz HTTP isteklerine izin ver", "allowInsecure": "Güvensiz HTTP isteklerine izin ver",
"stayOneVersionBehind": "En son sürümün bir sürüm gerisinde kalın", "stayOneVersionBehind": "En son sürümün bir sürüm gerisinde kalın",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "İndirmeden önce uygulama ayrıntılarını yenileyin", "refreshBeforeDownload": "İndirmeden önce uygulama ayrıntılarını yenileyin",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "İsim", "name": "İsim",
"smartname": "İsim (Akıllı)", "smartname": "İsim (Akıllı)",
"sortMethod": "Sıralama Yöntemi", "sortMethod": "Sıralama Yöntemi",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Налаштування краудсорсингових додатків", "crowdsourcedConfigsShort": "Налаштування краудсорсингових додатків",
"allowInsecure": "Дозволити незахищені HTTP-запити", "allowInsecure": "Дозволити незахищені HTTP-запити",
"stayOneVersionBehind": "Залишайтеся на одну версію актуальнішою", "stayOneVersionBehind": "Залишайтеся на одну версію актуальнішою",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Оновіть інформацію про програму перед завантаженням", "refreshBeforeDownload": "Оновіть інформацію про програму перед завантаженням",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Ім'я", "name": "Ім'я",
"smartname": "Ім'я (Smart)", "smartname": "Ім'я (Smart)",
"sortMethod": "Метод сортування", "sortMethod": "Метод сортування",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "Crowdsourced App Configurations", "crowdsourcedConfigsShort": "Crowdsourced App Configurations",
"allowInsecure": "Allow insecure HTTP requests", "allowInsecure": "Allow insecure HTTP requests",
"stayOneVersionBehind": "Stay one version behind latest", "stayOneVersionBehind": "Stay one version behind latest",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "Refresh app details before download", "refreshBeforeDownload": "Refresh app details before download",
"tencentAppStore": "Tencent App Store", "tencentAppStore": "Tencent App Store",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "Name", "name": "Name",
"smartname": "Name (Smart)", "smartname": "Name (Smart)",
"sortMethod": "Sort Method", "sortMethod": "Sort Method",

View File

@ -318,9 +318,11 @@
"crowdsourcedConfigsShort": "群眾外包的應用程式設定", "crowdsourcedConfigsShort": "群眾外包的應用程式設定",
"allowInsecure": "允許不安全的 HTTP 請求", "allowInsecure": "允許不安全的 HTTP 請求",
"stayOneVersionBehind": "保持比最新版本落後一個版本", "stayOneVersionBehind": "保持比最新版本落後一個版本",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "下載前刷新應用程式詳細資訊", "refreshBeforeDownload": "下載前刷新應用程式詳細資訊",
"tencentAppStore": "騰訊應用寶", "tencentAppStore": "騰訊應用寶",
"coolApk": "CoolApk", "coolApk": "CoolApk",
"vivoAppStore": "vivo App Store (CN)",
"name": "名稱", "name": "名稱",
"smartname": "名稱(智慧)", "smartname": "名稱(智慧)",
"sortMethod": "排序方式", "sortMethod": "排序方式",

View File

@ -318,6 +318,7 @@
"crowdsourcedConfigsShort": "众包应用程序配置", "crowdsourcedConfigsShort": "众包应用程序配置",
"allowInsecure": "允许不安全的 HTTP 请求", "allowInsecure": "允许不安全的 HTTP 请求",
"stayOneVersionBehind": "比最新版本晚一个版本", "stayOneVersionBehind": "比最新版本晚一个版本",
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
"refreshBeforeDownload": "下载前刷新应用程序详细信息", "refreshBeforeDownload": "下载前刷新应用程序详细信息",
"tencentAppStore": "腾讯应用宝", "tencentAppStore": "腾讯应用宝",
"coolApk": "酷安", "coolApk": "酷安",

View File

@ -1,24 +1,18 @@
import 'dart:convert';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:html/parser.dart';
import 'package:obtainium/app_sources/html.dart';
import 'package:obtainium/components/generated_form.dart'; import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/custom_errors.dart'; import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart'; import 'package:obtainium/providers/source_provider.dart';
parseDateTimeMMMddCommayyyy(String? dateString) { extension Unique<E, Id> on List<E> {
DateTime? releaseDate; List<E> unique([Id Function(E element)? id, bool inplace = true]) {
try { final ids = Set();
releaseDate = dateString != null var list = inplace ? this : List<E>.from(this);
? DateFormat('MMM dd, yyyy').parse(dateString) list.retainWhere((x) => ids.add(id != null ? id(x) : x as Id));
: null; return list;
releaseDate = dateString != null && releaseDate == null
? DateFormat('MMMM dd, yyyy').parse(dateString)
: releaseDate;
} catch (err) {
// ignore
} }
return releaseDate;
} }
class APKPure extends AppSource { class APKPure extends AppSource {
@ -35,6 +29,10 @@ class APKPure extends AppSource {
[ [
GeneratedFormSwitch('stayOneVersionBehind', GeneratedFormSwitch('stayOneVersionBehind',
label: tr('stayOneVersionBehind'), defaultValue: false) label: tr('stayOneVersionBehind'), defaultValue: false)
],
[
GeneratedFormSwitch('useFirstApkOfVersion',
label: tr('useFirstApkOfVersion'), defaultValue: true)
] ]
]; ];
} }
@ -65,109 +63,73 @@ class APKPure extends AppSource {
return Uri.parse(standardUrl).pathSegments.last; return Uri.parse(standardUrl).pathSegments.last;
} }
getDetailsForVersionLink( getDetailsForVersion(
String standardUrl, List<Map<String, dynamic>> versionVariants,
String appId,
String host,
List<String> supportedArchs, List<String> supportedArchs,
String link,
Map<String, dynamic> additionalSettings) async { Map<String, dynamic> additionalSettings) async {
var res = await sourceRequest(link, additionalSettings); var apkUrls = versionVariants
if (res.statusCode == 200) { .map((e) {
var html = parse(res.body); String appId = e['package_name'];
var apksDiv = String versionCode = e['version_code'];
html.querySelector('#version-list div div.show-more-content');
DateTime? topReleaseDate; List<String> architectures = e['native_code']?.cast<String>();
var apkUrls = apksDiv String architectureString = architectures.join(',');
?.querySelectorAll('div.group-title') if (architectures.contains("universal") ||
.map((e) { architectures.contains("unlimited")) {
String architectureString = e.text.trim(); architectures = [];
if (architectureString.toLowerCase() == 'unlimited' || }
architectureString.toLowerCase() == 'universal') { if (additionalSettings['autoApkFilterByArch'] == true &&
architectureString = ''; architectures.isNotEmpty &&
} architectures.where((a) => supportedArchs.contains(a)).isEmpty) {
List<String> architectures = architectureString return null;
.split(',') }
.map((e) => e.trim())
.where((e) => e.isNotEmpty) String type = e['asset']['type'];
.toList(); String downloadUri = e['asset']['url'];
// Only take the first APK for each architecture, ignore others for now, for simplicity
// Unclear why there can even be multiple APKs for the same version and arch return MapEntry(
var apkInfo = e.nextElementSibling?.querySelector('div.info'); '$appId-$versionCode-$architectureString.${type.toLowerCase()}',
String? versionCode = RegExp('[0-9]+') downloadUri);
.firstMatch( })
apkInfo?.querySelector('div.info-top .code')?.text ?? .nonNulls
'') .toList()
?.group(0) .unique((e) => e.key);
?.trim();
var types = apkInfo if (apkUrls.isEmpty) {
?.querySelectorAll('div.info-top span.tag') throw NoAPKError();
.map((e) => e.text.trim()) }
.map((t) => t == 'APKs' ? 'APK' : t) ??
[]; // get version details from first variant
String type = types.isEmpty ? 'APK' : types.first; var v = versionVariants.first;
String? dateString = apkInfo String version = v['version_name'];
?.querySelector('div.info-bottom span.time') String author = v['developer'];
?.text String appName = v['title'];
.trim(); DateTime releaseDate = DateTime.parse(v['update_date']);
DateTime? releaseDate = parseDateTimeMMMddCommayyyy(dateString); String? changeLog = v['whatsnew'];
if (additionalSettings['autoApkFilterByArch'] == true && if (changeLog != null && changeLog.isEmpty) {
architectures.isNotEmpty && changeLog = null;
architectures }
.where((a) => supportedArchs.contains(a))
.isEmpty) { if (additionalSettings['useFirstApkOfVersion'] == true) {
return const MapEntry('', ''); apkUrls = [apkUrls.first];
} }
topReleaseDate ??=
releaseDate; // Just use the release date of the first APK in the list as the release date for this version return APKDetails(version, apkUrls, AppNames(author, appName),
return MapEntry( releaseDate: releaseDate, changeLog: changeLog);
'$appId-$versionCode-$architectureString.${type.toLowerCase()}', }
'https://d.${hosts.contains(host) ? 'cdnpure.com' : host}/b/$type/$appId?versionCode=$versionCode');
}) @override
.where((e) => e.key.isNotEmpty) Future<Map<String, String>?> getRequestHeaders(
.toList() ?? Map<String, dynamic> additionalSettings,
[]; {bool forAPKDownload = false}) async {
if (apkUrls.isEmpty) { if (forAPKDownload) {
var link = return null;
html.querySelector("a.download-start-btn")?.attributes['href'];
RegExp downloadLinkRegEx = RegExp(
r'^https:\/\/d\.[^/]+\/b\/([^/]+)\/[^/?]+\?versionCode=([0-9]+)$',
caseSensitive: false);
RegExpMatch? match = downloadLinkRegEx.firstMatch(link ?? '');
if (match == null) {
throw NoAPKError();
}
String type = match.group(1)!;
String versionCode = match.group(2)!;
apkUrls = [
MapEntry('$appId-$versionCode-.${type.toLowerCase()}',
'https://d.${hosts.contains(host) ? 'cdnpure.com' : host}/b/$type/$appId?versionCode=$versionCode')
];
}
String version = Uri.parse(link).pathSegments.last;
String? author;
try {
author = html
.querySelector('span.info-sdk')
?.text
.trim()
.substring(version.length + 4) ??
Uri.parse(standardUrl).pathSegments.reversed.last;
} catch (e) {
author = html.querySelector('span.info-sdk')?.text.trim() ??
Uri.parse(standardUrl).pathSegments.reversed.last;
}
String appName =
html.querySelector('h1.info-title')?.text.trim() ?? appId;
String? changeLog = html
.querySelector('div.module.change-log')
?.innerHtml
.trim()
.replaceAll("<br>", " \n");
return APKDetails(version, apkUrls, AppNames(author, appName),
releaseDate: topReleaseDate, changeLog: changeLog);
} else { } else {
throw getObtainiumHttpError(res); return {
"Ual-Access-Businessid": "projecta",
"Ual-Access-ProjectA":
'{"device_info":{"os_ver":"${((await DeviceInfoPlugin().androidInfo).version.sdkInt)}"}}',
};
} }
} }
@ -177,41 +139,50 @@ class APKPure extends AppSource {
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
String appId = (await tryInferringAppId(standardUrl))!; String appId = (await tryInferringAppId(standardUrl))!;
String host = Uri.parse(standardUrl).host;
var res0 = await sourceRequest('$standardUrl/versions', additionalSettings); List<String> supportedArchs =
var decodedStandardUrl = standardUrl; (await DeviceInfoPlugin().androidInfo).supportedAbis;
try {
decodedStandardUrl = Uri.decodeFull(decodedStandardUrl); // request versions from API
} catch (e) { var res = await sourceRequest(
// "https://tapi.pureapk.com/v3/get_app_his_version?package_name=$appId&hl=en",
additionalSettings);
if (res.statusCode != 200) {
throw getObtainiumHttpError(res);
} }
var versionLinks = await grabLinksCommon(res0, { List<Map<String, dynamic>> apks =
'skipSort': true, jsonDecode(res.body)['version_list'].cast<Map<String, dynamic>>();
'customLinkFilterRegex': '$decodedStandardUrl/download/[^/]+\$'
});
var supportedArchs = (await DeviceInfoPlugin().androidInfo).supportedAbis; // group by version
List<List<Map<String, dynamic>>> versions = apks
.fold<Map<String, List<Map<String, dynamic>>>>({},
(Map<String, List<Map<String, dynamic>>> val,
Map<String, dynamic> element) {
String v = element['version_name'];
if (!val.containsKey(v)) {
val[v] = [];
}
val[v]?.add(element);
return val;
})
.values
.toList();
if (additionalSettings['autoApkFilterByArch'] != true) { if (versions.isEmpty) {
// No need to request multiple versions when we're not going to filter them (always pick the top one)
versionLinks = versionLinks.sublist(0, 1);
}
if (versionLinks.isEmpty) {
throw NoReleasesError(); throw NoReleasesError();
} }
for (var i = 0; i < versionLinks.length; i++) { for (var i = 0; i < versions.length; i++) {
var link = versionLinks[i]; var v = versions[i];
try { try {
if (i == 0 && additionalSettings['stayOneVersionBehind'] == true) { if (i == 0 && additionalSettings['stayOneVersionBehind'] == true) {
throw NoReleasesError(); throw NoReleasesError();
} }
return await getDetailsForVersionLink(standardUrl, appId, host, return await getDetailsForVersion(
supportedArchs, link.key, additionalSettings); v, supportedArchs, additionalSettings);
} catch (e) { } catch (e) {
if (additionalSettings['fallbackToOlderReleases'] != true || if (additionalSettings['fallbackToOlderReleases'] != true ||
i == versionLinks.length - 1) { i == versions.length - 1) {
rethrow; rethrow;
} }
} }

View File

@ -1,9 +1,23 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:html/parser.dart'; import 'package:html/parser.dart';
import 'package:obtainium/app_sources/apkpure.dart';
import 'package:obtainium/custom_errors.dart'; import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart'; import 'package:obtainium/providers/source_provider.dart';
parseDateTimeMMMddCommayyyy(String? dateString) {
DateTime? releaseDate;
try {
releaseDate = dateString != null
? DateFormat('MMM dd, yyyy').parse(dateString)
: null;
releaseDate = dateString != null && releaseDate == null
? DateFormat('MMMM dd, yyyy').parse(dateString)
: releaseDate;
} catch (err) {
// ignore
}
return releaseDate;
}
class Uptodown extends AppSource { class Uptodown extends AppSource {
Uptodown() { Uptodown() {
hosts = ['uptodown.com']; hosts = ['uptodown.com'];