Compare commits

..

15 Commits

Author SHA1 Message Date
Imran
10634e8ed2 Merge pull request #2225 from ImranR98/dev
- Ensure headers are still sent when URL request is redirected (#1973)
- Add 'ETag header' option for HTML and direct APK links (#2221)
- Ensure links on add app page do not overlap/merge (#2216)
2025-04-05 21:22:07 -04:00
Imran Remtulla
2317c5e0d3 Bugfixes 2025-04-05 21:19:52 -04:00
Imran Remtulla
e9dfb494f7 Upgrade packages, increment version 2025-04-05 21:01:43 -04:00
Imran Remtulla
71ca60244b Ensure links on add app page do not overlap/merge (#2216) 2025-04-05 14:39:12 -04:00
Imran Remtulla
94ab83ff75 Add 'ETag header' option for HTML and direct APK links (#2221) - needs testing 2025-04-05 14:32:26 -04:00
Imran Remtulla
2aa91ed535 typo 2025-04-05 14:12:57 -04:00
Imran Remtulla
23a72f1c83 Add Catalan to language menu 2025-04-05 04:52:46 -04:00
Imran Remtulla
12a25d5cbc Merge remote-tracking branch 'origin/main' into dev 2025-04-05 04:50:01 -04:00
Imran Remtulla
eb57d150b4 Ensure headers are still sent when URL request is redirected (#1973) 2025-04-05 04:49:54 -04:00
Imran
5dc8461341 Merge pull request #2223 from Cambrells/main
Catalan translation
2025-04-05 04:47:07 -04:00
Imran
5cf751caa8 Merge pull request #2211 from summoner001/main
Update hu.json
2025-04-05 04:46:55 -04:00
Imran
526ebdfc32 Merge pull request #2218 from Nishantsingh11/fix/auto-export-on-changes-2209
fix: automatically export on changes (#2209)
2025-04-05 04:46:43 -04:00
Cambrells
48f2164fe0 Add files via upload 2025-04-03 17:49:58 +02:00
Nishantsingh11
0d4bc44aa4 fix: automatically export on changes (#2209)
- Added functionality to auto-export on file changes
- Ensured smooth integration with existing export flow
- Tested thoroughly to confirm behavior
2025-04-01 13:26:31 +05:30
summoner001
e78961f0ab Update hu.json
Update translation
2025-03-30 13:18:43 +02:00
11 changed files with 516 additions and 47 deletions

388
assets/translations/ca.json Normal file
View File

@@ -0,0 +1,388 @@
{
"invalidURLForSource": "L'URL de l'aplicació {} no es vàlid",
"noReleaseFound": "No s'ha pogut trobar una versió adequada",
"noVersionFound": "No s'ha pogut determinar la versió",
"urlMatchesNoSource": "L'URL no coincideix amb cap font coneguda",
"cantInstallOlderVersion": "No és possible instal·lar una versió més antiga de l'aplicació",
"appIdMismatch": "L'ID del paquet descarregat no coincideix amb l'ID de l'aplicació instal·lada",
"functionNotImplemented": "Aquesta classe no ha implentat aquesta funció",
"placeholder": "Espai reservat",
"someErrors": "S'han produït alguns errors",
"unexpectedError": "Error inesperat",
"ok": "Accepta",
"and": "i",
"githubPATLabel": "Token d'accés personal a GitHub (augmenta el límit d'accés)",
"includePrereleases": "Inclou les versions preliminars",
"fallbackToOlderReleases": "Torna a les versions anteriors",
"filterReleaseTitlesByRegEx": "Filtra el títol de la versió per una expressió regular",
"invalidRegEx": "Expressió regular invàlida",
"noDescription": "Sense descripció",
"cancel": "Cancel·la",
"continue": "Continua",
"requiredInBrackets": "(requerit)",
"dropdownNoOptsError": "ERROR: EL DESPLEGABLE HA DE TENIR ALMENYS UNA OPCIÓ",
"colour": "Color",
"standard": "Estàndard",
"custom": "Personalitzat",
"useMaterialYou": "Usa 'Material You'",
"githubStarredRepos": "Repositoris favorits de GitHub",
"uname": "Nom d'usuari",
"wrongArgNum": "Nombre d'arguments proveït invàlid",
"xIsTrackOnly": "{} és només per a seguiment",
"source": "Font",
"app": "Aplicació",
"appsFromSourceAreTrackOnly": "Les aplicacions d'aquesta font són només per a seguiment.",
"youPickedTrackOnly": "Has seleccionat l'opció 'només per a seguiment'.",
"trackOnlyAppDescription": "Farem el seguiment de les actualitzacions per a l'aplicació, però Obtainium no podrà descarregar-la ni actualitzar-la.",
"cancelled": "Cancel·lat",
"appAlreadyAdded": "Aplicació ja afegida",
"alreadyUpToDateQuestion": "Aplicació ja actualitzada?",
"addApp": "Afegeix l'aplicació",
"appSourceURL": "URL font de l'aplicació",
"error": "Error",
"add": "Afegeix",
"searchSomeSourcesLabel": "Cerca (només algunes fonts)",
"search": "Cerca",
"additionalOptsFor": "Opcions addicionals per a {}",
"supportedSources": "Fonts suportades",
"trackOnlyInBrackets": "(només per a seguiment)",
"searchableInBrackets": "(permet la cerca)",
"appsString": "Aplicacions",
"noApps": "No hi ha aplicacions",
"noAppsForFilter": "No hi ha aplicacions per filtrar",
"byX": "Per: {}",
"percentProgress": "Progrés: {} %",
"pleaseWait": "Espera...",
"updateAvailable": "Actualització disponible",
"notInstalled": "No instal·lat",
"pseudoVersion": "pseudo-versió",
"selectAll": "Selecciona-ho tot",
"deselectX": "Desselecciona {}",
"xWillBeRemovedButRemainInstalled": "{} s'eliminarà d'Obtainium però romandrà instal·lada al dispositiu.",
"removeSelectedAppsQuestion": "Elimino les aplicacions seleccionades?",
"removeSelectedApps": "Elimina les aplicacions seleccionades",
"updateX": "Actualitza {}",
"installX": "Instal·la {}",
"markXTrackOnlyAsUpdated": "Marca {}\n(només per a seguiment)\ncom a actualitzada",
"changeX": "Canvia {}",
"installUpdateApps": "Instal·la/actualitza les aplicacions",
"installUpdateSelectedApps": "Instal·la/actualitza les aplicacions seleccionades",
"markXSelectedAppsAsUpdated": "Marco {} les aplicaciones seleccionades com a actualitzades?",
"no": "No",
"yes": "Sí",
"markSelectedAppsUpdated": "Marca les aplicacions seleccionades com a actualitzades",
"pinToTop": "Ancora-la al capdamunt",
"unpinFromTop": "Desancora-la del capdamunt",
"resetInstallStatusForSelectedAppsQuestion": "Restableixo l'estat d'instal·lació per a les aplicacions seleccionades?",
"installStatusOfXWillBeResetExplanation": "Es restablirà l'estat d'instal·lació de les aplicacions seleccionades.\n\nAçò pot ser útil quan la versió de l'aplicació mostrada per Obtainium és incorrecta a conseqüència d'una actualització no reeixida o d'algun altre problema.",
"customLinkMessage": "Aquests enllaços funcionen en dispositius amb Obtainium instal·lat",
"shareAppConfigLinks": "Comparteix la configuració de l'aplicació com a enllaç HTML",
"shareSelectedAppURLs": "Comparteix els URL de les aplicacions seleccionades",
"resetInstallStatus": "Restableix l'estat de la instal·lació",
"more": "Més",
"removeOutdatedFilter": "Elimina el filtre d'aplicacions desactualitzades",
"showOutdatedOnly": "Mostra només les aplicacions desactualitzades",
"filter": "Filtra",
"filterApps": "Filtra les aplicacions",
"appName": "Nom de l'aplicació",
"author": "Autor",
"upToDateApps": "Aplicacions actualizades",
"nonInstalledApps": "Aplicacions no instal·lades",
"importExport": "Importa/exporta",
"settings": "Paràmetres",
"exportedTo": "Exportat a {}",
"obtainiumExport": "Exporta Obtainium",
"invalidInput": "Entrada no vàlida",
"importedX": "Importat {}",
"obtainiumImport": "Importa Obtainium",
"importFromURLList": "Importa des de la llista d'URL",
"searchQuery": "Terme de cerca",
"appURLList": "Llista d'URL d'aplicacions",
"line": "Línia",
"searchX": "Cerca {}",
"noResults": "No hi ha resultats",
"importX": "Importa des de {}",
"importedAppsIdDisclaimer": "Les aplicacions importades podrien mostrar-se incorrectament com a «No instal·lada».\nPer solventar-ho reinstal·la-les a través d'Obtainium.\nAixò no hauria d'afectar les dades de les aplicacions.\n\nNomés afecta els URL i els mètodes d'importació de tercers.",
"importErrors": "Errors d'importació",
"importedXOfYApps": "{} de {} aplicacions importades.",
"followingURLsHadErrors": "Els següents URLs han tingut problemes:",
"selectURL": "Selecciona URL",
"selectURLs": "Selecciona URLs",
"pick": "Escull",
"theme": "Tema",
"dark": "Fosc",
"light": "Clar",
"followSystem": "Segueix el sistema",
"followSystemThemeExplanation": "Seguir el tema del sistema només és possible si uses aplicacions de tercers",
"useBlackTheme": "Fes servir el negre pur en el tema fosc",
"appSortBy": "Ordena les aplicacions per",
"authorName": "Autor/nom",
"nameAuthor": "Nom/Autor",
"asAdded": "Per l'ordre en què es van afegir",
"appSortOrder": "Per ordre de classificació",
"ascending": "Ascendent",
"descending": "Descendent",
"bgUpdateCheckInterval": "Comprova les actualitzacions en segon pla",
"neverManualOnly": "Mai, només manual",
"appearance": "Aparença",
"showWebInAppView": "Mostra el web d'origen en la vista de l'aplicació",
"pinUpdates": "Ancora les actualitzacions al capdamunt de les aplicacions",
"updates": "Actualitzacions",
"sourceSpecific": "Font específica",
"appSource": "Font de l'aplicació",
"noLogs": "Cap registre",
"appLogs": "Registres de l'aplicació",
"close": "Tanca",
"share": "Comparteix",
"appNotFound": "No s'ha trobat l'aplicació",
"obtainiumExportHyphenatedLowercase": "Exportació d'Obtainium",
"pickAnAPK": "Escull una APK",
"appHasMoreThanOnePackage": "{} té més d'un paquet:",
"deviceSupportsXArch": "Aquest dispositiu admet l'aquitectura de CPU: {}.",
"deviceSupportsFollowingArchs": "Aquest dispositiu admet les següents arquitectures de CPU:",
"warning": "Avís",
"sourceIsXButPackageFromYPrompt": "La font de l'aplicació és «{}» però el paquet de l'actualització ve de «{}». Vols continuar?",
"updatesAvailable": "Actualitzacions disponibles",
"updatesAvailableNotifDescription": "Notifica l'usuari que hi ha actualitzacions per a una o més aplicacions seguides per Obtainium",
"noNewUpdates": "No hi ha noves actualitzacions.",
"xHasAnUpdate": "{} té una actualització.",
"appsUpdated": "Aplicacions actualitzades",
"appsNotUpdated": "Error en actualitzar les aplicacions",
"appsUpdatedNotifDescription": "Notifica l'usuari que una o més aplicacions s'han actualitzat en segon pla",
"xWasUpdatedToY": "{} s'ha actualitzat a {}.",
"xWasNotUpdatedToY": "Error en actualitzar {} a {}.",
"errorCheckingUpdates": "Error en cercar actualitzacions",
"errorCheckingUpdatesNotifDescription": "Una notificació que es mostra quan la comprovació d'actualizacions en segon pla ha fallat",
"appsRemoved": "Aplicacions suprimides",
"appsRemovedNotifDescription": "Notifica l'usuari que una o més aplicacions s'han suprimit per errors en carregar-les",
"xWasRemovedDueToErrorY": "{} s'ha suprimit per aquest error: {}",
"completeAppInstallation": "Instal·lació completa de l'aplicació",
"obtainiumMustBeOpenToInstallApps": "Obtainium ha d'estar obert per poder instal·lar aplicacions",
"completeAppInstallationNotifDescription": "Demana l'usuari de tornar a Obtainium per acabar d'instal·lar una aplicació",
"checkingForUpdates": "S'estan cercant actualitzacions...",
"checkingForUpdatesNotifDescription": "Notificació temporal que apareix en cercar actualitzacions",
"pleaseAllowInstallPerm": "Permet que Obtainium instal·li aplicacions",
"trackOnly": "Només per a seguiment",
"errorWithHttpStatusCode": "Error {}",
"versionCorrectionDisabled": "Correcció de versions desactivada (el plugin sembla que no funciona)",
"unknown": "Desconegut",
"none": "Cap",
"never": "Mai",
"latestVersionX": "Última versió: {}",
"installedVersionX": "Versió instal·lada: {}",
"lastUpdateCheckX": "Última comprovació d'actualització: {}",
"remove": "Suprimeix",
"yesMarkUpdated": "Sí, marca com a actualitzada",
"fdroid": "Repositori oficial F-Droid",
"appIdOrName": "ID o nom de l'aplicació",
"appId": "ID de l'aplicació",
"appWithIdOrNameNotFound": "No s'han trobat aplicacions amb aquest ID o nom",
"reposHaveMultipleApps": "Els repositoris poden contenir diverses aplicacions",
"fdroidThirdPartyRepo": "Repositori F-Droid de tercers",
"install": "Instal·la",
"markInstalled": "Marca com a instal·lada",
"update": "Actualitza",
"markUpdated": "Marca com a actualitzada",
"additionalOptions": "Opcions addicionals",
"disableVersionDetection": "Desactiva la detecció de versions",
"noVersionDetectionExplanation": "Només has d'usar aquesta opció en les aplicacions en què la detecció de versions no funcioni correctament.",
"downloadingX": "Descarregant {}",
"downloadX": "Descarrega {}",
"downloadedX": "Descarregada {}",
"releaseAsset": "Recurs actualitzat",
"downloadNotifDescription": "Notifica l'usuari del progrés de la descàrrega d'una aplicació",
"noAPKFound": "No s'ha trobat l'APK",
"noVersionDetection": "No s'han detectat versions",
"categorize": "Categoritza",
"categories": "Categories",
"category": "Categoria",
"noCategory": "No hi ha la categoria",
"noCategories": "No hi ha les categories",
"deleteCategoriesQuestion": "Suprimeixo les categories?",
"categoryDeleteWarning": "Totes les aplicacions de les categories suprimides es marcaran com a no categoritzades.",
"addCategory": "Afegeix una categoria",
"label": "Nom",
"language": "Idioma",
"copiedToClipboard": "Copiat al porta-retalls",
"storagePermissionDenied": "Permís d'emmagatzematge denegat",
"selectedCategorizeWarning": "Açò substituirà els paràmetres de categorització per a les aplicacions selecionades.",
"filterAPKsByRegEx": "Filtra les APKs per l'expressió regular",
"removeFromObtainium": "Suprimeix d'Obtainium",
"uninstallFromDevice": "Desinstal·la del dispositiu",
"onlyWorksWithNonVersionDetectApps": "Només funciona per a aplicacions amb la detecció de versions desactivada.",
"releaseDateAsVersion": "Usa la data de llançament com a cadena de la versió",
"releaseTitleAsVersion": "Usa el títol com a cadena de la versió",
"releaseDateAsVersionExplanation": "Aquest opció només s'hauria d'usar per a aplicacions en què la detecció de la versió no funciona correctament però disposem de la data de publicació.",
"changes": "Canvis",
"releaseDate": "Data de publicació",
"importFromURLsInFile": "Importa els URLs des d'un fitxer (com ara OPML)",
"versionDetectionExplanation": "Concilia la cadena de la versió amb la versió detectada del Sistema Operatiu",
"versionDetection": "Detecció de la versió",
"standardVersionDetection": "Detecció de la versió estàndard",
"groupByCategory": "Agrupa per categories",
"autoApkFilterByArch": "Intenta filtrar les APKs per l'aquitectura de la CPU, si és possible",
"autoLinkFilterByArch": "Intenta filtrar els enllaços per l'aquitectura de la CPU, si és possible",
"overrideSource": "Força la font",
"dontShowAgain": "No ho tornis a mostrar",
"dontShowTrackOnlyWarnings": "No mostris avisos de les aplicacions 'només per a seguiment'",
"dontShowAPKOriginWarnings": "No mostris avisos dels orígens de les APKs",
"moveNonInstalledAppsToBottom": "Desplaça les aplicacions no instal·lades al capdavall de les aplicacions",
"gitlabPATLabel": "Token d'accés personal a GitLab",
"about": "Quant a",
"requiresCredentialsInSettings": "{} requereix credencials addicionals (a Paràmetres)",
"checkOnStart": "Comprova si hi ha actualitzacions en iniciar Obtainium",
"tryInferAppIdFromCode": "Intenta deduir l'ID de l'aplicació des del codi font",
"removeOnExternalUninstall": "Suprimeix de forma automàtica les aplicacions desinstal·lades externament",
"pickHighestVersionCode": "Selecciona de forma automàtica la versió superior de l'APK",
"checkUpdateOnDetailPage": "Comprova les actualitzacions en obrir la pàgina de detalls de l'aplicació",
"disablePageTransitions": "Inhabilita les animacions de transició de pàgina",
"reversePageTransitions": "Inverteix les animacions de transició de pàgina",
"minStarCount": "Nombre mínim d'estrelles",
"addInfoBelow": "Afegeix aquesta informació a sota.",
"addInfoInSettings": "Afegeix aquesta informació a Paràmetres.",
"githubSourceNote": "La limitació de peticions a GitHub es pot evitar amb una clau API.",
"sortByLastLinkSegment": "Ordena per 'només el darrer fragment de l'enllaç'",
"filterReleaseNotesByRegEx": "Filtra les notes de la publicació de la versió per una expressió regular",
"customLinkFilterRegex": "Filtre personalitzat de l'enllaç de l'APK per una expressió regular (Per_defecte '.apk$')",
"appsPossiblyUpdated": "S'ha intentat l'actualització de l'aplicació",
"appsPossiblyUpdatedNotifDescription": "Notifica l'usuari que les actualitzacions per a una o més aplicacions podrien haver-se fet en segon pla",
"xWasPossiblyUpdatedToY": "{} podria haver-se actualitzat a {}.",
"enableBackgroundUpdates": "Habilita les actualizacions en segon pla",
"backgroundUpdateReqsExplanation": "Les actualitzacions en segon pla és possible que no estiguin disponibles per a totes les aplicacions.",
"backgroundUpdateLimitsExplanation": "Les instal·lacions en segon pla reexides només es poden comprovar amb Obtainium obert.",
"verifyLatestTag": "Comprova l'etiqueta 'Latest' (última versió)",
"intermediateLinkRegex": "Filtra per un enllaç 'intermediari' per anar-hi",
"filterByLinkText": "Filtra els enllaços pel text de l'enllaç",
"intermediateLinkNotFound": "No s'ha trobat l'enllaç intermediari",
"intermediateLink": "Enllaç intermediari",
"exemptFromBackgroundUpdates": "Exempta d'actualitzacions en segon pla (si han estat habilitades)",
"bgUpdatesOnWiFiOnly": "Inhabilita les actualitzacions en segon pla sense Wi-Fi",
"bgUpdatesWhileChargingOnly": "Inhabilita les actualitzacions en segon pla quan no s'estigui carregant el mòbil",
"autoSelectHighestVersionCode": "Selecciona de forma automàtica la versió més recent de l'APK",
"versionExtractionRegEx": "Extracció de la cadena de la versió amb una expressió regular",
"trimVersionString": "Retalla la cadena de la versió amb una expressió regular",
"matchGroupToUseForX": "Grup de coincidència a usar per a \"{}\"",
"matchGroupToUse": "Grup de coincidència a usar per a l'extracció de la cadena de la versió amb una expressió regular",
"highlightTouchTargets": "Ressalta els elements de selecció menys obvis",
"pickExportDir": "Selecciona el directori d'exportació",
"autoExportOnChanges": "Exporta automàticament quan hi hagi canvis",
"includeSettings": "Inclou paràmetres",
"filterVersionsByRegEx": "Filtra les versions per una expressió regular",
"trySelectingSuggestedVersionCode": "Prova a seleccionar la versió de l'APK suggerida",
"dontSortReleasesList": "Mantén l'ordre de publicació de l'API",
"reverseSort": "Ordre invers",
"takeFirstLink": "Usa el primer enllaç",
"skipSort": "Omet l'ordre",
"debugMenu": "Menú de depuració",
"bgTaskStarted": "S'ha iniciat la tasca en segon pla (revisa-ho als registres).",
"runBgCheckNow": "Executa la comprovació d'actualitzacions en segon pla",
"versionExtractWholePage": "Aplica l'extracció de la cadena de la versió amb una expressió regular a tota la pàgina",
"installing": "Instal·lant",
"skipUpdateNotifications": "No notifiquis les actualitzacions",
"updatesAvailableNotifChannel": "Actualitzacions disponibles",
"appsUpdatedNotifChannel": "Aplicacions actualitzades",
"appsPossiblyUpdatedNotifChannel": "S'ha intentat actualitzar l'aplicació",
"errorCheckingUpdatesNotifChannel": "Error en cercar actualitzacions",
"appsRemovedNotifChannel": "Aplicacions suprimides",
"downloadingXNotifChannel": "Descarregant {}",
"completeAppInstallationNotifChannel": "Instal·lació finalitzada",
"checkingForUpdatesNotifChannel": "S'estan cercant actualitzacions",
"onlyCheckInstalledOrTrackOnlyApps": "Comprova les actualitzacions només per a aplicacions instal·lades o en seguiment",
"supportFixedAPKURL": "Suport per als URLs fixos de l'APK",
"selectX": "Selecciona {}",
"parallelDownloads": "Permet les descàrregues paralel·les",
"useShizuku": "Usa Shizuku o Sui per instal·lar",
"shizukuBinderNotFound": "Shizuku no s'està executant",
"shizukuOld": "Versió antiga de Shizuku (<11) - Actualitza-la",
"shizukuOldAndroidWithADB": "Shizuku s'executa en Android < 8.1 amb ADB - Actualitza Android o usa Sui com a alternativa",
"shizukuPretendToBeGooglePlay": "Defineix Google Play com a font d'instal·lació (si uses Shizuku)",
"useSystemFont": "Usa la font del sistema",
"useVersionCodeAsOSVersion": "Usa la versió de l'aplicació com a versió detectada del Sistema Operatiu",
"requestHeader": "Capçalera de sol·licitud",
"useLatestAssetDateAsReleaseDate": "Usa el darrer recurs carregat com a data de llançament",
"defaultPseudoVersioningMethod": "Mètode de pseudo-versionat predeterminat",
"partialAPKHash": "Hash de l'APK parcial",
"APKLinkHash": "Hash de l'enllaç de l'APK",
"directAPKLink": "Enllaç de l'APK directe",
"pseudoVersionInUse": "S'està usant una pseudo-versió",
"installed": "Instal·lada",
"latest": "Versió més recent",
"invertRegEx": "Inverteix l'expressió regular",
"note": "Nota",
"selfHostedNote": "El desplegable «{}» es pot usar per accedir a instàncies autoallotjades/personalitzades de qualsevol font.",
"badDownload": "L'APK no s'ha pogut analitzar (incompatible o descàrrega parcial)",
"beforeNewInstallsShareToAppVerifier": "Comparteix les aplicacions noves amb AppVerifier (si està instal·lat)",
"appVerifierInstructionToast": "Comparteix amb AppVerifier i torna aquí quan estigui fet.",
"wiki": "Ajuda/Wiki",
"crowdsourcedConfigsLabel": "Configuració de les aplicacions crowdsourcing (usa-ho sota la teva responsabilitat)",
"crowdsourcedConfigsShort": "Configuració de les aplicacions crowdsourcing",
"allowInsecure": "Permet les sol·licituds HTTP insegures",
"stayOneVersionBehind": "Roman a la versió anterior a l'última",
"refreshBeforeDownload": "Actualitza les dades de l'aplicació abans de descarregar-la",
"tencentAppStore": "Tencent App Store",
"name": "Nom",
"smartname": "Nom (smart)",
"sortMethod": "Mètode d'ordenació",
"welcome": "Benvinguda",
"documentationLinksNote": "La pàgina GitHub d'Obtainium enllaçada a sota conté enllaços a vídeos, articles, debats i altres recursos que t'ajudaran a entendre com usar l'aplicació.",
"removeAppQuestion": {
"one": "¿Suprimeixo l'aplicació?",
"other": "¿Suprimeixo les aplicacions?"
},
"tooManyRequestsTryAgainInMinutes": {
"one": "Massa peticions (límit excedit), torna-hi en {} minut",
"other": "Massa peticions (límit excedit), torna-hi en {} minuts"
},
"bgUpdateGotErrorRetryInMinutes": {
"one": "La comprovació d'actualitzacions en segon pla ha trobat un {}, es tornarà a provar en {} minut",
"other": "La comprovació d'actualitzacions en segon pla ha trobat un {}, es tornarà a provar en {} minuts"
},
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "La comprovació d'actualitzacions en segon pla ha trobat {} actualització, t'ho notificarem si cal",
"other": "La comprovació d'actualitzacions en segon pla ha trobat {} actualitzacions, t'ho notificarem si cal"
},
"apps": {
"one": "{} Aplicació",
"other": "{} Aplicacions"
},
"url": {
"one": "{} URL",
"other": "{} URLs"
},
"minute": {
"one": "{} Minut",
"other": "{} Minuts"
},
"hour": {
"one": "{} Hora",
"other": "{} Hores"
},
"day": {
"one": "{} Dia",
"other": "{} Dies"
},
"clearedNLogsBeforeXAfterY": {
"one": "Suprimit {n} registre (anterior a = {before}, posterior a = {after})",
"other": "Suprimits {n} registres (anteriors a = {before}, posteriors a = {after})"
},
"xAndNMoreUpdatesAvailable": {
"one": "{} i 1 aplicació més tenen actualitzacions.",
"other": "{} i {} aplicacions més tenen actualitzacions."
},
"xAndNMoreUpdatesInstalled": {
"one": "{} i 1 aplicació més s'han actualitzat.",
"other": "{} i {} aplicacions més s'han actualitzat."
},
"xAndNMoreUpdatesFailed": {
"one": "Error en actualitzar {} i 1 aplicació més.",
"other": "No s'ha pogut actualizar {} i {} aplicacions més."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} i 1 aplicació més podrien haver estat actualizades.",
"other": "{} i {} aplicacions més podrien haver estat actualitzades."
},
"apk": {
"one": "{} APK",
"other": "{} APKs"
}
}

View File

@@ -265,7 +265,7 @@
"matchGroupToUse": "Match group to use for version string extraction RegEx",
"highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Pick export directory",
"autoExportOnChanges": "Auto-export on changes",
"autoExportOnChanges": "Automatically export on changes",
"includeSettings": "Include settings",
"filterVersionsByRegEx": "Filter versions by regular expression",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
@@ -385,4 +385,4 @@
"one": "{} APK",
"other": "{} APKs"
}
}
}

View File

@@ -314,8 +314,8 @@
"beforeNewInstallsShareToAppVerifier": "Új alkalmazások megosztása az AppVerifierrel (ha elérhető)",
"appVerifierInstructionToast": "Ossza meg az AppVerifierrel, majd térjen vissza ide, ha kész.",
"wiki": "Súgó/Wiki",
"crowdsourcedConfigsLabel": "Crowdsource-ből származó alkalmazások beállítása (saját felelősségére használja)",
"crowdsourcedConfigsShort": "Crowdsourced App Configurations",
"crowdsourcedConfigsLabel": "Közreműködők által összeállított alkalmazásbeállítások (saját felelősségére használja)",
"crowdsourcedConfigsShort": "Alkalmazáslista",
"allowInsecure": "Nem biztonságos HTTP-kérések engedélyezése",
"stayOneVersionBehind": "Maradjon egy verzióval a legújabb mögött",
"refreshBeforeDownload": "Az alkalmazás adatainak frissítése a letöltés előtt",

View File

@@ -1,5 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:obtainium/app_sources/html.dart';
import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
@@ -8,12 +9,23 @@ class DirectAPKLink extends AppSource {
DirectAPKLink() {
name = tr('directAPKLink');
additionalSourceAppSpecificSettingFormItems = html
.additionalSourceAppSpecificSettingFormItems
.where((element) => element
.where((element) => element.key == 'requestHeader')
.isNotEmpty)
.toList();
additionalSourceAppSpecificSettingFormItems = [
...html.additionalSourceAppSpecificSettingFormItems
.where((element) => element
.where((element) => element.key == 'requestHeader')
.isNotEmpty)
.toList(),
[
GeneratedFormDropdown(
'defaultPseudoVersioningMethod',
[
MapEntry('partialAPKHash', tr('partialAPKHash')),
MapEntry('ETag', 'ETag')
],
label: tr('defaultPseudoVersioningMethod'),
defaultValue: 'partialAPKHash')
]
];
excludeCommonSettingKeys = [
'versionExtractionRegEx',
'matchGroupToUse',
@@ -57,9 +69,8 @@ class DirectAPKLink extends AppSource {
additionalSettingsNew[s] = additionalSettings[s];
}
}
additionalSettingsNew['defaultPseudoVersioningMethod'] = 'partialAPKHash';
additionalSettingsNew['directAPKLink'] = true;
additionalSettings['versionDetection'] = false;
additionalSettingsNew['versionDetection'] = false;
return html.getLatestAPKDetails(standardUrl, additionalSettingsNew);
}
}

View File

@@ -263,7 +263,8 @@ class HTML extends AppSource {
'defaultPseudoVersioningMethod',
[
MapEntry('partialAPKHash', tr('partialAPKHash')),
MapEntry('APKLinkHash', tr('APKLinkHash'))
MapEntry('APKLinkHash', tr('APKLinkHash')),
MapEntry('ETag', 'ETag')
],
label: tr('defaultPseudoVersioningMethod'),
defaultValue: 'partialAPKHash')
@@ -356,14 +357,24 @@ class HTML extends AppSource {
additionalSettings['versionExtractWholePage'] == true
? versionExtractionWholePageString
: relDecoded);
version ??= additionalSettings['defaultPseudoVersioningMethod'] ==
'APKLinkHash'
? rel.hashCode.toString()
: (await checkPartialDownloadHashDynamic(rel,
headers: await getRequestHeaders(additionalSettings,
forAPKDownload: true),
allowInsecure: additionalSettings['allowInsecure'] == true))
.toString();
var apkReqHeaders =
await getRequestHeaders(additionalSettings, forAPKDownload: true);
if (version == null &&
additionalSettings['defaultPseudoVersioningMethod'] == 'ETag') {
version = await checkETagHeader(rel,
headers: apkReqHeaders,
allowInsecure: additionalSettings['allowInsecure'] == true);
if (version == null) {
throw NoVersionError();
}
}
version ??=
additionalSettings['defaultPseudoVersioningMethod'] == 'APKLinkHash'
? rel.hashCode.toString()
: (await checkPartialDownloadHashDynamic(rel,
headers: apkReqHeaders,
allowInsecure: additionalSettings['allowInsecure'] == true))
.toString();
return APKDetails(
version,
[rel].map((e) {

View File

@@ -46,6 +46,7 @@ List<MapEntry<Locale, String>> supportedLocales = const [
'Esperanto'), // https://github.com/aissat/easy_localization/issues/220#issuecomment-846035493
MapEntry(Locale('in'), 'Bahasa Indonesia'),
MapEntry(Locale('ko'), '한국어'),
MapEntry(Locale('ca'), 'Català'),
];
const fallbackLocale = Locale('en');
const localeDir = 'assets/translations';

View File

@@ -575,8 +575,10 @@ class AddAppPageState extends State<AddAppPage> {
Widget getSourcesListWidget() => Padding(
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
child: Wrap(
direction: Axis.horizontal,
alignment: WrapAlignment.spaceBetween,
spacing: 12,
children: [
GestureDetector(
onTap: () {

View File

@@ -220,6 +220,22 @@ Future<String> checkPartialDownloadHash(String url, int bytesToGrab,
return hashListOfLists(bytes);
}
Future<String?> checkETagHeader(String url,
{Map<String, String>? headers, bool allowInsecure = false}) async {
// Send the initial request but cancel it as soon as you have the headers
var reqHeaders = headers ?? {};
var req = Request('GET', Uri.parse(url));
req.headers.addAll(reqHeaders);
var client = IOClient(createHttpClient(allowInsecure));
StreamedResponse response = await client.send(req);
var resHeaders = response.headers;
client.close();
return resHeaders[HttpHeaders.etagHeader]
?.replaceAll('"', '')
.hashCode
.toString();
}
Future<File> downloadFile(String url, String fileName, bool fileNameHasExt,
Function? onProgress, String destDir,
{bool useExisting = true,

View File

@@ -3,12 +3,13 @@
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'dart:typed_data';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:html/dom.dart';
import 'package:http/http.dart';
import 'package:http/io_client.dart';
import 'package:obtainium/app_sources/apkmirror.dart';
import 'package:obtainium/app_sources/apkpure.dart';
import 'package:obtainium/app_sources/aptoide.dart';
@@ -566,23 +567,62 @@ abstract class AppSource {
String url, Map<String, dynamic> additionalSettings,
{bool followRedirects = true, Object? postBody}) async {
var requestHeaders = await getRequestHeaders(additionalSettings);
if (requestHeaders != null || followRedirects == false) {
var req = Request(postBody == null ? 'GET' : 'POST', Uri.parse(url));
req.followRedirects = followRedirects;
if (requestHeaders != null) {
req.headers.addAll(requestHeaders);
var method = postBody == null ? 'GET' : 'POST';
var currentUrl = url;
var redirectCount = 0;
const maxRedirects = 10;
while (redirectCount < maxRedirects) {
var httpClient =
createHttpClient(additionalSettings['allowInsecure'] == true);
var request = await httpClient.openUrl(method, Uri.parse(currentUrl));
if (requestHeaders != null) {
requestHeaders.forEach((key, value) {
request.headers.set(key, value);
});
}
request.followRedirects = false;
if (postBody != null) {
request.headers.contentType = ContentType.json;
request.write(jsonEncode(postBody));
}
final response = await request.close();
if (followRedirects &&
(response.statusCode == 301 || response.statusCode == 302)) {
final location = response.headers.value(HttpHeaders.locationHeader);
if (location != null) {
currentUrl = location;
redirectCount++;
httpClient.close();
continue;
}
}
final bytes = (await response.fold<BytesBuilder>(
BytesBuilder(), (b, d) => b..add(d)))
.toBytes();
final headers = <String, String>{};
response.headers.forEach((name, values) {
headers[name] = values.join(', ');
});
httpClient.close();
return http.Response.bytes(
bytes,
response.statusCode,
headers: headers,
request: http.Request(method, Uri.parse(url)),
);
}
if (postBody != null) {
req.headers[HttpHeaders.contentTypeHeader] = 'application/json';
req.body = jsonEncode(postBody);
}
return Response.fromStream(await IOClient(
createHttpClient(additionalSettings['allowInsecure'] == true))
.send(req));
throw ObtainiumError('Too many redirects ($maxRedirects)');
} else {
return postBody == null
? get(Uri.parse(url))
: post(Uri.parse(url), body: jsonEncode(postBody));
? http.get(Uri.parse(url))
: http.post(Uri.parse(url), body: jsonEncode(postBody));
}
}

View File

@@ -304,10 +304,10 @@ packages:
dependency: "direct main"
description:
name: file_picker
sha256: "09b474c0c8117484b80cbebc043801ff91e05cfbd2874d512825c899e1754694"
sha256: "36a1652d99cb6bf8ccc8b9f43aded1fd60b234d23ce78af422c07f950a436ef7"
url: "https://pub.dev"
source: hosted
version: "9.2.3"
version: "10.0.0"
fixnum:
dependency: transitive
description:
@@ -490,10 +490,10 @@ packages:
dependency: "direct main"
description:
name: flutter_markdown
sha256: e7bbc718adc9476aa14cfddc1ef048d2e21e4e8f18311aaac723266db9f9e7b5
sha256: "634622a3a826d67cb05c0e3e576d1812c430fa98404e95b60b131775c73d76ec"
url: "https://pub.dev"
source: hosted
version: "0.7.6+2"
version: "0.7.7"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
@@ -1107,10 +1107,10 @@ packages:
dependency: transitive
description:
name: url_launcher_ios
sha256: "16a513b6c12bb419304e72ea0ae2ab4fed569920d1c7cb850263fe3acc824626"
sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb"
url: "https://pub.dev"
source: hosted
version: "6.3.2"
version: "6.3.3"
url_launcher_linux:
dependency: transitive
description:
@@ -1211,10 +1211,10 @@ packages:
dependency: transitive
description:
name: webview_flutter_wkwebview
sha256: c49a98510080378b1525132f407a92c3dcd3b7145bef04fb8137724aadcf1cf0
sha256: c14455137ce60a68e1ccaf4e8f2dae8cebcb3465ddaa2fcfb57584fb7c5afe4d
url: "https://pub.dev"
source: hosted
version: "3.18.4"
version: "3.18.5"
win32:
dependency: transitive
description:

View File

@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.1.48+2305
version: 1.1.49+2306
environment:
sdk: ^3.6.0
@@ -47,7 +47,7 @@ dependencies:
permission_handler: ^11.0.0
fluttertoast: ^8.0.9
device_info_plus: ^11.0.0
file_picker: ^9.0.0
file_picker: ^10.0.0
animations: ^2.0.4
android_package_installer: # TODO: See if PR will be accepted (dev may not be active), else remove this comment
git: