Compare commits

..

No commits in common. "main" and "3.4.0" have entirely different histories.
main ... 3.4.0

42 changed files with 706 additions and 865 deletions

View File

@ -55,7 +55,7 @@ jobs:
- uses: akkuman/gitea-release-action@v1 - uses: akkuman/gitea-release-action@v1
with: with:
files: | files: |
build/artifacts/${{ gitea.event.repository.name }}.tar.gz build/artifacts/repod.tar.gz
- uses: FKLC/sign-files-action@v1.0.0 - uses: FKLC/sign-files-action@v1.0.0
with: with:
privateKey: ${{ secrets.PRIVATEKEY }} privateKey: ${{ secrets.PRIVATEKEY }}
@ -63,13 +63,13 @@ jobs:
extension: .sig extension: .sig
outputFolder: build/artifacts outputFolder: build/artifacts
files: | files: |
build/artifacts/${{ gitea.event.repository.name }}.tar.gz build/artifacts/repod.tar.gz
- id: sign - id: sign
run: echo "SIGNATURE=$(cat build/artifacts/${{ gitea.event.repository.name }}.tar.gz.sig | openssl base64 -A)" >> $GITHUB_OUTPUT run: echo "SIGNATURE=$(cat build/artifacts/repod.tar.gz.sig | openssl base64 -A)" >> $GITHUB_OUTPUT
- uses: actionsflow/axios@v1 - uses: actionsflow/axios@v1
with: with:
url: https://apps.nextcloud.com/api/v1/apps/releases url: https://apps.nextcloud.com/api/v1/apps/releases
method: POST method: POST
accept: 200,201 accept: 200,201
headers: '{ "Authorization": "Token <<<${{ secrets.TOKEN }}>>>" }' headers: '{ "Authorization": "Token <<<${{ secrets.TOKEN }}>>>" }'
data: '{ "download": "https://git.crystalyx.net/${{ gitea.repository }}/releases/download/<<<${{ gitea.ref_name }}>>>/${{ gitea.event.repository.name }}.tar.gz", "signature": "<<<${{ steps.sign.outputs.SIGNATURE }}>>>" }' data: '{ "download": "https://git.crystalyx.net/Xefir/repod/releases/download/<<<${{ gitea.ref_name }}>>>/repod.tar.gz", "signature": "<<<${{ steps.sign.outputs.SIGNATURE }}>>>" }'

View File

@ -13,6 +13,7 @@ class MyConfig extends Config
$rules = parent::getRules(); $rules = parent::getRules();
$rules['@PhpCsFixer'] = true; $rules['@PhpCsFixer'] = true;
$rules['curly_braces_position']['classes_opening_brace'] = 'next_line_unless_newline_at_signature_end'; $rules['curly_braces_position']['classes_opening_brace'] = 'next_line_unless_newline_at_signature_end';
$rules['phpdoc_separation'] = false;
$rules['phpdoc_to_comment'] = false; $rules['phpdoc_to_comment'] = false;
return $rules; return $rules;
} }

View File

@ -1,13 +1,3 @@
## 3.4.1 - Skip & Chill - 2024-11-12
### Changed
- 💄 Make a little gap between player's controls
### Fixed
- 📝 Descriptions are now well formatted
- ⏩ Chapters supported !
> Click on a timestamp in descriptions to skip to the specified part of the podcast
## 3.4.0 - Good Night - 2024-11-09 ## 3.4.0 - Good Night - 2024-11-09
### Added ### Added

View File

@ -1,4 +1,39 @@
# https://github.com/nextcloud/appstore/blob/fixed-templates/nextcloudappstore/scaffolding/app-templates/26/app/Makefile # https://github.com/nextcloud/appstore/blob/fixed-templates/nextcloudappstore/scaffolding/app-templates/26/app/Makefile
# Generic Makefile for building and packaging a Nextcloud app which uses npm and
# Composer.
#
# Dependencies:
# * make
# * which
# * curl: used if phpunit and composer are not installed to fetch them from the web
# * tar: for building the archive
# * npm: for building and testing everything JS
#
# If no composer.json is in the app root directory, the Composer step
# will be skipped. The same goes for the package.json which can be located in
# the app root or the js/ directory.
#
# The npm command by launches the npm build script:
#
# npm run build
#
# The npm test command launches the npm test script:
#
# npm run test
#
# The idea behind this is to be completely testing and build tool agnostic. All
# build tools and additional package managers should be installed locally in
# your project, since this won't pollute people's global namespace.
#
# The following npm scripts in your package.json install and update the bower
# and npm dependencies and use gulp as build system (notice how everything is
# run from the node_modules folder):
#
# "scripts": {
# "test": "node node_modules/gulp-cli/bin/gulp.js karma",
# "prebuild": "npm install && node_modules/bower/bin/bower install && node_modules/bower/bin/bower update",
# "build": "node node_modules/gulp-cli/bin/gulp.js"
# },
app_name=$(notdir $(CURDIR)) app_name=$(notdir $(CURDIR))
build_tools_directory=$(CURDIR)/build/tools build_tools_directory=$(CURDIR)/build/tools
@ -124,11 +159,11 @@ appstore:
# Start a nextcloud server on Docker to kickstart developement # Start a nextcloud server on Docker to kickstart developement
.PHONY: dev .PHONY: dev
dev: build dev: build
docker stop $(app_name) || true docker stop repod || true
docker rm $(app_name) || true docker rm repod || true
docker build -t $(app_name) . docker build -t repod .
docker run -itd --rm --name $(app_name) -v $(CURDIR):/var/www/html/apps/$(app_name) -p 80:80 $(app_name) docker run -itd --rm --name repod -v $(CURDIR):/var/www/html/apps/repod -p 80:80 repod
npm run watch || docker stop $(app_name) npm run watch
# Generate translations # Generate translations
.PHONY: l10n .PHONY: l10n

View File

