diff --git a/composer.json b/composer.json
index 4d621a9..ce47578 100644
--- a/composer.json
+++ b/composer.json
@@ -2,13 +2,6 @@
 	"name": "nextcloud/opds_catalog",
 	"description": "An example description",
 	"license": "AGPL-3.0-or-later",
-	"authors": [
-		{
-			"name": "example",
-			"email": "example@example.com",
-			"homepage": "https://example.com"
-		}
-	],
 	"autoload": {
 		"psr-4": {
 			"OCA\\OpdsCatalog\\": "lib/"
@@ -17,7 +10,10 @@
 	"autoload-dev": {
 		"psr-4": {
 			"OCP\\": "vendor/nextcloud/ocp/OCP/"
-		}
+		},
+		"files": [
+			"stubs/OC_Image.php"
+		]
 	},
 	"scripts": {
 		"lint": "find . -name \\*.php -not -path './vendor/*' -not -path './vendor-bin/*' -not -path './build/*' -print0 | xargs -0 -n1 php -l",
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index e7de913..b246e02 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -4,6 +4,10 @@ declare(strict_types=1);
 
 namespace OCA\OpdsCatalog\AppInfo;
 
+use OCA\OpdsCatalog\Provider\AmazonPreviewProvider;
+use OCA\OpdsCatalog\Provider\ComicBookPreviewProvider;
+use OCA\OpdsCatalog\Provider\EpubPreviewProvider;
+use OCA\OpdsCatalog\Provider\FictionBookPreviewProvider;
 use OCP\AppFramework\App;
 use OCP\AppFramework\Bootstrap\IBootContext;
 use OCP\AppFramework\Bootstrap\IBootstrap;
@@ -17,7 +21,12 @@ class Application extends App implements IBootstrap
 		parent::__construct(self::APP_ID);
 	}
 
-	public function register(IRegistrationContext $context): void {}
+	public function register(IRegistrationContext $context): void {
+		$context->registerPreviewProvider(AmazonPreviewProvider::class, '/application\/(vnd.amazon.(ebook|mobi8-ebook)|x-(mobi8|mobipocket)-ebook)/');
+		$context->registerPreviewProvider(ComicBookPreviewProvider::class, '/application\/(vnd.comicbook[+-](rar|zip)|x-cb[7rtz])/');
+		$context->registerPreviewProvider(EpubPreviewProvider::class, '/application\/epub\+zip/');
+		$context->registerPreviewProvider(FictionBookPreviewProvider::class, '/application\/x-fictionbook.*/');
+	}
 
 	public function boot(IBootContext $context): void {}
 }
diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php
index 9dd1b80..0f2a630 100644
--- a/lib/Controller/PageController.php
+++ b/lib/Controller/PageController.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
 
 namespace OCA\OpdsCatalog\Controller;
 
+use Kiwilan\Opds\Entries\OpdsEntryNavigation;
 use Kiwilan\Opds\Enums\OpdsVersionEnum;
 use Kiwilan\Opds\Opds;
 use Kiwilan\Opds\OpdsConfig;
@@ -17,6 +18,9 @@ use OCP\AppFramework\Http\DataResponse;
 use OCP\AppFramework\Http\JSONResponse;
 use OCP\AppFramework\Http\NotFoundResponse;
 use OCP\AppFramework\Http\Response;
+use OCP\Files\File;
+use OCP\Files\Folder;
+use OCP\Files\IRootFolder;
 use OCP\IRequest;
 use OCP\IURLGenerator;
 use OCP\IUser;
