From 988f9a6f9fc307ce67465b75fd094057b815ffde Mon Sep 17 00:00:00 2001 From: UjuiUjuMandan <125150101+UjuiUjuMandan@users.noreply.github.com> Date: Thu, 13 Mar 2025 12:54:28 +0000 Subject: [PATCH 1/5] Add CoolApk --- README.md | 1 + assets/translations/en.json | 1 + assets/translations/zh.json | 1 + .../android/en-US/full_description.txt | 1 + .../metadata/android/ru/full_description.txt | 1 + lib/app_sources/coolapk.dart | 197 ++++++++++++++++++ lib/providers/source_provider.dart | 2 + pubspec.lock | 8 + pubspec.yaml | 1 + 9 files changed, 213 insertions(+) create mode 100644 lib/app_sources/coolapk.dart diff --git a/README.md b/README.md index 36bfccc..829570d 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Currently supported App sources: - [Uptodown](https://uptodown.com/) - [Huawei AppGallery](https://appgallery.huawei.com/) - [Tencent App Store](https://sj.qq.com/) + - [CoolApk](https://coolapk.com/) - [RuStore](https://rustore.ru/) - Jenkins Jobs - [APKMirror](https://apkmirror.com/) (Track-Only) diff --git a/assets/translations/en.json b/assets/translations/en.json index e745dfd..1642588 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -319,6 +319,7 @@ "stayOneVersionBehind": "Stay one version behind latest", "refreshBeforeDownload": "Refresh app details before download", "tencentAppStore": "Tencent App Store", + "coolApk": "CoolApk", "name": "Name", "smartname": "Name (Smart)", "sortMethod": "Sort Method", diff --git a/assets/translations/zh.json b/assets/translations/zh.json index 40884ff..10e6c39 100644 --- a/assets/translations/zh.json +++ b/assets/translations/zh.json @@ -319,6 +319,7 @@ "stayOneVersionBehind": "比最新版本晚一个版本", "refreshBeforeDownload": "下载前刷新应用程序详细信息", "tencentAppStore": "腾讯应用宝", + "coolApk": "酷安", "name": "名称", "smartname": "姓名(智能)", "sortMethod": "排序方法", diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt index 90025e2..d620444 100644 --- a/fastlane/metadata/android/en-US/full_description.txt +++ b/fastlane/metadata/android/en-US/full_description.txt @@ -25,6 +25,7 @@
  • APKMirror (Track-Only)
  • Huawei AppGallery
  • Tencent App Store
  • +
  • CoolApk
  • Jenkins Jobs
  • RuStore
  • diff --git a/fastlane/metadata/android/ru/full_description.txt b/fastlane/metadata/android/ru/full_description.txt index e762156..7249abd 100644 --- a/fastlane/metadata/android/ru/full_description.txt +++ b/fastlane/metadata/android/ru/full_description.txt @@ -25,6 +25,7 @@
  • APKMirror (Track-Only)
  • Huawei AppGallery
  • Tencent App Store
  • +
  • CoolApk
  • Jenkins Jobs
  • RuStore
  • diff --git a/lib/app_sources/coolapk.dart b/lib/app_sources/coolapk.dart new file mode 100644 index 0000000..976f7f2 --- /dev/null +++ b/lib/app_sources/coolapk.dart @@ -0,0 +1,197 @@ +import 'dart:convert'; +import 'package:bcrypt/bcrypt.dart'; +import 'package:crypto/crypto.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:obtainium/custom_errors.dart'; +import 'package:obtainium/providers/source_provider.dart'; +import 'dart:math'; + +// kanged from https://github.com/DUpdateSystem/UpgradeAll/blob/b2f92c9/core-websdk/src/main/java/net/xzos/upgradeall/core/websdk/api/client_proxy/hubs/CoolApk.kt +class CoolApk extends AppSource { + CoolApk() { + name = tr('coolApk'); + hosts = ['www.coolapk.com', 'api2.coolapk.com']; + allowSubDomains = true; + naiveStandardVersionDetection = true; + } + + @override + String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) { + print('Debug: Standardizing URL: $url'); + RegExp standardUrlRegEx = RegExp( + r'^https?://(www\.)?coolapk\.com/apk/[^/]+', + caseSensitive: false); + var match = standardUrlRegEx.firstMatch(url); + if (match == null) { + print('Debug: Invalid URL for CoolApk: $url'); + throw InvalidURLError(name); + } + String standardizedUrl = match.group(0)!; + print('Debug: Standardized URL: $standardizedUrl'); + return standardizedUrl; + } + + @override + Future tryInferringAppId(String standardUrl, + {Map additionalSettings = const {}}) async { + print('Debug: Inferring appId from URL: $standardUrl'); + String appId = Uri.parse(standardUrl).pathSegments.last; + print('Debug: Inferred appId: $appId'); + return appId; + } + + @override + Future getLatestAPKDetails( + String standardUrl, + Map additionalSettings, + ) async { + String appId = (await tryInferringAppId(standardUrl))!; + String apiUrl = 'https://api2.coolapk.com'; + print('Debug: Fetching details for appId: $appId'); + + // get latest + var detailUrl = '$apiUrl/v6/apk/detail?id=$appId'; + var headers = await getRequestHeaders(additionalSettings); + print('Debug: Requesting URL: $detailUrl with headers: $headers'); + var res = await sourceRequest(detailUrl, additionalSettings); + + print('Debug: Response status code: ${res.statusCode}'); + if (res.statusCode != 200) { + print('Debug: HTTP error: ${res.statusCode} - ${res.body}'); + throw getObtainiumHttpError(res); + } + + var json = jsonDecode(res.body); + print('Debug: Parsed JSON: $json'); + if (json['status'] == -2 || json['data'] == null) { + print('Debug: No releases found or invalid status: ${json['status']}'); + throw NoReleasesError(); + } + + var detail = json['data']; + String version = detail['apkversionname'].toString(); + String appName = detail['title'].toString(); + String author = detail['developername']?.toString() ?? 'CoolApk'; + String changelog = detail['changelog']?.toString() ?? ''; + int? releaseDate = detail['lastupdate'] != null + ? (detail['lastupdate'] is int + ? detail['lastupdate'] * 1000 + : int.parse(detail['lastupdate'].toString()) * 1000) + : null; + String aid = detail['id'].toString(); + + print('Debug: Version: $version, AppName: $appName, Author: $author, AID: $aid'); + + // get apk url + String apkUrl = await _getLatestApkUrl(apiUrl, appId, aid, version, headers); + if (apkUrl.isEmpty) { + print('Debug: No APK URL found for $appId'); + throw NoAPKError(); + } + print('Debug: APK URL: $apkUrl'); + + String apkName = '${appId}_$version.apk'; + + return APKDetails( + version, + [MapEntry(apkName, apkUrl)], + AppNames(author, appName), + releaseDate: releaseDate != null + ? DateTime.fromMillisecondsSinceEpoch(releaseDate) + : null, + changeLog: changelog, + ); + } + + Future _getLatestApkUrl(String apiUrl, String appId, String aid, + String version, Map? headers) async { + String url = '$apiUrl/v6/apk/download?pn=$appId&aid=$aid'; + print('Debug: Fetching APK URL: $url'); + var res = await sourceRequest(url, {}, followRedirects: false); + print('Debug: APK request status code: ${res.statusCode}'); + if (res.statusCode >= 300 && res.statusCode < 400) { + String location = res.headers['location'] ?? ''; + print('Debug: Redirect location: $location'); + return location; + } + print('Debug: No redirect found for APK URL'); + return ''; + } + + @override + Future?> getRequestHeaders( + Map additionalSettings, + {bool forAPKDownload = false}) async { + var tokenPair = _getToken(); + print('Debug: Generated tokenPair: $tokenPair'); + // CoolAPK header + return { + 'User-Agent': + 'Dalvik/2.1.0 (Linux; U; Android 9; MI 8 SE MIUI/9.5.9) (#Build; Xiaomi; MI 8 SE; PKQ1.181121.001; 9) +CoolMarket/12.4.2-2208241-universal', + 'X-App-Id': 'com.coolapk.market', + 'X-Requested-With': 'XMLHttpRequest', + 'X-Sdk-Int': '30', + 'X-App-Mode': 'universal', + 'X-App-Channel': 'coolapk', + 'X-Sdk-Locale': 'zh-CN', + 'X-App-Version': '12.4.2', + 'X-Api-Supported': '2208241', + 'X-App-Code': '2208241', + 'X-Api-Version': '12', + 'X-App-Device': tokenPair['deviceCode']!, + 'X-Dark-Mode': '0', + 'X-App-Token': tokenPair['token']!, + }; + } + + Map _getToken() { + final rand = Random(); + + String randHexString(int n) => + List.generate(n, (_) => rand.nextInt(256).toRadixString(16).padLeft(2, '0')) + .join() + .toUpperCase(); + + String randMacAddress() => + List.generate(6, (_) => rand.nextInt(256).toRadixString(16).padLeft(2, '0')) + .join(':'); + + // 加密算法来自 https://github.com/XiaoMengXinX/FuckCoolapkTokenV2、https://github.com/Coolapk-UWP/Coolapk-UWP + // device + String aid = randHexString(16); + String mac = randMacAddress(); + const manufactor = 'Google'; + const brand = 'Google'; + const model = 'Pixel 5a'; + const buildNumber = 'SQ1D.220105.007'; + + // generate deviceCode + String deviceCode = + base64.encode('$aid; ; ; $mac; $manufactor; $brand; $model; $buildNumber'.codeUnits); + print('Debug: DeviceCode: $deviceCode'); + + // generate timestamp + String timeStamp = (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(); + String base64TimeStamp = base64.encode(timeStamp.codeUnits); + String md5TimeStamp = md5.convert(timeStamp.codeUnits).toString(); + String md5DeviceCode = md5.convert(deviceCode.codeUnits).toString(); + print('Debug: TimeStamp: $timeStamp, MD5TimeStamp: $md5TimeStamp, MD5DeviceCode: $md5DeviceCode'); + + // generate token + String token = + 'token://com.coolapk.market/dcf01e569c1e3db93a3d0fcf191a622c?$md5TimeStamp\$$md5DeviceCode&com.coolapk.market'; + String base64Token = base64.encode(token.codeUnits); + String md5Base64Token = md5.convert(base64Token.codeUnits).toString(); + String md5Token = md5.convert(token.codeUnits).toString(); + print('Debug: Token: $token, Base64Token: $base64Token, MD5Base64Token: $md5Base64Token'); + + // generate salt and hash + String bcryptSalt = '\$2a\$10\$${base64TimeStamp.substring(0, 14)}/${md5Token.substring(0, 6)}u'; + String bcryptResult = BCrypt.hashpw(md5Base64Token, bcryptSalt); + String reBcryptResult = bcryptResult.replaceRange(0, 3, '\$2y'); + String finalToken = 'v2${base64.encode(reBcryptResult.codeUnits)}'; + print('Debug: BCryptSalt: $bcryptSalt, BCryptResult: $bcryptResult, FinalToken: $finalToken'); + + return {'deviceCode': deviceCode, 'token': finalToken}; + } +} \ No newline at end of file diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index 6084fb9..1096b29 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -13,6 +13,7 @@ import 'package:obtainium/app_sources/apkmirror.dart'; import 'package:obtainium/app_sources/apkpure.dart'; import 'package:obtainium/app_sources/aptoide.dart'; import 'package:obtainium/app_sources/codeberg.dart'; +import 'package:obtainium/app_sources/coolapk.dart'; import 'package:obtainium/app_sources/directAPKLink.dart'; import 'package:obtainium/app_sources/fdroid.dart'; import 'package:obtainium/app_sources/fdroidrepo.dart'; @@ -894,6 +895,7 @@ class SourceProvider { Uptodown(), HuaweiAppGallery(), Tencent(), + CoolApk(), Jenkins(), APKMirror(), RuStore(), diff --git a/pubspec.lock b/pubspec.lock index 803a8ee..78fb2ed 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -124,6 +124,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + bcrypt: + dependency: "direct main" + description: + name: bcrypt + sha256: "9dc3f234d5935a76917a6056613e1a6d9b53f7fa56f98e24cd49b8969307764b" + url: "https://pub.dev" + source: hosted + version: "1.1.3" boolean_selector: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index af6cd79..92eab1e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -70,6 +70,7 @@ dependencies: url: https://github.com/AlexBacich/shared-storage ref: master crypto: ^3.0.3 + bcrypt: ^1.1.3 app_links: ^6.0.1 background_fetch: ^1.2.1 equations: ^5.0.2 From 74d6ffcfb39ee54e4811a096ad0fc60a4a34a7f7 Mon Sep 17 00:00:00 2001 From: Lucas <55422065+lucasmz-dev@users.noreply.github.com> Date: Sun, 6 Apr 2025 17:33:43 +0000 Subject: [PATCH 2/5] Add files via upload --- .../obtainium-unofficial-app-pt_BR.json | 379 ++++++++++++++++++ 1 file changed, 379 insertions(+) create mode 100644 assets/translations/obtainium-unofficial-app-pt_BR.json diff --git a/assets/translations/obtainium-unofficial-app-pt_BR.json b/assets/translations/obtainium-unofficial-app-pt_BR.json new file mode 100644 index 0000000..5270cc3 --- /dev/null +++ b/assets/translations/obtainium-unofficial-app-pt_BR.json @@ -0,0 +1,379 @@ +{ + "noVersionFound": "Não foi possível determinar a versão do lançamento", + "urlMatchesNoSource": "A URL não corresponde com nenhuma fonte conhecida", + "cantInstallOlderVersion": "Não é possível instalar uma versão mais antiga de um app", + "functionNotImplemented": "Essa classe não implementou esse recurso ainda", + "placeholder": "Espaço reservado", + "someErrors": "Ocorreram alguns erros", + "unexpectedError": "Erro inesperado", + "ok": "Ok", + "and": "e", + "githubPATLabel": "Token de acesso pessoal do GitHub (aumenta o limite de taxa)", + "includePrereleases": "Incluir pré-lançamentos", + "filterReleaseTitlesByRegEx": "Filtrar títulos de lançamentos por expressão regular", + "invalidRegEx": "Expressão regular inválida", + "noDescription": "Sem descrição", + "cancel": "Cancelar", + "continue": "Continuar", + "requiredInBrackets": "(obrigatório)", + "colour": "Cor", + "standard": "Padrão", + "custom": "Personalizado", + "useMaterialYou": "Usar Material You", + "githubStarredRepos": "Repositórios com estrela do GitHub", + "uname": "Nome de usuário", + "xIsTrackOnly": "{} é somente de rastreio", + "source": "Fonte", + "app": "App", + "appsFromSourceAreTrackOnly": "Apps desta fonte são somente para rastreamento.", + "youPickedTrackOnly": "Você selecionou a opção de somente rastreamento.", + "cancelled": "Cancelado", + "appAlreadyAdded": "O app já foi adicionado", + "alreadyUpToDateQuestion": "O app já está atualizado?", + "addApp": "Adicionar app", + "appSourceURL": "URL da fonte do app", + "error": "Erro", + "add": "Adicionar", + "searchSomeSourcesLabel": "Pesquisar (somente algumas fontes)", + "search": "Pesquisar", + "additionalOptsFor": "Opções adicionais de {}", + "supportedSources": "Fontes suportadas", + "trackOnlyInBrackets": "(somente rastreamento)", + "searchableInBrackets": "(pesquisável)", + "appsString": "Apps", + "noAppsForFilter": "Nenhum app pro filtro", + "byX": "Por {}", + "percentProgress": "Progresso: {}%", + "pleaseWait": "Por favor aguarde", + "notInstalled": "Não instalado", + "pseudoVersion": "pseudo-versão", + "selectAll": "Selecionar tudo", + "deselectX": "Desselecionar {}", + "removeSelectedAppsQuestion": "Remover os apps selecionados?", + "removeSelectedApps": "Remover apps selecionados", + "updateX": "Atualizar {}", + "installX": "Instalar {}", + "markXTrackOnlyAsUpdated": "Marcar {}\n(somente rastreamento)\ncomo atualizado", + "changeX": "Alterar {}", + "installUpdateApps": "Instalar/atualizar apps", + "installUpdateSelectedApps": "Instalar/atualizar apps selecionados", + "no": "Não", + "yes": "Sim", + "markSelectedAppsUpdated": "Marcar apps selecionados como atualizados", + "pinToTop": "Fixar ao topo", + "unpinFromTop": "Desfixar do topo", + "resetInstallStatusForSelectedAppsQuestion": "Redefinir o estado de instalação dos apps selecionados?", + "customLinkMessage": "Esses links funcionarão em dispositivos com o Obtainium instalado", + "shareAppConfigLinks": "Compartilhar configuração do app como um link HTML", + "shareSelectedAppURLs": "Compartilhar as URLs dos apps selecionados", + "resetInstallStatus": "Redefinir estado de instalação", + "more": "Mais", + "removeOutdatedFilter": "Remover filtro de apps desatualizados", + "showOutdatedOnly": "Mostrar somente apps desatualizados", + "filter": "Filtro", + "filterApps": "Filtrar apps", + "appName": "Nome do app", + "author": "Autor", + "upToDateApps": "Apps atualizados", + "nonInstalledApps": "Apps não instalados", + "importExport": "Importar/Exportar", + "exportedTo": "Exportado para {}", + "obtainiumExport": "Exportação do Obtainium", + "invalidInput": "Entrada inválida", + "importedX": "{} importado(s)", + "obtainiumImport": "Importação do Obtainium", + "searchQuery": "Consulta de pesquisa", + "appURLList": "Lista de URLs dos apps", + "searchX": "Pesquisar {}", + "noResults": "Nenhum resultado encontrado", + "importX": "Importar {}", + "importErrors": "Erros de importação", + "importedXOfYApps": "{} de {} foram importados.", + "followingURLsHadErrors": "As seguintes URLs tiveram erros:", + "selectURL": "Selecionar URL", + "selectURLs": "Selecionar URLs", + "pick": "Escolher", + "theme": "Tema", + "dark": "Escuro", + "light": "Claro", + "followSystem": "Seguir o sistema", + "useBlackTheme": "Usar o tema escuro de preto profundo", + "appSortBy": "Ordenar apps por", + "authorName": "Autor/nome", + "nameAuthor": "Nome/autor", + "asAdded": "Como adicionados", + "appSortOrder": "Ordem dos apps", + "ascending": "Crescente", + "descending": "Decrescente", + "bgUpdateCheckInterval": "Intervalo de busca por atualizações em segundo plano", + "neverManualOnly": "Nunca - somente manualmente", + "appearance": "Aparência", + "pinUpdates": "Fixar atualizações no topo da tela de apps", + "updates": "Atualizações", + "sourceSpecific": "Específico à fonte", + "appSource": "Fonte do app", + "noLogs": "Nenhum registro", + "appLogs": "Registros do app", + "close": "Fechar", + "share": "Compartilhar", + "pickAnAPK": "Selecione um APK", + "appHasMoreThanOnePackage": "{} tem mais de um pacote:", + "deviceSupportsXArch": "Seu dispositivo suporta a arquitetura de CPU {}.", + "warning": "Alerta", + "sourceIsXButPackageFromYPrompt": "A fonte do app é '{}' mas o pacote de lançamento vem de '{}'. Continuar mesmo assim?", + "updatesAvailable": "Atualizações disponíveis", + "noNewUpdates": "Nenhuma atualização disponível.", + "xHasAnUpdate": "{} tem uma atualização.", + "appsUpdated": "Apps atualizados", + "appsNotUpdated": "Falhou ao atualizar os aplicativos", + "appsUpdatedNotifDescription": "Notifica o usuário que atualizações de um ou mais apps foram aplicadas em segundo plano", + "xWasNotUpdatedToY": "Falha ao atualizar {} para a versão {}.", + "errorCheckingUpdates": "Ocorreu um erro ao buscar atualizações", + "appsRemoved": "Apps removidos", + "appsRemovedNotifDescription": "Notifica o usuário que um ou mais apps foram removidos devido a erros ao carregá-los", + "xWasRemovedDueToErrorY": "{} for removido devido ao erro: {}", + "completeAppInstallation": "Concluir instalação do app", + "completeAppInstallationNotifDescription": "Pede pro usuário voltar ao Obtainium para concluir a instalação de um app", + "checkingForUpdates": "Buscando atualizações", + "checkingForUpdatesNotifDescription": "Notificação transitória que aparece ao buscar atualizações", + "pleaseAllowInstallPerm": "Permita que o Obtainium instale apps", + "trackOnly": "Somente rastreamento", + "errorWithHttpStatusCode": "Erro {}", + "unknown": "Desconhecido", + "none": "Nenhum", + "never": "Nunca", + "latestVersionX": "Mais recente: {}", + "installedVersionX": "Instalado: {}", + "lastUpdateCheckX": "Última busca por atualizações: {}", + "remove": "Remover", + "yesMarkUpdated": "Sim, marcar como atualizado", + "fdroid": "Oficial do F-Droid", + "appIdOrName": "ID do app ou nome", + "appId": "ID do app", + "reposHaveMultipleApps": "Repositórios podem conter vários apps", + "fdroidThirdPartyRepo": "Repositório de terceiros do F-Droid", + "install": "Instalar", + "markInstalled": "Marcar como instalado", + "update": "Atualizar", + "markUpdated": "Marcar como atualizado", + "additionalOptions": "Opções adicionais", + "downloadingX": "Baixando {}", + "downloadX": "Baixar {}", + "downloadedX": "{} foi baixado", + "releaseAsset": "Item de lançamento", + "downloadNotifDescription": "Notifica o usuário do progresso ao baixar um app", + "noAPKFound": "Nenhum APK encontrado", + "noVersionDetection": "Sem detecção de versão", + "categorize": "Categorizar", + "categories": "Categorias", + "category": "Categoria", + "noCategory": "Nenhuma categoria", + "deleteCategoriesQuestion": "Excluir categorias?", + "addCategory": "Adicionar categoria", + "label": "Rótulo", + "language": "Idioma", + "copiedToClipboard": "Copiado para a área de transferência", + "storagePermissionDenied": "Permissão de armazenamento negada", + "selectedCategorizeWarning": "Isso substituirá a configuração de categoria existente dos apps selecionados.", + "filterAPKsByRegEx": "Filtrar APKs por expressão regular", + "removeFromObtainium": "Remover do Obtainium", + "onlyWorksWithNonVersionDetectApps": "Funciona somente em apps com a detecção de versão desativada.", + "releaseTitleAsVersion": "Usar título do lançamento como número da versão", + "changes": "Alterações", + "releaseDate": "Data de lançamento", + "importFromURLsInFile": "Importar das URLs em arquivo (como OPML)", + "versionDetectionExplanation": "Combinar o número da versão com a versão detectada pelo sistema", + "versionDetection": "Detecção de versão", + "standardVersionDetection": "Detecção de versão padrão", + "groupByCategory": "Agrupar por categoria", + "autoApkFilterByArch": "Tentar filtrar APKs pela arquitetura da CPU quando possível", + "autoLinkFilterByArch": "Tentar filtrar links pela arquitetura da CPU quando possível", + "overrideSource": "Sobrescrever fonte", + "dontShowAgain": "Não mostrar isso novamente", + "dontShowTrackOnlyWarnings": "Não mostrar alertas de \"somente rastreamento\"", + "moveNonInstalledAppsToBottom": "Mover apps não instalados ao final da tela de apps", + "gitlabPATLabel": "Token de acesso pessoal do GitLab", + "about": "Sobre", + "requiresCredentialsInSettings": "{} precisa de credenciais adicionais (nas Configurações)", + "checkOnStart": "Buscar atualizações ao abrir o app", + "removeOnExternalUninstall": "Remover automaticamente apps desinstalados externamente", + "pickHighestVersionCode": "Selecionar APK de versão mais alta automaticamente", + "disablePageTransitions": "Desativar animações de transição de tela", + "reversePageTransitions": "Inverter animações de transição de tela", + "minStarCount": "Número de estrelas mínimo", + "addInfoBelow": "Adicione essa informação abaixo.", + "addInfoInSettings": "Adicione essa informação nas Configurações.", + "sortByLastLinkSegment": "Ordenar somente pelo ultimo segmento do link", + "filterReleaseNotesByRegEx": "Filtrar notas de lançamento por expressão regular", + "appsPossiblyUpdated": "Tentativas de atualização de apps", + "xWasPossiblyUpdatedToY": "{} pode ter sido atualizado para a versão {}.", + "enableBackgroundUpdates": "Ativar atualizações em segundo plano", + "backgroundUpdateReqsExplanation": "Atualizações em segundo plano podem não funcionar com todos os apps.", + "backgroundUpdateLimitsExplanation": "O sucesso de uma instalação em segundo plano só pode ser determinada ao abrir o Obtainium.", + "verifyLatestTag": "Verificar a tag 'mais recente'", + "filterByLinkText": "Filtrar links por texto do link", + "intermediateLinkNotFound": "Link intermediário não encontrado", + "intermediateLink": "Link intermediário", + "exemptFromBackgroundUpdates": "Isento de atualizações em segundo plano (caso ativadas)", + "bgUpdatesWhileChargingOnly": "Desativar atualizações em segundo plano fora do carregador", + "autoSelectHighestVersionCode": "Selecionar automaticamente APK com o código de versão mais alto", + "versionExtractionRegEx": "ExReg de extração do número da versão", + "trimVersionString": "Cortar número da versal com ExReg", + "matchGroupToUseForX": "Corresponder grupo para o uso em \"{}\"", + "highlightTouchTargets": "Acentuar alvos de toque menos óbvios", + "pickExportDir": "Selecionar pasta de exportação", + "autoExportOnChanges": "Exportar automaticamente ao ocorrer alterações", + "includeSettings": "Incluir configurações", + "trySelectingSuggestedVersionCode": "Tente selecionar o APK com o código de versão sugerido", + "dontSortReleasesList": "Manter ordem de lançamento da API", + "reverseSort": "Ordem inversa", + "takeFirstLink": "Usar o primeiro link", + "skipSort": "Pular ordenação", + "debugMenu": "Menu de depuração", + "runBgCheckNow": "Executar busca por atualizações em segundo plano agora", + "versionExtractWholePage": "Aplicar ExReg de extração de número de versão à página inteira", + "installing": "Instalando", + "updatesAvailableNotifChannel": "Atualizações disponíveis", + "appsUpdatedNotifChannel": "Apps atualizados", + "appsPossiblyUpdatedNotifChannel": "Tentativas de atualização de apps", + "errorCheckingUpdatesNotifChannel": "Erro ao buscar atualizações", + "appsRemovedNotifChannel": "Apps removidos", + "downloadingXNotifChannel": "Baixando {}", + "checkingForUpdatesNotifChannel": "Buscando atualizações", + "onlyCheckInstalledOrTrackOnlyApps": "Buscar atualizações somente para apps instalados e de somente rastreamento", + "supportFixedAPKURL": "Suportar URLs de APK fixas", + "parallelDownloads": "Permitir downloads em paralelo", + "shizukuBinderNotFound": "Serviço Shizuku não está em execução", + "shizukuOld": "Versão do Shizuku antiga (<11) - atualize", + "shizukuPretendToBeGooglePlay": "Definir Google Play como a fonte de instalação (se o Shizuku é usado)", + "useSystemFont": "Usar a fonte do sistema", + "useVersionCodeAsOSVersion": "Usar código de versão do app como a versão detectada pelo sistema", + "requestHeader": "Cabeçalho da solicitação", + "defaultPseudoVersioningMethod": "Método de pseudo-versão padrão", + "partialAPKHash": "Hash do APK parcial", + "APKLinkHash": "Hash do link do APK", + "directAPKLink": "Link direto ao APK", + "pseudoVersionInUse": "Uma pseudo-versão está em uso", + "installed": "Instalado", + "latest": "Mais recente", + "invertRegEx": "Inverter expressão regular", + "note": "Observação", + "badDownload": "O APK não pode ser interpretado (incompatível ou baixado parcialmente)", + "beforeNewInstallsShareToAppVerifier": "Compartilhar apps novos com o AppVerifier (se disponível)", + "appVerifierInstructionToast": "Compartilhe com o AppVerifier, e volte aqui ao estar pronto.", + "wiki": "Ajuda/Wiki", + "crowdsourcedConfigsShort": "Configurações de app da comunidade", + "allowInsecure": "Permitir solicitações de HTTP inseguras", + "stayOneVersionBehind": "Ficar uma versão antes da mais recente", + "refreshBeforeDownload": "Atualizar detalhes do app antes de baixar", + "tencentAppStore": "Loja de Apps da Tencent", + "name": "Nome", + "smartname": "Nome (inteligente)", + "welcome": "Boas vindas", + "removeAppQuestion": { + "one": "Remover app?", + "other": "Remover apps?" + }, + "tooManyRequestsTryAgainInMinutes": { + "one": "Muitas solicitações (limitado) - tente novamente em {} minuto", + "other": "Muitas solicitações (limitado) - tente novamente em {} minutos" + }, + "bgUpdateGotErrorRetryInMinutes": { + "one": "A busca de atualizações em segundo plano encontrou um {}, será agendado uma nova tentativa em {} minuto", + "other": "A busca de atualizações em segundo plano encontrou um {}, será agendado uma nova tentativa em {} minutos" + }, + "apps": { + "one": "{} app", + "other": "{} apps" + }, + "url": { + "one": "{} URL", + "other": "{} URLs" + }, + "minute": { + "one": "{} minuto", + "other": "{} minutos" + }, + "hour": { + "one": "{} hora", + "other": "{} horas" + }, + "day": { + "one": "{} dia", + "other": "{} dias" + }, + "xAndNMoreUpdatesAvailable": { + "one": "{} e mais 1 app têm atualizações.", + "other": "{} e mais {} apps têm atualizações." + }, + "xAndNMoreUpdatesInstalled": { + "one": "{} e mais 1 app foram atualizados.", + "other": "{} e mais {} apps foram atualizados." + }, + "xAndNMoreUpdatesFailed": { + "one": "Falha ao atualizar {} e mais 1 app.", + "other": "Falha ao atualizar {} e mais {} apps." + }, + "xAndNMoreUpdatesPossiblyInstalled": { + "one": "{} e mais 1 app podem ter sido atualizados.", + "other": "{} e mais {} apps podem ter sido atualizados." + }, + "apk": { + "one": "{} APK", + "other": "{} APKs" + }, + "invalidURLForSource": "Não é uma URL de app válida de {}", + "noReleaseFound": "Não foi possível encontrar um lançamento adequado", + "appIdMismatch": "O ID do pacote baixado não corresponde ao existente", + "fallbackToOlderReleases": "Recorrer à lançamentos mais antigos", + "dropdownNoOptsError": "ERRO: O MENU DEVE TER PELO MENOS UMA OPÇÃO", + "wrongArgNum": "Número errado de argumentos fornecidos", + "trackOnlyAppDescription": "As atualizações do app serão rastreadas, mas o Obtainium não baixará ou instalará elas.", + "noApps": "Nenhum app", + "updateAvailable": "Atualização disponível", + "xWillBeRemovedButRemainInstalled": "{} será removido do Obtainium mas continuará instalado no dispositivo.", + "markXSelectedAppsAsUpdated": "Marcar os {} apps selecionados como atualizados?", + "installStatusOfXWillBeResetExplanation": "Os estados de instalação dos apps selecionados serão redefinidos.\n\nIsso pode ajudar quando a versão exibida no Obtainium está incorreta devido a atualizações malsucedidas ou outros problemas.", + "settings": "Configurações", + "importFromURLList": "Importar da lista de URLs", + "line": "Linha", + "importedAppsIdDisclaimer": "Os apps importados podem ser exibidos incorretamente como se não estivessem instalados.\nPara resolver isso, reinstale eles pelo Obtainium.\nIsso não afetará os dados dos apps.\n\nIsso somente afeta a URL e os métodos de importação de terceiros.", + "followSystemThemeExplanation": "Só é possível seguir o tema do sistema ao usar aplicativos de terceiros", + "appNotFound": "O app não foi encontrado", + "updatesAvailableNotifDescription": "Notifica o usuário que atualizações estão disponíveis para um ou mais apps rastreados pelo Obtainium", + "xWasUpdatedToY": "{} foi atualizado para a versão {}.", + "showWebInAppView": "Mostrar a fonte da pagina web na tela de apps", + "deviceSupportsFollowingArchs": "Seu dispositivo suporta as seguintes arquiteturas de CPU:", + "errorCheckingUpdatesNotifDescription": "Uma notificação que mostra quando a busca de atualizações em segundo plano falha", + "obtainiumMustBeOpenToInstallApps": "O Obtainium precisa estar aberto para instalar apps", + "versionCorrectionDisabled": "Correção de versão desativada (o plugin parece não funcionar)", + "appWithIdOrNameNotFound": "Nenhum app foi encontrado com aquele ID ou nome", + "disableVersionDetection": "Desativar detecção de versão", + "noVersionDetectionExplanation": "Essa opção só seve ser usada para apps aonde a detecção de versão não funciona corretamente.", + "noCategories": "Nenhuma categoria", + "categoryDeleteWarning": "Todos os apps em categorias excluídas ficarão sem categoria.", + "uninstallFromDevice": "Desinstalar do dispositivo", + "releaseDateAsVersion": "Usar data de lançamento como número da versão", + "dontShowAPKOriginWarnings": "Não mostrar alertas de origem dos APKs", + "tryInferAppIdFromCode": "Tentar inferir o ID do app pelo código fonte", + "checkUpdateOnDetailPage": "Buscar atualizações ao abrir a tela de detalhes de um app", + "githubSourceNote": "O limite de taxa do GitHub pode ser evitado ao usar uma chave de API.", + "customLinkFilterRegex": "Filtro de link de APK personalizado por expressão regular (padrão '.apk$')", + "appsPossiblyUpdatedNotifDescription": "Notifica o usuário que atualizações de um ou mais apps podem ter sido aplicadas em segundo plano", + "bgUpdatesOnWiFiOnly": "Desativar atualizações em segundo plano fora do Wi-Fi", + "matchGroupToUse": "Corresponder grupo para o uso para a extração do número da versão por ExReg", + "filterVersionsByRegEx": "Filtrar versões por expressão regular", + "completeAppInstallationNotifChannel": "Concluir instalação do app", + "useShizuku": "Usar Shizuku ou Sui para instalação", + "useLatestAssetDateAsReleaseDate": "Usar o envio de item mais recente como a data de lançamento", + "crowdsourcedConfigsLabel": "Configurações de app pela comunidade (use ao seu próprio risco)", + "releaseDateAsVersionExplanation": "Essa opção só deve ser usada para apps quais a detecção de versão não funciona corretamente, mas uma data de lançamento está disponível.", + "intermediateLinkRegex": "Filtrar por um link 'intermediário' para visitar", + "bgTaskStarted": "Tarefa em segundo plano iniada - verifique os registros.", + "skipUpdateNotifications": "Pular notificações de atualização", + "selectX": "Selecionar {}", + "shizukuOldAndroidWithADB": "Shizuku sendo executado no Android < 8.1 com ADB - atualize o Android ou use o Sui", + "selfHostedNote": "O menu de opções \"{}\" pode ser usado para alcançar instâncias hospedadas-por-você/personalizadas de qualquer fonte.", + "sortMethod": "Método de ordenação", + "documentationLinksNote": "A página do Obtainium no GitHub visível abaixo contém links de vídeos, artigos, discussões, e outros recursos que podem te ajudar ao usar o app." +} From d4e857f7f47c82828d8e34edf4d4164b8be3b284 Mon Sep 17 00:00:00 2001 From: Lucas <55422065+lucasmz-dev@users.noreply.github.com> Date: Sun, 6 Apr 2025 17:36:20 +0000 Subject: [PATCH 3/5] Rename obtainium-unofficial-app-pt_BR.json to pt-BR.json --- .../pt-BR.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename assets/{translations/obtainium-unofficial-app-pt_BR.json => translation/pt-BR.json} (100%) diff --git a/assets/translations/obtainium-unofficial-app-pt_BR.json b/assets/translation/pt-BR.json similarity index 100% rename from assets/translations/obtainium-unofficial-app-pt_BR.json rename to assets/translation/pt-BR.json From 9c3bdafa47245b1cf69423b999c4fd82fc0a9fbd Mon Sep 17 00:00:00 2001 From: Lucas <55422065+lucasmz-dev@users.noreply.github.com> Date: Sun, 6 Apr 2025 17:36:42 +0000 Subject: [PATCH 4/5] Rename pt-BR.json to pt-BR.json --- assets/{translation => translations}/pt-BR.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename assets/{translation => translations}/pt-BR.json (100%) diff --git a/assets/translation/pt-BR.json b/assets/translations/pt-BR.json similarity index 100% rename from assets/translation/pt-BR.json rename to assets/translations/pt-BR.json From c9aed8dfc42ea3b105a4dd5417b606d47f0740b3 Mon Sep 17 00:00:00 2001 From: UjuiUjuMandan <125150101+UjuiUjuMandan@users.noreply.github.com> Date: Mon, 14 Apr 2025 04:49:11 +0000 Subject: [PATCH 5/5] clean up debug prints --- lib/app_sources/coolapk.dart | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/lib/app_sources/coolapk.dart b/lib/app_sources/coolapk.dart index 976f7f2..01027da 100644 --- a/lib/app_sources/coolapk.dart +++ b/lib/app_sources/coolapk.dart @@ -17,26 +17,21 @@ class CoolApk extends AppSource { @override String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) { - print('Debug: Standardizing URL: $url'); RegExp standardUrlRegEx = RegExp( r'^https?://(www\.)?coolapk\.com/apk/[^/]+', caseSensitive: false); var match = standardUrlRegEx.firstMatch(url); if (match == null) { - print('Debug: Invalid URL for CoolApk: $url'); throw InvalidURLError(name); } String standardizedUrl = match.group(0)!; - print('Debug: Standardized URL: $standardizedUrl'); return standardizedUrl; } @override Future tryInferringAppId(String standardUrl, {Map additionalSettings = const {}}) async { - print('Debug: Inferring appId from URL: $standardUrl'); String appId = Uri.parse(standardUrl).pathSegments.last; - print('Debug: Inferred appId: $appId'); return appId; } @@ -47,24 +42,18 @@ class CoolApk extends AppSource { ) async { String appId = (await tryInferringAppId(standardUrl))!; String apiUrl = 'https://api2.coolapk.com'; - print('Debug: Fetching details for appId: $appId'); // get latest var detailUrl = '$apiUrl/v6/apk/detail?id=$appId'; var headers = await getRequestHeaders(additionalSettings); - print('Debug: Requesting URL: $detailUrl with headers: $headers'); var res = await sourceRequest(detailUrl, additionalSettings); - print('Debug: Response status code: ${res.statusCode}'); if (res.statusCode != 200) { - print('Debug: HTTP error: ${res.statusCode} - ${res.body}'); throw getObtainiumHttpError(res); } var json = jsonDecode(res.body); - print('Debug: Parsed JSON: $json'); if (json['status'] == -2 || json['data'] == null) { - print('Debug: No releases found or invalid status: ${json['status']}'); throw NoReleasesError(); } @@ -80,15 +69,11 @@ class CoolApk extends AppSource { : null; String aid = detail['id'].toString(); - print('Debug: Version: $version, AppName: $appName, Author: $author, AID: $aid'); - // get apk url String apkUrl = await _getLatestApkUrl(apiUrl, appId, aid, version, headers); if (apkUrl.isEmpty) { - print('Debug: No APK URL found for $appId'); throw NoAPKError(); } - print('Debug: APK URL: $apkUrl'); String apkName = '${appId}_$version.apk'; @@ -106,15 +91,11 @@ class CoolApk extends AppSource { Future _getLatestApkUrl(String apiUrl, String appId, String aid, String version, Map? headers) async { String url = '$apiUrl/v6/apk/download?pn=$appId&aid=$aid'; - print('Debug: Fetching APK URL: $url'); var res = await sourceRequest(url, {}, followRedirects: false); - print('Debug: APK request status code: ${res.statusCode}'); if (res.statusCode >= 300 && res.statusCode < 400) { String location = res.headers['location'] ?? ''; - print('Debug: Redirect location: $location'); return location; } - print('Debug: No redirect found for APK URL'); return ''; } @@ -123,7 +104,6 @@ class CoolApk extends AppSource { Map additionalSettings, {bool forAPKDownload = false}) async { var tokenPair = _getToken(); - print('Debug: Generated tokenPair: $tokenPair'); // CoolAPK header return { 'User-Agent': @@ -168,14 +148,12 @@ class CoolApk extends AppSource { // generate deviceCode String deviceCode = base64.encode('$aid; ; ; $mac; $manufactor; $brand; $model; $buildNumber'.codeUnits); - print('Debug: DeviceCode: $deviceCode'); // generate timestamp String timeStamp = (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(); String base64TimeStamp = base64.encode(timeStamp.codeUnits); String md5TimeStamp = md5.convert(timeStamp.codeUnits).toString(); String md5DeviceCode = md5.convert(deviceCode.codeUnits).toString(); - print('Debug: TimeStamp: $timeStamp, MD5TimeStamp: $md5TimeStamp, MD5DeviceCode: $md5DeviceCode'); // generate token String token = @@ -183,14 +161,12 @@ class CoolApk extends AppSource { String base64Token = base64.encode(token.codeUnits); String md5Base64Token = md5.convert(base64Token.codeUnits).toString(); String md5Token = md5.convert(token.codeUnits).toString(); - print('Debug: Token: $token, Base64Token: $base64Token, MD5Base64Token: $md5Base64Token'); // generate salt and hash String bcryptSalt = '\$2a\$10\$${base64TimeStamp.substring(0, 14)}/${md5Token.substring(0, 6)}u'; String bcryptResult = BCrypt.hashpw(md5Base64Token, bcryptSalt); String reBcryptResult = bcryptResult.replaceRange(0, 3, '\$2y'); String finalToken = 'v2${base64.encode(reBcryptResult.codeUnits)}'; - print('Debug: BCryptSalt: $bcryptSalt, BCryptResult: $bcryptResult, FinalToken: $finalToken'); return {'deviceCode': deviceCode, 'token': finalToken}; }