@ -6,12 +6,12 @@ You need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) inst
## Features ## Features
- 🔍 Browse and subscribe huge collection of podcasts - Browse podcasts and play them directly in Nextcloud
- 🔊 Listen to episodes directly in Nextcloud - Keep track of subscribed shows and episodes
- 📋 Keep track of subscribed shows and episodes - Sync them with GPodderSync compatible clients
- 🌐 Sync them with GPodderSync compatible clients - Import and export subscriptions
- 📱 Mobile friendly interface - Mobile friendly interface
- 📡 Import and export your subscriptions - Unified search integration
## Comparaison with similar apps for Nextcloud ## Comparaison with similar apps for Nextcloud
@ -19,32 +19,37 @@ You need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) inst
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| Actively maintened | ✅ | ✅ | ✅ | ❌ | | Actively maintened | ✅ | ✅ | ✅ | ❌ |
| Play your local music files | ❌ | ❌ | ✅ | ❌ | | Play your local music files | ❌ | ❌ | ✅ | ❌ |
| Sync with [GPodder clients](#clients-supporting-sync-of-gpoddersync) | ✅ | ✅ | [](https://github.com/owncloud/music/issues/975) | [](https://git.project-insanity.org/onny/nextcloud-app-podcast/-/issues/103) | | Sync with [GPodder clients](#clients-supporting-sync-of-gpoddersync) | ✅ | ✅ | ❌ | ❌ |
| Add and manage subscriptions | ✅ | ❌ | ✅ | ✅ | | Add and manage subscriptions | ✅ | ❌ | ✅ | ✅ |
| Listen synced episodes by another clients | ✅ | ✅ | ❌ | ❌ | | Listen synced episodes by another clients | ✅ | ✅ | ❌ | ❌ |
| Fetch and listen new epidodes | ✅ | [](https://github.com/pbek/nextcloud-nextpod/issues/5) | ✅ | ✅ | | Fetch and listen new epidodes | ✅ | | ✅ | ✅ |
| Keep track of listened episodes | ✅ | ✅ | [](https://github.com/owncloud/music/issues/1148) | ✅ | | Keep track of listened episodes | ✅ | ✅ | | ✅ |
| Download epidodes | ✅ | ✅ | ❌ | ✅ | | Download epidodes | ✅ | ✅ | ❌ | ✅ |
| Sleep timer | ✅ | ❌ | [](https://github.com/owncloud/music/issues/884#issuecomment-921582302) | ❌ | | Import and export subscriptions | ✅ | ❌ | ❌ | ❌ |
| Advanced player controls | ✅ | ❌ | ✅ | ✅ |
| Import and export subscriptions | ✅ | ❌ | [](https://github.com/owncloud/music/issues/904) | [](https://git.project-insanity.org/onny/nextcloud-app-podcast/-/issues/185) |
| Search and discover new podcasts | ✅ | ❌ | ❌ | ✅ | | Search and discover new podcasts | ✅ | ❌ | ❌ | ✅ |
| Open episode website and RSS feed | ✅ | ✅ | ❌ | ✅ | | Open episode website and RSS feed | ✅ | ✅ | ❌ | ✅ |
| Integrate with Nextcloud search engine | ✅ | ❌ | ❌ | ✅ | | Integrate with Nextcloud search engine | ✅ | ❌ | ❌ | ✅ |
| Integrate with [Nextcloud Notes](https://apps.nextcloud.com/apps/notes) | ❌ | ✅ | ❌ | ❌ | | Integrate with [Nextcloud Notes](https://apps.nextcloud.com/apps/notes) | ❌ | ✅ | ❌ | ❌ |
| Mobile friendly interface | ✅ | ❌ | ✅ | ✅ | | Mobile friendly interface | ✅ | ❌ | ✅ | ✅ |
| Support chapters | ✅ | ❌ | ❌ | ✅ | | Support chapters | ❌ | ❌ | ❌ | ✅ |
| Available in multiple languages | [](https://translate.crystalyx.net/projects/repod/gitea/) (en/fr/de) | ❌ | [](https://github.com/owncloud/music/issues/671#issuecomment-782746463) | [](https://www.transifex.com/project-insanityorg/podcast-1/dashboard/) (en/de) | | Available in multiple languages | ⭕ (en/fr/de) | ❌ | ✅ | ⭕ (en/de) |
> Click on ⭕ to open the ticket
## Screenshots ## Screenshots
<img src="./screens/index.png" width="230" title="Homepage" /> ### Homepage
<img src="./screens/discover.png" width="230" title="Discover" /> ![homepage](./screens/index.png)
<img src="./screens/search.png" width="230" title="Search" />
<img src="./screens/episodes.png" width="230" title="Episode list" /> ### Discover
<img src="./screens/modal.png" width="230" title="Episode description" /> ![homepage](./screens/discover.png)
### Search
![search](./screens/search.png)
### Episode list
![episodes](./screens/episodes.png)
### Episode description
![modal](./screens/modal.png)
## Clients supporting sync of GPodderSync ## Clients supporting sync of GPodderSync
@ -63,14 +68,6 @@ Either from the official Nextcloud [app store](https://apps.nextcloud.com/apps/r
- Conflict with Plasma Integration Firefox addon ([#164](https://git.crystalyx.net/Xefir/repod/issues/164)) - Conflict with Plasma Integration Firefox addon ([#164](https://git.crystalyx.net/Xefir/repod/issues/164))
## Translations
You can contribute to translate the app in your language and you don't need to have any development background to do so !
Please join the effort at our **[Weblate](https://translate.crystalyx.net/projects/repod/gitea/)** project.
Thank you so much if you decide to participate ❤️
## Credits ## Credits
- [GPodder Sync](https://github.com/thrillfall/nextcloud-gpodder) for the database API - [GPodder Sync](https://github.com/thrillfall/nextcloud-gpodder) for the database API

View File

@ -14,7 +14,7 @@
## Requirements ## Requirements
You need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installed to use this app!]]></description> You need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installed to use this app!]]></description>
<version>3.4.1</version> <version>3.4.0</version>
<licence>agpl</licence> <licence>agpl</licence>
<author mail="xefir@crystalyx.net" homepage="https://crystalyx.net">Michel Roux</author> <author mail="xefir@crystalyx.net" homepage="https://crystalyx.net">Michel Roux</author>
<namespace>RePod</namespace> <namespace>RePod</namespace>

View File

@ -12,18 +12,12 @@
"lint": "find . -name \\*.php -not -path './vendor/*' -not -path './vendor-bin/*' -not -path './build/*' -print0 | xargs -0 -n1 php -l", "lint": "find . -name \\*.php -not -path './vendor/*' -not -path './vendor-bin/*' -not -path './build/*' -print0 | xargs -0 -n1 php -l",
"cs:check": "php-cs-fixer fix --dry-run --diff", "cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:fix": "php-cs-fixer fix", "cs:fix": "php-cs-fixer fix",
"psalm": "psalm --threads=1 --no-cache --show-info=true", "psalm": "psalm --threads=1 --no-cache --show-info=true"
"rector": "rector && composer cs:fix"
},
"require": {
"php": "^8.1"
}, },
"require-dev": { "require-dev": {
"nextcloud/ocp": "^30.0.2", "nextcloud/ocp": "^30.0.2",
"roave/security-advisories": "dev-latest", "roave/security-advisories": "dev-latest",
"nextcloud/coding-standard": "^1.3.2", "nextcloud/coding-standard": "^1.3.2",
"nextcloud/rector": "^0.2.1",
"rector/rector": "^1.2.10",
"vimeo/psalm": "^5.26.1" "vimeo/psalm": "^5.26.1"
}, },
"config": { "config": {

253
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "a82f9b080077d3e1ddc2ee3a0c736cf4", "content-hash": "2bded493404c793fc0cfceedfeccc38c",
"packages": [], "packages": [],
"packages-dev": [ "packages-dev": [
{ {
@ -169,16 +169,16 @@
}, },
{ {
"name": "composer/pcre", "name": "composer/pcre",
"version": "3.3.2", "version": "3.3.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/composer/pcre.git", "url": "https://github.com/composer/pcre.git",
"reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" "reference": "63aaeac21d7e775ff9bc9d45021e1745c97521c4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", "url": "https://api.github.com/repos/composer/pcre/zipball/63aaeac21d7e775ff9bc9d45021e1745c97521c4",
"reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", "reference": "63aaeac21d7e775ff9bc9d45021e1745c97521c4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -188,8 +188,8 @@
"phpstan/phpstan": "<1.11.10" "phpstan/phpstan": "<1.11.10"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "^1.12 || ^2", "phpstan/phpstan": "^1.11.10",
"phpstan/phpstan-strict-rules": "^1 || ^2", "phpstan/phpstan-strict-rules": "^1.1",
"phpunit/phpunit": "^8 || ^9" "phpunit/phpunit": "^8 || ^9"
}, },
"type": "library", "type": "library",
@ -228,7 +228,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/composer/pcre/issues", "issues": "https://github.com/composer/pcre/issues",
"source": "https://github.com/composer/pcre/tree/3.3.2" "source": "https://github.com/composer/pcre/tree/3.3.1"
}, },
"funding": [ "funding": [
{ {
@ -244,7 +244,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-11-12T16:29:46+00:00" "time": "2024-08-27T18:44:43+00:00"
}, },
{ {
"name": "composer/semver", "name": "composer/semver",
@ -822,68 +822,6 @@
}, },
"time": "2024-10-19T00:42:39+00:00" "time": "2024-10-19T00:42:39+00:00"
}, },
{
"name": "nextcloud/rector",
"version": "v0.2.1",
"source": {
"type": "git",
"url": "https://github.com/nextcloud-libraries/rector.git",
"reference": "d73ab086700564f675eda9a834b6b08410a1da7e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nextcloud-libraries/rector/zipball/d73ab086700564f675eda9a834b6b08410a1da7e",
"reference": "d73ab086700564f675eda9a834b6b08410a1da7e",
"shasum": ""
},
"require": {
"php": "^8.1"
},
"require-dev": {
"phpunit/phpunit": "^10.5",
"ramsey/devtools": "^2.0",
"rector/rector": "^1.2"
},
"type": "library",
"extra": {
"captainhook": {
"force-install": true
},
"ramsey/conventional-commits": {
"configFile": "conventional-commits.json"
},
"ramsey/devtools": {
"command-prefix": "dev",
"memory-limit": "-1"
}
},
"autoload": {
"psr-4": {
"Nextcloud\\Rector\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"AGPL-3.0-or-later"
],
"authors": [
{
"name": "Christoph Wurst",
"email": "christoph@winzerhof-wurst.at",
"homepage": "https://wuc.me"
}
],
"description": "Rector upgrade rules for Nextcloud",
"keywords": [
"nextcloud",
"refactoring"
],
"support": {
"issues": "https://github.com/nextcloud-libraries/rector/issues",
"source": "https://github.com/nextcloud-libraries/rector/tree/v0.2.1"
},
"time": "2024-10-01T12:24:08+00:00"
},
{ {
"name": "nikic/php-parser", "name": "nikic/php-parser",
"version": "v4.19.4", "version": "v4.19.4",
@ -1047,16 +985,16 @@
}, },
{ {
"name": "phpdocumentor/reflection-docblock", "name": "phpdocumentor/reflection-docblock",
"version": "5.6.0", "version": "5.5.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "f3558a4c23426d12bffeaab463f8a8d8b681193c" "reference": "0c70d2c566e899666f367ab7b80986beb3581e6f"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/f3558a4c23426d12bffeaab463f8a8d8b681193c", "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/0c70d2c566e899666f367ab7b80986beb3581e6f",
"reference": "f3558a4c23426d12bffeaab463f8a8d8b681193c", "reference": "0c70d2c566e899666f367ab7b80986beb3581e6f",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1065,7 +1003,7 @@
"php": "^7.4 || ^8.0", "php": "^7.4 || ^8.0",
"phpdocumentor/reflection-common": "^2.2", "phpdocumentor/reflection-common": "^2.2",
"phpdocumentor/type-resolver": "^1.7", "phpdocumentor/type-resolver": "^1.7",
"phpstan/phpdoc-parser": "^1.7|^2.0", "phpstan/phpdoc-parser": "^1.7",
"webmozart/assert": "^1.9.1" "webmozart/assert": "^1.9.1"
}, },
"require-dev": { "require-dev": {
@ -1105,29 +1043,29 @@
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"support": { "support": {
"issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
"source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.0" "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.5.1"
}, },
"time": "2024-11-12T11:25:25+00:00" "time": "2024-11-06T11:58:54+00:00"
}, },
{ {
"name": "phpdocumentor/type-resolver", "name": "phpdocumentor/type-resolver",
"version": "1.10.0", "version": "1.9.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git", "url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" "reference": "1fb5ba8d045f5dd984ebded5b1cc66f29459422d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/1fb5ba8d045f5dd984ebded5b1cc66f29459422d",
"reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", "reference": "1fb5ba8d045f5dd984ebded5b1cc66f29459422d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"doctrine/deprecations": "^1.0", "doctrine/deprecations": "^1.0",
"php": "^7.3 || ^8.0", "php": "^7.3 || ^8.0",
"phpdocumentor/reflection-common": "^2.0", "phpdocumentor/reflection-common": "^2.0",
"phpstan/phpdoc-parser": "^1.18|^2.0" "phpstan/phpdoc-parser": "^1.18"
}, },
"require-dev": { "require-dev": {
"ext-tokenizer": "*", "ext-tokenizer": "*",
@ -1163,36 +1101,36 @@
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": { "support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues", "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.9.0"
}, },
"time": "2024-11-09T15:12:26+00:00" "time": "2024-11-03T20:11:34+00:00"
}, },
{ {
"name": "phpstan/phpdoc-parser", "name": "phpstan/phpdoc-parser",
"version": "2.0.0", "version": "1.33.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git", "url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "c00d78fb6b29658347f9d37ebe104bffadf36299" "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/c00d78fb6b29658347f9d37ebe104bffadf36299", "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/82a311fd3690fb2bf7b64d5c98f912b3dd746140",
"reference": "c00d78fb6b29658347f9d37ebe104bffadf36299", "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.4 || ^8.0" "php": "^7.2 || ^8.0"
}, },
"require-dev": { "require-dev": {
"doctrine/annotations": "^2.0", "doctrine/annotations": "^2.0",
"nikic/php-parser": "^5.3.0", "nikic/php-parser": "^4.15",
"php-parallel-lint/php-parallel-lint": "^1.2", "php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/extension-installer": "^1.0", "phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^2.0", "phpstan/phpstan": "^1.5",
"phpstan/phpstan-phpunit": "^2.0", "phpstan/phpstan-phpunit": "^1.1",
"phpstan/phpstan-strict-rules": "^2.0", "phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.6", "phpunit/phpunit": "^9.5",
"symfony/process": "^5.2" "symfony/process": "^5.2"
}, },
"type": "library", "type": "library",
@ -1210,67 +1148,9 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types", "description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": { "support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues", "issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/2.0.0" "source": "https://github.com/phpstan/phpdoc-parser/tree/1.33.0"
}, },
"time": "2024-10-13T11:29:49+00:00" "time": "2024-10-13T11:25:22+00:00"
},
{
"name": "phpstan/phpstan",
"version": "1.12.10",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "fc463b5d0fe906dcf19689be692c65c50406a071"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/fc463b5d0fe906dcf19689be692c65c50406a071",
"reference": "fc463b5d0fe906dcf19689be692c65c50406a071",
"shasum": ""
},
"require": {
"php": "^7.2|^8.0"
},
"conflict": {
"phpstan/phpstan-shim": "*"
},
"bin": [
"phpstan",
"phpstan.phar"
],
"type": "library",
"autoload": {
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHPStan - PHP Static Analysis Tool",
"keywords": [
"dev",
"static analysis"
],
"support": {
"docs": "https://phpstan.org/user-guide/getting-started",
"forum": "https://github.com/phpstan/phpstan/discussions",
"issues": "https://github.com/phpstan/phpstan/issues",
"security": "https://github.com/phpstan/phpstan/security/policy",
"source": "https://github.com/phpstan/phpstan-src"
},
"funding": [
{
"url": "https://github.com/ondrejmirtes",
"type": "github"
},
{
"url": "https://github.com/phpstan",
"type": "github"
}
],
"time": "2024-11-11T15:37:09+00:00"
}, },
{ {
"name": "psr/clock", "name": "psr/clock",
@ -1473,65 +1353,6 @@
}, },
"time": "2021-07-14T16:41:46+00:00" "time": "2021-07-14T16:41:46+00:00"
}, },
{
"name": "rector/rector",
"version": "1.2.10",
"source": {
"type": "git",
"url": "https://github.com/rectorphp/rector.git",
"reference": "40f9cf38c05296bd32f444121336a521a293fa61"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/40f9cf38c05296bd32f444121336a521a293fa61",
"reference": "40f9cf38c05296bd32f444121336a521a293fa61",
"shasum": ""
},
"require": {
"php": "^7.2|^8.0",
"phpstan/phpstan": "^1.12.5"
},
"conflict": {
"rector/rector-doctrine": "*",
"rector/rector-downgrade-php": "*",
"rector/rector-phpunit": "*",
"rector/rector-symfony": "*"
},
"suggest": {
"ext-dom": "To manipulate phpunit.xml via the custom-rule command"
},
"bin": [
"bin/rector"
],
"type": "library",
"autoload": {
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Instant Upgrade and Automated Refactoring of any PHP code",
"keywords": [
"automation",
"dev",
"migration",
"refactoring"
],
"support": {
"issues": "https://github.com/rectorphp/rector/issues",
"source": "https://github.com/rectorphp/rector/tree/1.2.10"
},
"funding": [
{
"url": "https://github.com/tomasvotruba",
"type": "github"
}
],
"time": "2024-11-08T13:59:10+00:00"
},
{ {
"name": "roave/security-advisories", "name": "roave/security-advisories",
"version": "dev-latest", "version": "dev-latest",
@ -3398,9 +3219,7 @@
}, },
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {},
"php": "^8.1"
},
"platform-dev": {}, "platform-dev": {},
"platform-overrides": { "platform-overrides": {
"php": "8.1" "php": "8.1"

View File

@ -5,9 +5,8 @@ OC.L10N.register(
"Podcast" : "Podcast", "Podcast" : "Podcast",
"RePod" : "RePod", "RePod" : "RePod",
"🔊 Browse, manage and listen to podcasts" : "🔊 Suchen, Verwalten und Anhören von Podcasts", "🔊 Browse, manage and listen to podcasts" : "🔊 Suchen, Verwalten und Anhören von Podcasts",
"## Features\n- 🔍 Browse and subscribe huge collection of podcasts\n- 🔊 Listen to episodes directly in Nextcloud\n- 🌐 Sync your activity with [AntennaPod](https://antennapod.org/) and [other apps](https://git.crystalyx.net/Xefir/repod#clients-supporting-sync-of-gpoddersync)\n- 📱 Mobile friendly interface\n- 📡 Import and export your subscriptions\n- ➡️ Full features comparison [here](https://git.crystalyx.net/Xefir/repod#comparaison-with-similar-apps-for-nextcloud)\n\n## Requirements\nYou need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installed to use this app!" : "## Funktionen\n- 🔍 Durchsuchen und abonnieren einer großen Sammlung von Podcasts\n- 🔊 Episoden direkt in Nextcloud anhören\n- 🌐 Synchronisiere deine Aktivität mit [AntennaPod](https://antennapod.org/)\n- 📱 Mobile-freundliche Schnittstelle\n- 📡 Importieren und Exportieren Ihrer Abonnements\n- ➡️ Vollständiger Funktionsvergleich [hier](https://git.crystalyx.net/Xefir/repod#comparaison-with-similar-apps-for-nextcloud)\n\n## Voraussetzungen\nDu musst [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installiert haben, um diese App zu benutzen!", "## Features\n- 🔍 Browse and subscribe huge collection of podcasts\n- 🔊 Listen to episodes directly in Nextcloud\n- 🌐 Sync your activity with [AntennaPod](https://antennapod.org/) and [other apps](https://git.crystalyx.net/Xefir/repod#clients-supporting-sync-of-gpoddersync)\n- 📱 Mobile friendly interface\n- 📡 Import and export your subscriptions\n- ➡️ Full features comparison [here](https://git.crystalyx.net/Xefir/repod#comparaison-with-similar-apps-for-nextcloud)\n\n## Requirements\nYou need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installed to use this app!" : "## Funktionen\n- 🔍 Durchsuchen und abonnieren einer großen Sammlung von Podcasts\n- 🔊 Episoden direkt in Nextcloud anhören\n- 🌐 Synchronisiere deine Aktivität mit [AntennaPod](https://antennapod.org/)\n- 📱 Mobile-freundliche Schnittstelle\n- 📡 Importieren und Exportieren Ihrer Abonnements\n\n## Voraussetzungen\nDu musst [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installiert haben, um diese App zu benutzen!",
"Download" : "Herunterladen", "Download" : "Herunterladen",
"Skip to {match}" : "Springen zu {match}",
"Add a RSS link" : "Einen RSS-Link hinzufügen", "Add a RSS link" : "Einen RSS-Link hinzufügen",
"Subscribe" : "Abonnieren", "Subscribe" : "Abonnieren",
"Error while adding the feed" : "Fehler beim Hinzufügen des Feeds", "Error while adding the feed" : "Fehler beim Hinzufügen des Feeds",
@ -56,4 +55,4 @@ OC.L10N.register(
"No favorites" : "Keine Favoriten", "No favorites" : "Keine Favoriten",
"A browser extension conflict with RePod" : "Ein Browser-Erweiterungskonflikt mit RePod" "A browser extension conflict with RePod" : "Ein Browser-Erweiterungskonflikt mit RePod"
}, },
"nplurals=2; plural=n != 1;"); "");

View File

@ -3,9 +3,8 @@
"Podcast" : "Podcast", "Podcast" : "Podcast",
"RePod" : "RePod", "RePod" : "RePod",
"🔊 Browse, manage and listen to podcasts" : "🔊 Suchen, Verwalten und Anhören von Podcasts", "🔊 Browse, manage and listen to podcasts" : "🔊 Suchen, Verwalten und Anhören von Podcasts",
"## Features\n- 🔍 Browse and subscribe huge collection of podcasts\n- 🔊 Listen to episodes directly in Nextcloud\n- 🌐 Sync your activity with [AntennaPod](https://antennapod.org/) and [other apps](https://git.crystalyx.net/Xefir/repod#clients-supporting-sync-of-gpoddersync)\n- 📱 Mobile friendly interface\n- 📡 Import and export your subscriptions\n- ➡️ Full features comparison [here](https://git.crystalyx.net/Xefir/repod#comparaison-with-similar-apps-for-nextcloud)\n\n## Requirements\nYou need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installed to use this app!" : "## Funktionen\n- 🔍 Durchsuchen und abonnieren einer großen Sammlung von Podcasts\n- 🔊 Episoden direkt in Nextcloud anhören\n- 🌐 Synchronisiere deine Aktivität mit [AntennaPod](https://antennapod.org/)\n- 📱 Mobile-freundliche Schnittstelle\n- 📡 Importieren und Exportieren Ihrer Abonnements\n- ➡️ Vollständiger Funktionsvergleich [hier](https://git.crystalyx.net/Xefir/repod#comparaison-with-similar-apps-for-nextcloud)\n\n## Voraussetzungen\nDu musst [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installiert haben, um diese App zu benutzen!", "## Features\n- 🔍 Browse and subscribe huge collection of podcasts\n- 🔊 Listen to episodes directly in Nextcloud\n- 🌐 Sync your activity with [AntennaPod](https://antennapod.org/) and [other apps](https://git.crystalyx.net/Xefir/repod#clients-supporting-sync-of-gpoddersync)\n- 📱 Mobile friendly interface\n- 📡 Import and export your subscriptions\n- ➡️ Full features comparison [here](https://git.crystalyx.net/Xefir/repod#comparaison-with-similar-apps-for-nextcloud)\n\n## Requirements\nYou need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installed to use this app!" : "## Funktionen\n- 🔍 Durchsuchen und abonnieren einer großen Sammlung von Podcasts\n- 🔊 Episoden direkt in Nextcloud anhören\n- 🌐 Synchronisiere deine Aktivität mit [AntennaPod](https://antennapod.org/)\n- 📱 Mobile-freundliche Schnittstelle\n- 📡 Importieren und Exportieren Ihrer Abonnements\n\n## Voraussetzungen\nDu musst [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installiert haben, um diese App zu benutzen!",
"Download" : "Herunterladen", "Download" : "Herunterladen",
"Skip to {match}" : "Springen zu {match}",
"Add a RSS link" : "Einen RSS-Link hinzufügen", "Add a RSS link" : "Einen RSS-Link hinzufügen",
"Subscribe" : "Abonnieren", "Subscribe" : "Abonnieren",
"Error while adding the feed" : "Fehler beim Hinzufügen des Feeds", "Error while adding the feed" : "Fehler beim Hinzufügen des Feeds",
@ -53,5 +52,5 @@
"Pin some subscriptions to see their latest updates" : "Pinne einige Abonnements, um ihre neuesten Updates zu sehen", "Pin some subscriptions to see their latest updates" : "Pinne einige Abonnements, um ihre neuesten Updates zu sehen",
"No favorites" : "Keine Favoriten", "No favorites" : "Keine Favoriten",
"A browser extension conflict with RePod" : "Ein Browser-Erweiterungskonflikt mit RePod" "A browser extension conflict with RePod" : "Ein Browser-Erweiterungskonflikt mit RePod"
},"pluralForm" :"nplurals=2; plural=n != 1;" },"pluralForm" :""
} }