@@ -25,6 +29,7 @@ class PageController extends Controller
 {
 	public function __construct(
 		IRequest $request,
+		private readonly IRootFolder $rootFolder,
 		private readonly IUser $user,
 		private readonly IURLGenerator $urlGenerator
 	) {
@@ -33,8 +38,29 @@ class PageController extends Controller
 
 	#[NoCSRFRequired]
 	#[NoAdminRequired]
-	#[FrontpageRoute(verb: 'GET', url: '/')]
-	public function index(): Response {
+	#[FrontpageRoute(verb: 'GET', url: '/{path}', requirements: ['path' => '.+'])]
+	public function index(string $path): Response {
+		$userFolder = $this->rootFolder->getUserFolder($this->user->getUID());
+		$root = $userFolder->get($path);
+		$feeds = [];
+
+		if ($root instanceof Folder) {
+			foreach ($root->getDirectoryListing() as $node) {
+				if ($node instanceof Folder) {
+					$feeds[] = new OpdsEntryNavigation(
+						(string) $node->getId(),
+						$node->getName(),
+						$userFolder->getRelativePath($node->getPath()) ?? $path.'/'.$node->getName(),
+						properties: [
+							'numberOfItems' => count($node->getDirectoryListing()),
+						],
+						updated: new \DateTime('@'.$node->getMTime())
+					);
+				} elseif ($node instanceof File) {
+				}
+			}
+		}
+
 		$config = new OpdsConfig(
 			name: $this->user->getDisplayName()."'s Library",
 			author: $this->user->getDisplayName(),
diff --git a/lib/Provider/AmazonPreviewProvider.php b/lib/Provider/AmazonPreviewProvider.php
new file mode 100644
index 0000000..963c44d
--- /dev/null
+++ b/lib/Provider/AmazonPreviewProvider.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace OCA\OpdsCatalog\Provider;
+
+class AmazonPreviewProvider extends EbookPreviewProvider
+{
+	public function getMimeType(): string {
+		return '/application\/(vnd.amazon.(ebook|mobi8-ebook)|x-(mobi8|mobipocket)-ebook)/';
+	}
+}
diff --git a/lib/Provider/ComicBookPreviewProvider.php b/lib/Provider/ComicBookPreviewProvider.php
new file mode 100644
index 0000000..115e39e
--- /dev/null
+++ b/lib/Provider/ComicBookPreviewProvider.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace OCA\OpdsCatalog\Provider;
+
+class ComicBookPreviewProvider extends EbookPreviewProvider
+{
+	public function getMimeType(): string {
+		return '/application\/(vnd.comicbook[+-](rar|zip)|x-cb[7rtz])/';
+	}
+}
diff --git a/lib/Provider/EbookPreviewProvider.php b/lib/Provider/EbookPreviewProvider.php
new file mode 100644
index 0000000..61777d3
--- /dev/null
+++ b/lib/Provider/EbookPreviewProvider.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+namespace OCA\OpdsCatalog\Provider;
+
+use Kiwilan\Ebook\Ebook;
+use Kiwilan\Ebook\EbookCover;
+use OCP\Files\File;
+use OCP\Files\FileInfo;
+use OCP\IImage;
+use OCP\Image;
+use OCP\Preview\IProviderV2;
+
+abstract class EbookPreviewProvider implements IProviderV2
+{
+	public function isAvailable(FileInfo $file): bool {
+		return Ebook::isValid($file->getPath());
+	}
+
+	public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
+		$ebook = Ebook::read($file->getPath());
+
+		if ($ebook instanceof Ebook) {
+			$cover = $ebook->getCover();
+
+			if ($cover instanceof EbookCover) {
+				$path = $cover->getPath();
+
+				if (null !== $path) {
+					$image = new Image();
+					$image->loadFromFile($path);
+					$image->scaleDownToFit($maxX, $maxY);
+
+					return $image;
+				}
+			}
+		}
+
+		return null;
+	}
+}
diff --git a/lib/Provider/EpubPreviewProvider.php b/lib/Provider/EpubPreviewProvider.php
new file mode 100644
index 0000000..c6e4bb6
--- /dev/null
+++ b/lib/Provider/EpubPreviewProvider.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace OCA\OpdsCatalog\Provider;
+
+class EpubPreviewProvider extends EbookPreviewProvider
+{
+	public function getMimeType(): string {
+		return '/application\/epub\+zip/';
+	}
+}
diff --git a/lib/Provider/FictionBookPreviewProvider.php b/lib/Provider/FictionBookPreviewProvider.php
new file mode 100644
index 0000000..18c4566
--- /dev/null
+++ b/lib/Provider/FictionBookPreviewProvider.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace OCA\OpdsCatalog\Provider;
+
+class FictionBookPreviewProvider extends EbookPreviewProvider
+{
+	public function getMimeType(): string {
+		return '/application\/x-fictionbook.*/';
+	}
+}
diff --git a/stubs/OC_Image.php b/stubs/OC_Image.php
new file mode 100644
index 0000000..07b1811
--- /dev/null
+++ b/stubs/OC_Image.php
@@ -0,0 +1,20 @@
+<?php
+
+use OCP\IImage;
+
+interface OC_Image extends IImage
+{
+	/**
+	 * Loads an image from a local file.
+	 *
+	 * @param bool|string $imagePath the path to a local file
+	 *
+	 * @return bool|GdImage An image resource or false on error
+	 */
+	public function loadFromFile($imagePath = false);
+
+	/**
+	 * Shrinks larger images to fit within specified boundaries while preserving ratio.
+	 */
+	public function scaleDownToFit(int $maxWidth, int $maxHeight): bool;
+}