View File

@ -7,7 +7,6 @@ OC.L10N.register(
"🔊 Browse, manage and listen to podcasts" : "🔊 Parcourir, gérer et écouter vos podcasts", "🔊 Browse, manage and listen to podcasts" : "🔊 Parcourir, gérer et écouter vos podcasts",
"## Features\n- 🔍 Browse and subscribe huge collection of podcasts\n- 🔊 Listen to episodes directly in Nextcloud\n- 🌐 Sync your activity with [AntennaPod](https://antennapod.org/) and [other apps](https://git.crystalyx.net/Xefir/repod#clients-supporting-sync-of-gpoddersync)\n- 📱 Mobile friendly interface\n- 📡 Import and export your subscriptions\n- ➡️ Full features comparison [here](https://git.crystalyx.net/Xefir/repod#comparaison-with-similar-apps-for-nextcloud)\n\n## Requirements\nYou need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installed to use this app!" : "## Fonctionnalités\n- 🔍 Parcourir et s'abonner à une grande collections de podcasts\n- 🔊 Écouter vos épisodes directement sur Nextcloud\n- 🌐 Synchroniser son activité avec [AntennaPod](https://antennapod.org/) et d'autres [applications](https://git.crystalyx.net/Xefir/repod#clients-supporting-sync-of-gpoddersync)\n- 📱 Interface optimisée pour mobiles et ordinateurs\n- 📡 Import/export de ses abonnements\n- ➡️ Tableau récapitulatif complet des fonctionnalitées [ici](https://git.crystalyx.net/Xefir/repod#comparaison-with-similar-apps-for-nextcloud)\n\n## Pré-requis\nVous devez avoir [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installé pour utiliser cette application !", "## Features\n- 🔍 Browse and subscribe huge collection of podcasts\n- 🔊 Listen to episodes directly in Nextcloud\n- 🌐 Sync your activity with [AntennaPod](https://antennapod.org/) and [other apps](https://git.crystalyx.net/Xefir/repod#clients-supporting-sync-of-gpoddersync)\n- 📱 Mobile friendly interface\n- 📡 Import and export your subscriptions\n- ➡️ Full features comparison [here](https://git.crystalyx.net/Xefir/repod#comparaison-with-similar-apps-for-nextcloud)\n\n## Requirements\nYou need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installed to use this app!" : "## Fonctionnalités\n- 🔍 Parcourir et s'abonner à une grande collections de podcasts\n- 🔊 Écouter vos épisodes directement sur Nextcloud\n- 🌐 Synchroniser son activité avec [AntennaPod](https://antennapod.org/) et d'autres [applications](https://git.crystalyx.net/Xefir/repod#clients-supporting-sync-of-gpoddersync)\n- 📱 Interface optimisée pour mobiles et ordinateurs\n- 📡 Import/export de ses abonnements\n- ➡️ Tableau récapitulatif complet des fonctionnalitées [ici](https://git.crystalyx.net/Xefir/repod#comparaison-with-similar-apps-for-nextcloud)\n\n## Pré-requis\nVous devez avoir [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installé pour utiliser cette application !",
"Download" : "Télécharger", "Download" : "Télécharger",
"Skip to {match}" : "Sauter à {match}",
"Add a RSS link" : "Ajouter un lien RSS", "Add a RSS link" : "Ajouter un lien RSS",
"Subscribe" : "S'abonner", "Subscribe" : "S'abonner",
"Error while adding the feed" : "Erreur lors de l'ajout du flux", "Error while adding the feed" : "Erreur lors de l'ajout du flux",

View File

@ -5,7 +5,6 @@
"🔊 Browse, manage and listen to podcasts" : "🔊 Parcourir, gérer et écouter vos podcasts", "🔊 Browse, manage and listen to podcasts" : "🔊 Parcourir, gérer et écouter vos podcasts",
"## Features\n- 🔍 Browse and subscribe huge collection of podcasts\n- 🔊 Listen to episodes directly in Nextcloud\n- 🌐 Sync your activity with [AntennaPod](https://antennapod.org/) and [other apps](https://git.crystalyx.net/Xefir/repod#clients-supporting-sync-of-gpoddersync)\n- 📱 Mobile friendly interface\n- 📡 Import and export your subscriptions\n- ➡️ Full features comparison [here](https://git.crystalyx.net/Xefir/repod#comparaison-with-similar-apps-for-nextcloud)\n\n## Requirements\nYou need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installed to use this app!" : "## Fonctionnalités\n- 🔍 Parcourir et s'abonner à une grande collections de podcasts\n- 🔊 Écouter vos épisodes directement sur Nextcloud\n- 🌐 Synchroniser son activité avec [AntennaPod](https://antennapod.org/) et d'autres [applications](https://git.crystalyx.net/Xefir/repod#clients-supporting-sync-of-gpoddersync)\n- 📱 Interface optimisée pour mobiles et ordinateurs\n- 📡 Import/export de ses abonnements\n- ➡️ Tableau récapitulatif complet des fonctionnalitées [ici](https://git.crystalyx.net/Xefir/repod#comparaison-with-similar-apps-for-nextcloud)\n\n## Pré-requis\nVous devez avoir [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installé pour utiliser cette application !", "## Features\n- 🔍 Browse and subscribe huge collection of podcasts\n- 🔊 Listen to episodes directly in Nextcloud\n- 🌐 Sync your activity with [AntennaPod](https://antennapod.org/) and [other apps](https://git.crystalyx.net/Xefir/repod#clients-supporting-sync-of-gpoddersync)\n- 📱 Mobile friendly interface\n- 📡 Import and export your subscriptions\n- ➡️ Full features comparison [here](https://git.crystalyx.net/Xefir/repod#comparaison-with-similar-apps-for-nextcloud)\n\n## Requirements\nYou need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installed to use this app!" : "## Fonctionnalités\n- 🔍 Parcourir et s'abonner à une grande collections de podcasts\n- 🔊 Écouter vos épisodes directement sur Nextcloud\n- 🌐 Synchroniser son activité avec [AntennaPod](https://antennapod.org/) et d'autres [applications](https://git.crystalyx.net/Xefir/repod#clients-supporting-sync-of-gpoddersync)\n- 📱 Interface optimisée pour mobiles et ordinateurs\n- 📡 Import/export de ses abonnements\n- ➡️ Tableau récapitulatif complet des fonctionnalitées [ici](https://git.crystalyx.net/Xefir/repod#comparaison-with-similar-apps-for-nextcloud)\n\n## Pré-requis\nVous devez avoir [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installé pour utiliser cette application !",
"Download" : "Télécharger", "Download" : "Télécharger",
"Skip to {match}" : "Sauter à {match}",
"Add a RSS link" : "Ajouter un lien RSS", "Add a RSS link" : "Ajouter un lien RSS",
"Subscribe" : "S'abonner", "Subscribe" : "S'abonner",
"Error while adding the feed" : "Erreur lors de l'ajout du flux", "Error while adding the feed" : "Erreur lors de l'ajout du flux",

View File

@ -16,7 +16,6 @@ use OCP\AppFramework\Services\IInitialState;
class Application extends App implements IBootstrap class Application extends App implements IBootstrap
{ {
public const APP_ID = 'repod'; public const APP_ID = 'repod';
private const GPODDERSYNC_ID = 'gpoddersync'; private const GPODDERSYNC_ID = 'gpoddersync';
public function __construct() { public function __construct() {
@ -41,7 +40,7 @@ class Application extends App implements IBootstrap
if (!$gpoddersync) { if (!$gpoddersync) {
try { try {
$appManager->enableApp(self::GPODDERSYNC_ID); $appManager->enableApp(self::GPODDERSYNC_ID);
} catch (AppPathNotFoundException) { } catch (AppPathNotFoundException $e) {
} }
} }

View File

@ -22,10 +22,10 @@ class EpisodesController extends Controller
{ {
public function __construct( public function __construct(
IRequest $request, IRequest $request,
private readonly EpisodeActionReader $episodeActionReader, private EpisodeActionReader $episodeActionReader,
private readonly EpisodeActionRepository $episodeActionRepository, private EpisodeActionRepository $episodeActionRepository,
private readonly IClientService $clientService, private IClientService $clientService,
private readonly UserService $userService private UserService $userService
) { ) {
parent::__construct(Application::APP_ID, $request); parent::__construct(Application::APP_ID, $request);
} }
@ -37,8 +37,8 @@ class EpisodesController extends Controller
$client = $this->clientService->newClient(); $client = $this->clientService->newClient();
$feed = $client->get($url); $feed = $client->get($url);
$episodes = $this->episodeActionReader->parseRssXml((string) $feed->getBody()); $episodes = $this->episodeActionReader->parseRssXml((string) $feed->getBody());
usort($episodes, fn (EpisodeActionExtraData $a, EpisodeActionExtraData $b): int => $b->getPubDate() <=> $a->getPubDate()); usort($episodes, fn (EpisodeActionExtraData $a, EpisodeActionExtraData $b) => $b->getPubDate() <=> $a->getPubDate());
$episodes = array_values(array_intersect_key($episodes, array_unique(array_map(fn (EpisodeActionExtraData $episode): string => $episode->getGuid(), $episodes)))); $episodes = array_values(array_intersect_key($episodes, array_unique(array_map(fn (EpisodeActionExtraData $episode) => $episode->getGuid(), $episodes))));
return new JSONResponse($episodes, $feed->getStatusCode()); return new JSONResponse($episodes, $feed->getStatusCode());
} }

View File

@ -22,11 +22,11 @@ class OpmlController extends Controller
{ {
public function __construct( public function __construct(
IRequest $request, IRequest $request,
private readonly IL10N $l10n, private IL10N $l10n,
private readonly PodcastDataReader $podcastDataReader, private PodcastDataReader $podcastDataReader,
private readonly PodcastMetricsReader $podcastMetricsReader, private PodcastMetricsReader $podcastMetricsReader,
private readonly SubscriptionChangeSaver $subscriptionChangeSaver, private SubscriptionChangeSaver $subscriptionChangeSaver,
private readonly UserService $userService private UserService $userService
) { ) {
parent::__construct(Application::APP_ID, $request); parent::__construct(Application::APP_ID, $request);
} }
@ -55,7 +55,7 @@ class OpmlController extends Controller
foreach ($subscriptions as $subscription) { foreach ($subscriptions as $subscription) {
try { try {
$podcast = $this->podcastDataReader->getCachedOrFetchPodcastData($subscription->getUrl(), $this->userService->getUserUID()); $podcast = $this->podcastDataReader->getCachedOrFetchPodcastData($subscription->getUrl(), $this->userService->getUserUID());
} catch (\Exception) { } catch (\Exception $e) {
continue; continue;
} }

View File

@ -11,13 +11,15 @@ use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\ContentSecurityPolicy;
use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\TemplateResponse;
use OCP\IConfig;
use OCP\IRequest; use OCP\IRequest;
use OCP\Util; use OCP\Util;
class PageController extends Controller class PageController extends Controller
{ {
public function __construct( public function __construct(
IRequest $request IRequest $request,
private IConfig $config
) { ) {
parent::__construct(Application::APP_ID, $request); parent::__construct(Application::APP_ID, $request);
} }

View File

@ -20,9 +20,9 @@ class PodcastController extends Controller
{ {
public function __construct( public function __construct(
IRequest $request, IRequest $request,
private readonly ICacheFactory $cacheFactory, private ICacheFactory $cacheFactory,
private readonly IClientService $clientService, private IClientService $clientService,
private readonly PodcastDataReader $podcastDataReader private PodcastDataReader $podcastDataReader
) { ) {
parent::__construct(Application::APP_ID, $request); parent::__construct(Application::APP_ID, $request);
} }
@ -36,7 +36,7 @@ class PodcastController extends Controller
if ($this->cacheFactory->isLocalCacheAvailable()) { if ($this->cacheFactory->isLocalCacheAvailable()) {
try { try {
$podcast = $this->podcastDataReader->tryGetCachedPodcastData($url); $podcast = $this->podcastDataReader->tryGetCachedPodcastData($url);
} catch (\Exception) { } catch (\Exception $e) {
} }
} }
@ -51,7 +51,7 @@ class PodcastController extends Controller
if ($this->cacheFactory->isLocalCacheAvailable()) { if ($this->cacheFactory->isLocalCacheAvailable()) {
try { try {
$this->podcastDataReader->trySetCachedPodcastData($url, $podcast); $this->podcastDataReader->trySetCachedPodcastData($url, $podcast);
} catch (\Exception) { } catch (\Exception $e) {
} }
} }

View File

@ -17,7 +17,7 @@ class SearchController extends Controller
{ {
public function __construct( public function __construct(
IRequest $request, IRequest $request,
private readonly MultiPodService $multiPodService private MultiPodService $multiPodService
) { ) {
parent::__construct(Application::APP_ID, $request); parent::__construct(Application::APP_ID, $request);
} }

View File

@ -17,7 +17,7 @@ class ToplistController extends Controller
{ {
public function __construct( public function __construct(
IRequest $request, IRequest $request,
private readonly FyydService $fyydService private FyydService $fyydService
) { ) {
parent::__construct(Application::APP_ID, $request); parent::__construct(Application::APP_ID, $request);
} }

View File

@ -28,22 +28,22 @@ use OCA\GPodderSync\Core\EpisodeAction\EpisodeAction;
* action: ?EpisodeActionType * action: ?EpisodeActionType
* } * }
*/ */
class EpisodeActionExtraData implements \JsonSerializable, \Stringable class EpisodeActionExtraData implements \JsonSerializable
{ {
public function __construct( public function __construct(
private readonly string $title, private string $title,
private readonly ?string $url, private ?string $url,
private readonly string $name, private string $name,
private readonly ?string $link, private ?string $link,
private readonly ?string $image, private ?string $image,
private readonly ?string $description, private ?string $description,
private readonly int $fetchedAtUnix, private int $fetchedAtUnix,
private readonly string $guid, private string $guid,
private readonly ?string $type, private ?string $type,
private readonly ?int $size, private ?int $size,
private readonly ?\DateTime $pubDate, private ?\DateTime $pubDate,
private readonly ?string $duration, private ?string $duration,
private readonly ?EpisodeAction $action private ?EpisodeAction $action
) {} ) {}
public function __toString(): string { public function __toString(): string {
@ -120,7 +120,7 @@ class EpisodeActionExtraData implements \JsonSerializable, \Stringable
'size' => $this->size, 'size' => $this->size,
'pubDate' => $this->pubDate, 'pubDate' => $this->pubDate,
'duration' => $this->duration, 'duration' => $this->duration,
'action' => $this->action instanceof EpisodeAction ? $this->action->toArray() : null, 'action' => $this->action ? $this->action->toArray() : null,
]; ];
} }

View File

@ -11,8 +11,8 @@ use OCA\RePod\Service\UserService;
class EpisodeActionReader extends CoreEpisodeActionReader class EpisodeActionReader extends CoreEpisodeActionReader
{ {
public function __construct( public function __construct(
private readonly EpisodeActionRepository $episodeActionRepository, private EpisodeActionRepository $episodeActionRepository,
private readonly UserService $userService private UserService $userService
) {} ) {}
/** /**
@ -20,7 +20,6 @@ class EpisodeActionReader extends CoreEpisodeActionReader
* Specs : https://github.com/Podcast-Standards-Project/PSP-1-Podcast-RSS-Specification/blob/main/README.md. * Specs : https://github.com/Podcast-Standards-Project/PSP-1-Podcast-RSS-Specification/blob/main/README.md.
* *
* @return EpisodeActionExtraData[] * @return EpisodeActionExtraData[]
*
* @throws \Exception if the XML data could not be parsed * @throws \Exception if the XML data could not be parsed
*/ */
public function parseRssXml(string $xmlString, ?int $fetchedAtUnix = null): array { public function parseRssXml(string $xmlString, ?int $fetchedAtUnix = null): array {
@ -93,6 +92,9 @@ class EpisodeActionReader extends CoreEpisodeActionReader
$description = $this->stringOrNull($iTunesItemChildren->summary); $description = $this->stringOrNull($iTunesItemChildren->summary);
} }
// Remove tags
$description = strip_tags(str_replace(['<br>', '<br/>', '<br />'], "\n", $description ?? ''));
// Get episode duration // Get episode duration
if (isset($iTunesItemChildren)) { if (isset($iTunesItemChildren)) {
$duration = $this->stringOrNull($iTunesItemChildren->duration); $duration = $this->stringOrNull($iTunesItemChildren->duration);
@ -105,7 +107,7 @@ class EpisodeActionReader extends CoreEpisodeActionReader
if (isset($pubDate)) { if (isset($pubDate)) {
try { try {
$pubDate = new \DateTime($pubDate); $pubDate = new \DateTime($pubDate);
} catch (\Exception) { } catch (\Exception $e) {
$pubDate = null; $pubDate = null;
} }
} }

View File

@ -13,9 +13,9 @@ class FyydService implements IPodProvider
private const BASE_URL = 'https://api.fyyd.de/0.2/'; private const BASE_URL = 'https://api.fyyd.de/0.2/';
public function __construct( public function __construct(
private readonly IClientService $clientService, private IClientService $clientService,
private readonly LoggerInterface $logger, private LoggerInterface $logger,
private readonly UserService $userService private UserService $userService
) {} ) {}
public function search(string $value): array { public function search(string $value): array {
@ -94,8 +94,8 @@ class FyydService implements IPodProvider
if (array_key_exists('data', $langJson) && is_array($langJson['data'])) { if (array_key_exists('data', $langJson) && is_array($langJson['data'])) {
$language = in_array($userLang, $langJson['data']) ? $userLang : 'en'; $language = in_array($userLang, $langJson['data']) ? $userLang : 'en';
} }
} catch (\Exception $exception) { } catch (\Exception $e) {
$this->logger->error($exception->getMessage(), $exception->getTrace()); $this->logger->error($e->getMessage(), $e->getTrace());
} }
$podcastClient = $this->clientService->newClient(); $podcastClient = $this->clientService->newClient();

View File

@ -12,8 +12,8 @@ class ItunesService implements IPodProvider
private const BASE_URL = 'https://itunes.apple.com/'; private const BASE_URL = 'https://itunes.apple.com/';
public function __construct( public function __construct(
private readonly IClientService $clientService, private IClientService $clientService,
private readonly UserService $userService private UserService $userService
) {} ) {}
public function search(string $value): array { public function search(string $value): array {

View File

@ -17,7 +17,7 @@ class MultiPodService implements IPodProvider
public function __construct( public function __construct(
FyydService $fyydService, FyydService $fyydService,
ItunesService $itunesService, ItunesService $itunesService,
private readonly LoggerInterface $logger private LoggerInterface $logger
) { ) {
$this->providers = [$fyydService, $itunesService]; $this->providers = [$fyydService, $itunesService];
} }
@ -36,7 +36,7 @@ class MultiPodService implements IPodProvider
} }
} }
usort($podcasts, fn (PodcastData $a, PodcastData $b): int => $b->getFetchedAtUnix() <=> $a->getFetchedAtUnix()); usort($podcasts, fn (PodcastData $a, PodcastData $b) => $b->getFetchedAtUnix() <=> $a->getFetchedAtUnix());
return array_values( return array_values(
array_intersect_key( array_intersect_key(

View File

@ -16,9 +16,9 @@ use OCP\Search\SearchResultEntry;
class SearchProvider implements IProvider class SearchProvider implements IProvider
{ {
public function __construct( public function __construct(
private readonly IL10N $l10n, private IL10N $l10n,
private readonly IURLGenerator $urlGenerator, private IURLGenerator $urlGenerator,
private readonly MultiPodService $multiPodService private MultiPodService $multiPodService
) {} ) {}
public function getId(): string { public function getId(): string {
@ -30,7 +30,7 @@ class SearchProvider implements IProvider
} }
public function getOrder(string $route, array $routeParameters): int { public function getOrder(string $route, array $routeParameters): int {
if (str_starts_with($route, Application::APP_ID.'.')) { if (0 === strpos($route, Application::APP_ID.'.')) {
// Active app, prefer my results // Active app, prefer my results
return -1; return -1;
} }

View File

@ -10,8 +10,8 @@ use OCP\L10N\IFactory;
class UserService class UserService
{ {
public function __construct( public function __construct(
private readonly IFactory $l10n, private IFactory $l10n,
private readonly IUserSession $userSession private IUserSession $userSession
) {} ) {}
public function getUserUID(): string { public function getUserUID(): string {
@ -27,7 +27,7 @@ class UserService
public function getCountryCode(): string { public function getCountryCode(): string {
$isoCodes = explode('_', $this->getIsoCode()); $isoCodes = explode('_', $this->getIsoCode());
return $isoCodes[1] ?? 'us'; return isset($isoCodes[1]) ? $isoCodes[1] : 'us';
} }
public function getLangCode(): string { public function getLangCode(): string {

736
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,34 +1,33 @@
{ {
"name": "repod", "name": "repod",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"type": "module",
"scripts": { "scripts": {
"build": "vite build", "build": "vue-tsc && vite build",
"dev": "vite --mode development build", "dev": "vite --mode development build",
"watch": "vite --mode development build --watch",
"lint": "vue-tsc && eslint src", "lint": "vue-tsc && eslint src",
"lint:fix": "vue-tsc && eslint src --fix", "lint:fix": "vue-tsc && eslint src --fix",
"stylelint": "stylelint src/**/*.vue src/**/*.scss src/**/*.css", "stylelint": "stylelint src/**/*.vue src/**/*.scss src/**/*.css",
"stylelint:fix": "stylelint src/**/*.vue src/**/*.scss src/**/*.css --fix", "stylelint:fix": "stylelint src/**/*.vue src/**/*.scss src/**/*.css --fix"
"watch": "vite --mode development build --watch"
}, },
"type": "module",
"browserslist": [ "browserslist": [
"extends @nextcloud/browserslist-config" "extends @nextcloud/browserslist-config"
], ],
"prettier": "@nextcloud/prettier-config",
"dependencies": { "dependencies": {
"@formatjs/intl-segmenter": "^11.7.4", "@formatjs/intl-segmenter": "^11.7.3",
"@nextcloud/axios": "^2.5.1", "@nextcloud/axios": "^2.5.1",
"@nextcloud/initial-state": "^2.2.0", "@nextcloud/initial-state": "^2.2.0",
"@nextcloud/l10n": "^3.1.0", "@nextcloud/l10n": "^3.1.0",
"@nextcloud/router": "^3.0.1", "@nextcloud/router": "^3.0.1",
"@nextcloud/vite-config": "^2.2.2", "@nextcloud/vite-config": "^2.2.2",
"@nextcloud/vue": "9.0.0-alpha.5", "@nextcloud/vue": "9.0.0-alpha.5",
"dompurify": "^3.2.0", "dompurify": "^3.1.7",
"linkify-html": "^4.1.3", "linkify-html": "^4.1.3",
"pinia": "^2.2.6", "pinia": "^2.2.6",
"toastify-js": "^1.12.0", "toastify-js": "^1.12.0",
"vite": "^5.4.11", "vite": "^5.4.10",
"vue": "^3.5.13", "vue": "^3.5.12",
"vue-material-design-icons": "^5.3.1", "vue-material-design-icons": "^5.3.1",
"vue-router": "^4.4.5" "vue-router": "^4.4.5"
}, },
@ -39,11 +38,14 @@
"@nextcloud/stylelint-config": "^3.0.1", "@nextcloud/stylelint-config": "^3.0.1",
"@types/toastify-js": "^1.12.3", "@types/toastify-js": "^1.12.3",
"@vue/eslint-config-typescript": "^13", "@vue/eslint-config-typescript": "^13",
"@vue/tsconfig": "^0.6.0", "@vue/tsconfig": "^0.5.1",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-pinia": "^0.4.1", "eslint-plugin-pinia": "^0.4.1",
"eslint-plugin-prettier": "^5.2.1", "eslint-plugin-prettier": "^5.2.1",
"ts-node": "^10.9.2",
"typescript": "5.5", "typescript": "5.5",
"vue-eslint-parser": "^9.4.3",
"vue-tsc": "^2.1.10" "vue-tsc": "^2.1.10"
} },
"prettier": "@nextcloud/prettier-config"
} }

View File

@ -1,34 +0,0 @@
<?php
declare(strict_types=1);
use Nextcloud\Rector\Set\NextcloudSets;
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withPaths([
__DIR__.'/appinfo',
__DIR__.'/lib',
])
->withPhpSets(php81: true)
->withSets([
NextcloudSets::NEXTCLOUD_27,
])
->withPreparedSets(
deadCode: true,
codeQuality: true,
codingStyle: true,
typeDeclarations: true,
privatization: true,
instanceOf: true,
earlyReturn: true,
strictBooleans: true,
rectorPreset: true,
phpunitCodeQuality: true,
doctrineCodeQuality: true,
symfonyCodeQuality: true,
symfonyConfigs: true,
twig: true,
phpunit: true,
)
;

View File

@ -1,4 +1,3 @@
{ {
"$schema": "https://docs.renovatebot.com/renovate-schema.json", "$schema": "https://docs.renovatebot.com/renovate-schema.json"
"rangeStrategy": "bump"
} }

View File

@ -1,44 +1,22 @@
<template> <template>
<div ref="html" v-sanitize="source" class="html" /> <div v-sanitize="source" class="html" />
</template> </template>
<script lang="ts"> <script lang="ts">
import dompurify from 'dompurify' import dompurify from 'dompurify'
import linkifyHtml from 'linkify-html' import linkifyHtml from 'linkify-html'
import { mapActions } from 'pinia'
import { t } from '@nextcloud/l10n'
import { timeToSeconds } from '../../utils/time.ts'
import { usePlayer } from '../../store/player.ts'
export default { export default {
name: 'SafeHtml', name: 'SafeHtml',
directives: { directives: {
sanitize: { sanitize: {
mounted(el, binding) { mounted(el, binding) {
el.innerHTML = dompurify el.innerHTML = dompurify.sanitize(
.sanitize(
linkifyHtml(binding.value, { linkifyHtml(binding.value, {
nl2br: true, nl2br: true,
target: '_blank', target: '_blank',
}), }),
) )
.replace(
/(([0-9]?[0-9]):)?([0-5]?[0-9]):([0-5][0-9])/g,
(
match,
noop: string,
hours: string,
minutes: string,
seconds: string,
) =>
`<seekable time="${timeToSeconds(
parseInt(hours),
parseInt(minutes),
parseInt(seconds),
)}" title="${t('repod', 'Skip to {match}', { match })}">${
match
}</seekable>`,
)
}, },
}, },
}, },
@ -48,31 +26,11 @@ export default {
required: true, required: true,
}, },
}, },
mounted() {
const seekables = (this.$refs.html as HTMLElement).querySelectorAll(
'seekable',
)
for (const seekable of seekables) {
seekable.addEventListener('click', (event) => {
this.seek(
parseInt(
(event.target as HTMLElement).getAttribute('time') || '',
),
)
this.play()
})
}
},
methods: {
...mapActions(usePlayer, ['play', 'seek']),
},
} }
</script> </script>
<style> <style>
.html a, .html a {
seekable {
cursor: pointer;
text-decoration: underline; text-decoration: underline;
} }
</style> </style>

View File

@ -11,6 +11,7 @@
:name="episode.name" :name="episode.name"
:one-line="oneLine" :one-line="oneLine"
:style="{ opacity: hasEnded(episode) ? 0.4 : 1 }" :style="{ opacity: hasEnded(episode) ? 0.4 : 1 }"
:title="episode.description"
@click="modalEpisode = episode"> @click="modalEpisode = episode">
<template #actions> <template #actions>
<NcActionButton <NcActionButton

View File

@ -55,7 +55,6 @@ export default {
<style scoped> <style scoped>
.controls { .controls {
display: flex; display: flex;
gap: 0.5rem;
} }
.pointer { .pointer {

View File

@ -51,16 +51,3 @@ export const durationToSeconds = (duration: string): number => {
seconds += splitDuration.length > 2 ? parseInt(splitDuration[2]) * 60 * 60 : 0 seconds += splitDuration.length > 2 ? parseInt(splitDuration[2]) * 60 * 60 : 0
return seconds return seconds
} }
/**
* Convert splitted time to seconds
* @param {number} hours The number of seconds
* @param {number} minutes The number of seconds
* @param {number} seconds The number of seconds
* @return {number}
*/
export const timeToSeconds = (
hours: number,
minutes: number,
seconds: number,
): number => hours * 3600 + minutes * 60 + seconds

View File

@ -8,9 +8,7 @@ class EpisodeActionReader
{ {
/** /**
* @param array $episodeActionsArray [] * @param array $episodeActionsArray []
*
* @return EpisodeAction[] * @return EpisodeAction[]
*
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*/ */
public function fromArray(array $episodeActionsArray) {} public function fromArray(array $episodeActionsArray) {}

View File

@ -29,7 +29,6 @@ class PodcastData implements \JsonSerializable
/** /**
* @return PodcastData * @return PodcastData
*
* @throws \Exception if the XML data could not be parsed * @throws \Exception if the XML data could not be parsed
*/ */
public static function parseRssXml(string $xmlString, ?int $fetchedAtUnix = null) {} public static function parseRssXml(string $xmlString, ?int $fetchedAtUnix = null) {}
@ -83,7 +82,6 @@ class PodcastData implements \JsonSerializable
/** /**
* @param PodcastDataType $data * @param PodcastDataType $data
*
* @return PodcastData * @return PodcastData
*/ */
public static function fromArray(array $data) {} public static function fromArray(array $data) {}

View File

@ -21,7 +21,6 @@ class EpisodeActionMapper extends QBMapper
/** /**
* @return EpisodeActionEntity[] * @return EpisodeActionEntity[]
*
* @throws Exception * @throws Exception
*/ */
public function findAll(int $sinceTimestamp, string $userId) {} public function findAll(int $sinceTimestamp, string $userId) {}

View File

@ -14,14 +14,12 @@ class EpisodeActionWriter
/** /**
* @return EpisodeActionEntity * @return EpisodeActionEntity
*
* @throws Exception * @throws Exception
*/ */
public function save(EpisodeActionEntity $episodeActionEntity) {} public function save(EpisodeActionEntity $episodeActionEntity) {}
/** /**
* @return EpisodeActionEntity * @return EpisodeActionEntity
*
* @throws Exception * @throws Exception
*/ */
public function update(EpisodeActionEntity $episodeActionEntity) {} public function update(EpisodeActionEntity $episodeActionEntity) {}

View File

@ -1,22 +1,20 @@
# SOME DESCRIPTIVE TITLE. # SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the Nextcloud package. # This file is distributed under the same license as the Nextcloud package.
# OiledAmoeba <florian+crystalyx@ruhnke.cloud>, 2024. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# Michel Roux <xefir@crystalyx.net>, 2024. #
#, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Nextcloud 3.14159\n" "Project-Id-Version: Nextcloud 3.14159\n"
"Report-Msgid-Bugs-To: translations\\@example.com\n" "Report-Msgid-Bugs-To: translations\\@example.com\n"
"PO-Revision-Date: 2024-11-15 21:02+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Michel Roux <xefir@crystalyx.net>\n" "Last-Translator: OiledAmoeba <florian+crystalyx@ruhnke.cloud>\n"
"Language-Team: German <https://translate.crystalyx.net/projects/repod/gitea/" "Language-Team: LANGUAGE <LL@li.org>\n"
"de/>\n"
"Language: de\n" "Language: de\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.8.3\n"
msgid "RePod Subscriptions" msgid "RePod Subscriptions"
msgstr "RePod Abonnements" msgstr "RePod Abonnements"
@ -30,7 +28,6 @@ msgstr "RePod"
msgid "🔊 Browse, manage and listen to podcasts" msgid "🔊 Browse, manage and listen to podcasts"
msgstr "🔊 Suchen, Verwalten und Anhören von Podcasts" msgstr "🔊 Suchen, Verwalten und Anhören von Podcasts"
#, fuzzy
msgid "" msgid ""
"## Features\n" "## Features\n"
"- 🔍 Browse and subscribe huge collection of podcasts\n" "- 🔍 Browse and subscribe huge collection of podcasts\n"
@ -50,12 +47,9 @@ msgstr ""
"## Funktionen\n" "## Funktionen\n"
"- 🔍 Durchsuchen und abonnieren einer großen Sammlung von Podcasts\n" "- 🔍 Durchsuchen und abonnieren einer großen Sammlung von Podcasts\n"
"- 🔊 Episoden direkt in Nextcloud anhören\n" "- 🔊 Episoden direkt in Nextcloud anhören\n"
"- 🌐 Synchronisiere deine Aktivität mit [AntennaPod](https://antennapod.org/)" "- 🌐 Synchronisiere deine Aktivität mit [AntennaPod](https://antennapod.org/)\n"
"\n"
"- 📱 Mobile-freundliche Schnittstelle\n" "- 📱 Mobile-freundliche Schnittstelle\n"
"- 📡 Importieren und Exportieren Ihrer Abonnements\n" "- 📡 Importieren und Exportieren Ihrer Abonnements\n"
"- ➡️ Vollständiger Funktionsvergleich [hier](https://git.crystalyx.net/Xefir/"
"repod#comparaison-with-similar-apps-for-nextcloud)\n"
"\n" "\n"
"## Voraussetzungen\n" "## Voraussetzungen\n"
"Du musst [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) " "Du musst [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) "
@ -64,10 +58,6 @@ msgstr ""
msgid "Download" msgid "Download"
msgstr "Herunterladen" msgstr "Herunterladen"
#, fuzzy
msgid "Skip to {match}"
msgstr "Springen zu {match}"
msgid "Add a RSS link" msgid "Add a RSS link"
msgstr "Einen RSS-Link hinzufügen" msgstr "Einen RSS-Link hinzufügen"
@ -92,7 +82,6 @@ msgstr "Titel können nicht abgerufen werden"
msgid "Copy feed" msgid "Copy feed"
msgstr "Feed kopieren" msgstr "Feed kopieren"
#, fuzzy
msgid "Link copied to the clipboard" msgid "Link copied to the clipboard"
msgstr "Der Link des Feeds wurde in die Zwischenablage kopiert" msgstr "Der Link des Feeds wurde in die Zwischenablage kopiert"
@ -102,7 +91,6 @@ msgstr "Abspielen"
msgid "Stop" msgid "Stop"
msgstr "Stopp" msgstr "Stopp"
#, fuzzy
msgid "Read" msgid "Read"
msgstr "Gelesen" msgstr "Gelesen"
@ -115,22 +103,18 @@ msgstr "Kann den Status der Folge nicht ändern"
msgid "Could not fetch episodes" msgid "Could not fetch episodes"
msgstr "Folgen können nicht abgerufen werden" msgstr "Folgen können nicht abgerufen werden"
#, fuzzy
msgid "Rewind 10 seconds" msgid "Rewind 10 seconds"
msgstr "10 Sekunden zurückspulen" msgstr "10 Sekunden zurückspulen"
msgid "Pause" msgid "Pause"
msgstr "Pause" msgstr "Pause"
#, fuzzy
msgid "Fast forward 30 seconds" msgid "Fast forward 30 seconds"
msgstr "30 Sekunden vorspulen" msgstr "30 Sekunden vorspulen"
#, fuzzy
msgid "Mute" msgid "Mute"
msgstr "Stumm" msgstr "Stumm"
#, fuzzy
msgid "Unmute" msgid "Unmute"
msgstr "Stummschalten" msgstr "Stummschalten"
@ -161,31 +145,25 @@ msgstr "Importiere OPML-Datei"
msgid "Rate RePod ❤️" msgid "Rate RePod ❤️"
msgstr "Bewerte RePod ❤️" msgstr "Bewerte RePod ❤️"
#, fuzzy
msgid "Sleep timer" msgid "Sleep timer"
msgstr "Einschlaftimer" msgstr "Einschlaftimer"
#, fuzzy
msgid "Minutes" msgid "Minutes"
msgstr "Minuten" msgstr "Minuten"
#, fuzzy
msgid "%n min" msgid "%n min"
msgid_plural "%n mins" msgid_plural "%n mins"
msgstr[0] "%n min" msgstr[0] "%n min"
msgstr[1] "%n mins" msgstr[1] "%n mins"
#, fuzzy
msgid "%n sec" msgid "%n sec"
msgid_plural "%n secs" msgid_plural "%n secs"
msgstr[0] "%s sec" msgstr[0] "%s sec"
msgstr[1] "%n secs" msgstr[1] "%n secs"
#, fuzzy
msgid "Playback speed" msgid "Playback speed"
msgstr "Wiedergabegeschwindigkeit" msgstr "Wiedergabegeschwindigkeit"
#, fuzzy
msgid "Favorite" msgid "Favorite"
msgstr "Favorit" msgstr "Favorit"
@ -195,7 +173,6 @@ msgstr "Bist Du sicher, dass Du das Abonnement löschen möchtest?"
msgid "Error while removing the feed" msgid "Error while removing the feed"
msgstr "Fehler beim Löschen des Feeds" msgstr "Fehler beim Löschen des Feeds"
#, fuzzy
msgid "You can only have 10 favorites" msgid "You can only have 10 favorites"
msgstr "Du kannst nur 10 Favoriten haben" msgstr "Du kannst nur 10 Favoriten haben"
@ -217,14 +194,11 @@ msgstr "Benötigte App fehlt"
msgid "Install GPodder Sync" msgid "Install GPodder Sync"
msgstr "Installiere GPodder Sync" msgstr "Installiere GPodder Sync"
#, fuzzy
msgid "Pin some subscriptions to see their latest updates" msgid "Pin some subscriptions to see their latest updates"
msgstr "Pinne einige Abonnements, um ihre neuesten Updates zu sehen" msgstr "Pinne einige Abonnements, um ihre neuesten Updates zu sehen"
#, fuzzy
msgid "No favorites" msgid "No favorites"
msgstr "Keine Favoriten" msgstr "Keine Favoriten"
#, fuzzy
msgid "A browser extension conflict with RePod" msgid "A browser extension conflict with RePod"
msgstr "Ein Browser-Erweiterungskonflikt mit RePod" msgstr "Ein Browser-Erweiterungskonflikt mit RePod"

View File

@ -62,9 +62,6 @@ msgstr ""
msgid "Download" msgid "Download"
msgstr "Télécharger" msgstr "Télécharger"
msgid "Skip to {match}"
msgstr "Sauter à {match}"
msgid "Add a RSS link" msgid "Add a RSS link"
msgstr "Ajouter un lien RSS" msgstr "Ajouter un lien RSS"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Nextcloud 3.14159\n" "Project-Id-Version: Nextcloud 3.14159\n"
"Report-Msgid-Bugs-To: translations\\@example.com\n" "Report-Msgid-Bugs-To: translations\\@example.com\n"
"POT-Creation-Date: 2024-11-12 20:57+0000\n" "POT-Creation-Date: 2024-11-09 18:34+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -55,215 +55,211 @@ msgid ""
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:1 #: /app/specialVueFakeDummyForL10nScript.js:1
#: /app/specialVueFakeDummyForL10nScript.js:24
#: /app/specialVueFakeDummyForL10nScript.js:25 #: /app/specialVueFakeDummyForL10nScript.js:25
#: /app/specialVueFakeDummyForL10nScript.js:26
msgid "Download" msgid "Download"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:2 #: /app/specialVueFakeDummyForL10nScript.js:2
msgid "Skip to {match}"
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:3
msgid "Add a RSS link" msgid "Add a RSS link"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:3
#: /app/specialVueFakeDummyForL10nScript.js:4 #: /app/specialVueFakeDummyForL10nScript.js:4
#: /app/specialVueFakeDummyForL10nScript.js:5 #: /app/specialVueFakeDummyForL10nScript.js:5
#: /app/specialVueFakeDummyForL10nScript.js:6 #: /app/specialVueFakeDummyForL10nScript.js:12
#: /app/specialVueFakeDummyForL10nScript.js:13
msgid "Subscribe" msgid "Subscribe"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:7 #: /app/specialVueFakeDummyForL10nScript.js:6
#: /app/specialVueFakeDummyForL10nScript.js:14 #: /app/specialVueFakeDummyForL10nScript.js:13
msgid "Error while adding the feed" msgid "Error while adding the feed"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:8 #: /app/specialVueFakeDummyForL10nScript.js:7
msgid "Could not fetch search results" msgid "Could not fetch search results"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:9 #: /app/specialVueFakeDummyForL10nScript.js:8
msgid "New podcasts" msgid "New podcasts"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:10 #: /app/specialVueFakeDummyForL10nScript.js:9
msgid "Hot podcasts" msgid "Hot podcasts"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:11 #: /app/specialVueFakeDummyForL10nScript.js:10
msgid "Could not fetch tops" msgid "Could not fetch tops"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:12 #: /app/specialVueFakeDummyForL10nScript.js:11
msgid "Copy feed" msgid "Copy feed"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:15 #: /app/specialVueFakeDummyForL10nScript.js:14
msgid "Link copied to the clipboard" msgid "Link copied to the clipboard"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:15
#: /app/specialVueFakeDummyForL10nScript.js:16 #: /app/specialVueFakeDummyForL10nScript.js:16
#: /app/specialVueFakeDummyForL10nScript.js:17 #: /app/specialVueFakeDummyForL10nScript.js:31
#: /app/specialVueFakeDummyForL10nScript.js:32
msgid "Play" msgid "Play"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:17
#: /app/specialVueFakeDummyForL10nScript.js:18 #: /app/specialVueFakeDummyForL10nScript.js:18
#: /app/specialVueFakeDummyForL10nScript.js:19
msgid "Stop" msgid "Stop"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:19
#: /app/specialVueFakeDummyForL10nScript.js:20 #: /app/specialVueFakeDummyForL10nScript.js:20
#: /app/specialVueFakeDummyForL10nScript.js:21 #: /app/specialVueFakeDummyForL10nScript.js:21
#: /app/specialVueFakeDummyForL10nScript.js:22
msgid "Read" msgid "Read"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:22
#: /app/specialVueFakeDummyForL10nScript.js:23 #: /app/specialVueFakeDummyForL10nScript.js:23
#: /app/specialVueFakeDummyForL10nScript.js:24
msgid "Open website" msgid "Open website"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:27 #: /app/specialVueFakeDummyForL10nScript.js:26
msgid "Could not change the status of the episode" msgid "Could not change the status of the episode"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:27
#: /app/specialVueFakeDummyForL10nScript.js:28 #: /app/specialVueFakeDummyForL10nScript.js:28
#: /app/specialVueFakeDummyForL10nScript.js:29
msgid "Could not fetch episodes" msgid "Could not fetch episodes"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:30 #: /app/specialVueFakeDummyForL10nScript.js:29
msgid "Rewind 10 seconds" msgid "Rewind 10 seconds"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:31 #: /app/specialVueFakeDummyForL10nScript.js:30
msgid "Pause" msgid "Pause"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:33 #: /app/specialVueFakeDummyForL10nScript.js:32
msgid "Fast forward 30 seconds" msgid "Fast forward 30 seconds"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:33
#: /app/specialVueFakeDummyForL10nScript.js:34 #: /app/specialVueFakeDummyForL10nScript.js:34
#: /app/specialVueFakeDummyForL10nScript.js:35 #: /app/specialVueFakeDummyForL10nScript.js:35
#: /app/specialVueFakeDummyForL10nScript.js:36
msgid "Mute" msgid "Mute"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:37 #: /app/specialVueFakeDummyForL10nScript.js:36
msgid "Unmute" msgid "Unmute"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:38 #: /app/specialVueFakeDummyForL10nScript.js:37
msgid "Export subscriptions" msgid "Export subscriptions"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:39 #: /app/specialVueFakeDummyForL10nScript.js:38
msgid "Filtering episodes" msgid "Filtering episodes"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:40 #: /app/specialVueFakeDummyForL10nScript.js:39
msgid "Show all" msgid "Show all"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:41 #: /app/specialVueFakeDummyForL10nScript.js:40
msgid "Listened" msgid "Listened"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:42 #: /app/specialVueFakeDummyForL10nScript.js:41
msgid "Listening" msgid "Listening"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:43 #: /app/specialVueFakeDummyForL10nScript.js:42
msgid "Unlistened" msgid "Unlistened"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:44 #: /app/specialVueFakeDummyForL10nScript.js:43
msgid "Import subscriptions" msgid "Import subscriptions"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:45 #: /app/specialVueFakeDummyForL10nScript.js:44
msgid "Import OPML file" msgid "Import OPML file"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:46 #: /app/specialVueFakeDummyForL10nScript.js:45
msgid "Rate RePod ❤️" msgid "Rate RePod ❤️"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:47 #: /app/specialVueFakeDummyForL10nScript.js:46
msgid "Sleep timer" msgid "Sleep timer"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:48 #: /app/specialVueFakeDummyForL10nScript.js:47
msgid "Minutes" msgid "Minutes"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:49 #: /app/specialVueFakeDummyForL10nScript.js:48
msgid "%n min" msgid "%n min"
msgid_plural "%n mins" msgid_plural "%n mins"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: /app/specialVueFakeDummyForL10nScript.js:50 #: /app/specialVueFakeDummyForL10nScript.js:49
msgid "%n sec" msgid "%n sec"
msgid_plural "%n secs" msgid_plural "%n secs"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: /app/specialVueFakeDummyForL10nScript.js:51 #: /app/specialVueFakeDummyForL10nScript.js:50
msgid "Playback speed" msgid "Playback speed"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:51
#: /app/specialVueFakeDummyForL10nScript.js:52 #: /app/specialVueFakeDummyForL10nScript.js:52
#: /app/specialVueFakeDummyForL10nScript.js:53 #: /app/specialVueFakeDummyForL10nScript.js:53
#: /app/specialVueFakeDummyForL10nScript.js:54
msgid "Favorite" msgid "Favorite"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:55 #: /app/specialVueFakeDummyForL10nScript.js:54
msgid "Are you sure you want to delete this subscription?" msgid "Are you sure you want to delete this subscription?"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:56 #: /app/specialVueFakeDummyForL10nScript.js:55
msgid "Error while removing the feed" msgid "Error while removing the feed"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:57 #: /app/specialVueFakeDummyForL10nScript.js:56
msgid "You can only have 10 favorites" msgid "You can only have 10 favorites"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:58 #: /app/specialVueFakeDummyForL10nScript.js:57
msgid "Add a podcast" msgid "Add a podcast"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:59 #: /app/specialVueFakeDummyForL10nScript.js:58
msgid "Could not fetch subscriptions" msgid "Could not fetch subscriptions"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:60 #: /app/specialVueFakeDummyForL10nScript.js:59
msgid "Find a podcast" msgid "Find a podcast"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:61 #: /app/specialVueFakeDummyForL10nScript.js:60
msgid "Error loading feed" msgid "Error loading feed"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:62 #: /app/specialVueFakeDummyForL10nScript.js:61
msgid "Missing required app" msgid "Missing required app"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:63 #: /app/specialVueFakeDummyForL10nScript.js:62
msgid "Install GPodder Sync" msgid "Install GPodder Sync"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:64 #: /app/specialVueFakeDummyForL10nScript.js:63
msgid "Pin some subscriptions to see their latest updates" msgid "Pin some subscriptions to see their latest updates"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:65 #: /app/specialVueFakeDummyForL10nScript.js:64
msgid "No favorites" msgid "No favorites"
msgstr "" msgstr ""

View File

@ -10,6 +10,6 @@
"noImplicitAny": false, "noImplicitAny": false,
"rootDir": ".", "rootDir": ".",
"strict": true, "strict": true,
"noEmit": true "noEmit": true,
}
} }
}