From 1ce788bb3304e72188cc84482e36198228e8796a Mon Sep 17 00:00:00 2001 From: Chen Asraf Date: Sat, 7 Jun 2025 00:04:39 +0300 Subject: [PATCH] feat: music scanner, settings page & api --- CHANGELOG.md | 171 +++++++++++ appinfo/info.xml | 7 +- lib/AppInfo/Application.php | 1 + lib/Command/ScanMusic.php | 51 ++++ lib/Controller/SettingsController.php | 82 ++++++ lib/Sections/JukeboxUserSection.php | 36 +++ lib/Service/MusicScanner.php | 77 +++-- lib/Settings/JukeboxAdmin.php | 44 +++ lib/Settings/JukeboxUserSettings.php | 39 +++ package.json | 1 + pnpm-lock.yaml | 249 ++++++++++++++++ scaffold.config.cjs | 79 +++++ src/Settings.vue | 407 ++++++-------------------- src/axios.ts | 7 + src/settings.ts | 8 +- version.txt | 1 + vite.config.ts | 11 +- 17 files changed, 923 insertions(+), 348 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 lib/Command/ScanMusic.php create mode 100644 lib/Controller/SettingsController.php create mode 100644 lib/Sections/JukeboxUserSection.php create mode 100644 lib/Settings/JukeboxAdmin.php create mode 100644 lib/Settings/JukeboxUserSettings.php create mode 100644 scaffold.config.cjs create mode 100644 src/axios.ts create mode 100644 version.txt diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4729a27 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,171 @@ +# Changelog + +## [0.7.0](https://github.com/chenasraf/nextcloud-jukebox/compare/v0.6.4...v0.7.0) (2025-05-28) + +### Features + +- add localizations + ([5910320](https://github.com/chenasraf/nextcloud-jukebox/commit/5910320b90507cc65a89d2bffb2d24f39d2a15ca)) + +### Bug Fixes + +- **ApiController:** use argument param in updateSettings + ([aba72a1](https://github.com/chenasraf/nextcloud-jukebox/commit/aba72a13f2a6379ee128ee5ffb21a3fe1ea8ccdc)) +- rounding + ([ff02782](https://github.com/chenasraf/nextcloud-jukebox/commit/ff027827de1ac3d70cf7eeb818dbebdaf5b2e4a2)) + +## [0.6.4](https://github.com/chenasraf/nextcloud-jukebox/compare/v0.6.3...v0.6.4) (2025-01-18) + +### Bug Fixes + +- skip projects with missing default currency + ([d015f26](https://github.com/chenasraf/nextcloud-jukebox/commit/d015f26bc2a953367cdcb4c365f2633b486f1b4d)) + +## [0.6.3](https://github.com/chenasraf/nextcloud-jukebox/compare/v0.6.2...v0.6.3) (2024-12-25) + +### Bug Fixes + +- support cospend v3.0.7 + ([39f1a9e](https://github.com/chenasraf/nextcloud-jukebox/commit/39f1a9efc0af68ae6a2f3cf5b3c769957da75405)), + closes [#33](https://github.com/chenasraf/nextcloud-jukebox/issues/33) + +## [0.6.2](https://github.com/chenasraf/nextcloud-jukebox/compare/v0.6.1...v0.6.2) (2024-12-21) + +### Bug Fixes + +- log level + methods docstring for currency + ([dff6d94](https://github.com/chenasraf/nextcloud-jukebox/commit/dff6d947d3fe857d95ae028eb9383a7600ad27f4)) + +## [0.6.1](https://github.com/chenasraf/nextcloud-jukebox/compare/v0.6.0...v0.6.1) (2024-12-08) + +### Bug Fixes + +- appstore script path resolution + ([1c02b79](https://github.com/chenasraf/nextcloud-jukebox/commit/1c02b796c55074a6afd1fec3c5aaf815f0947f75)) + +## [0.6.0](https://github.com/chenasraf/nextcloud-jukebox/compare/v0.5.2...v0.6.0) (2024-12-08) + +### Features + +- add supported currencies table + ([b05290b](https://github.com/chenasraf/nextcloud-jukebox/commit/b05290beab361e44354df489074d40b93c4ea2e5)) + +### Bug Fixes + +- sort currencies table + ([56e160f](https://github.com/chenasraf/nextcloud-jukebox/commit/56e160f3a19bd6e0a399c0236c14899edc25a4b2)) + +## [0.5.2](https://github.com/chenasraf/nextcloud-jukebox/compare/v0.5.1...v0.5.2) (2024-12-07) + +### Bug Fixes + +- add missing eur currency info + ([7948fd9](https://github.com/chenasraf/nextcloud-jukebox/commit/7948fd9a456654c0d81ab73501d0a87056ea49cb)) + +## [0.5.1](https://github.com/chenasraf/nextcloud-jukebox/compare/v0.5.0...v0.5.1) (2024-12-06) + +### Bug Fixes + +- settings section priority + ([da05142](https://github.com/chenasraf/nextcloud-jukebox/commit/da0514250882472b7b3ef0f9f293e0cf6f5568a5)) + +## [0.5.0](https://github.com/chenasraf/nextcloud-jukebox/compare/v0.4.0...v0.5.0) (2024-12-05) + +### Features + +- update admin page + ([ab029a3](https://github.com/chenasraf/nextcloud-jukebox/commit/ab029a3ecdec763dbe79ef38d8e0bf1676ef00b4)) +- update admin page ui + ([32c2c94](https://github.com/chenasraf/nextcloud-jukebox/commit/32c2c94526148efe767584c79ef8a380f26c0252)) +- update app+settings icons + ([b85256f](https://github.com/chenasraf/nextcloud-jukebox/commit/b85256f5a236b5701013878b18e03e1c8baabd07)) + +## [0.4.0](https://github.com/chenasraf/nextcloud-jukebox/compare/v0.3.0...v0.4.0) (2024-12-05) + +### Features + +- add admin page intro section + ([023b2fd](https://github.com/chenasraf/nextcloud-jukebox/commit/023b2fd61c28cfdcb9a787b4cb4b5d853dffcdad)) + +### Bug Fixes + +- only include available currencies + ([902522f](https://github.com/chenasraf/nextcloud-jukebox/commit/902522f20f29382a837c0062a0c08c3f681cef73)) + +## [0.3.0](https://github.com/chenasraf/nextcloud-jukebox/compare/v0.2.1...v0.3.0) (2024-12-04) + +### Features + +- improve currency matching + ([8738623](https://github.com/chenasraf/nextcloud-jukebox/commit/87386235c22a6dcd09f17cbeaa094152ccfd8540)) + +## [0.2.1](https://github.com/chenasraf/nextcloud-jukebox/compare/v0.2.0...v0.2.1) (2024-12-03) + +### Bug Fixes + +- release artifact tar + ([8d632ef](https://github.com/chenasraf/nextcloud-jukebox/commit/8d632ef7f215255246f209ab6e0593ef786e2bfc)) + +## [0.2.0](https://github.com/chenasraf/nextcloud-jukebox/compare/v0.1.0...v0.2.0) (2024-12-03) + +### Features + +- add admin example + ([1b3804b](https://github.com/chenasraf/nextcloud-jukebox/commit/1b3804ba0d8f73687c4260fbb2f20aac4470b758)) +- admin page view + ([68ae6eb](https://github.com/chenasraf/nextcloud-jukebox/commit/68ae6eb09e35057e426072c9986c7965d29401ea)) +- api controller poc + ([802c72f](https://github.com/chenasraf/nextcloud-jukebox/commit/802c72f0f7dd9be5f9abc3829ff403b9abfda7f8)) +- settings page logic + ([20d66b3](https://github.com/chenasraf/nextcloud-jukebox/commit/20d66b3650f53701a9a9ec54ac9cf15961592ced)) +- update name + ([ab7c03b](https://github.com/chenasraf/nextcloud-jukebox/commit/ab7c03b42475f701a151f383f06170f999d51c75)) + +### Bug Fixes + +- app info + ([aebc8a5](https://github.com/chenasraf/nextcloud-jukebox/commit/aebc8a52cc49cb736ce5e78f23ddc0626006a4d1)) +- build cmd + ([4046b7b](https://github.com/chenasraf/nextcloud-jukebox/commit/4046b7b8df01bce39fa4f31947971166b8f4aa56)) +- cron + ([5aebc7a](https://github.com/chenasraf/nextcloud-jukebox/commit/5aebc7a2aa46fba0daf403f11baa731620e335ae)) +- tests + use IAppConfig + ([b6058ef](https://github.com/chenasraf/nextcloud-jukebox/commit/b6058eff576790620f8b8166550d903872731f1d)) +- typescript version + ([c4f625a](https://github.com/chenasraf/nextcloud-jukebox/commit/c4f625a19236df7834a68b6a7d75c8b27d5113e6)) +- updateSettings ep + types + ([aae1dbc](https://github.com/chenasraf/nextcloud-jukebox/commit/aae1dbc141ab9c6ee8d57682e283d8615e3c4c91)) + +## 0.1.0 (2024-12-03) + +### Features + +- add admin example + ([1b3804b](https://github.com/chenasraf/nextcloud-jukebox/commit/1b3804ba0d8f73687c4260fbb2f20aac4470b758)) +- admin page view + ([68ae6eb](https://github.com/chenasraf/nextcloud-jukebox/commit/68ae6eb09e35057e426072c9986c7965d29401ea)) +- api controller poc + ([802c72f](https://github.com/chenasraf/nextcloud-jukebox/commit/802c72f0f7dd9be5f9abc3829ff403b9abfda7f8)) +- poc ready + ([e54cb41](https://github.com/chenasraf/nextcloud-jukebox/commit/e54cb41c5b549294fc8b014ef2a507178f4e8597)) +- poc working + ([c32cdaf](https://github.com/chenasraf/nextcloud-jukebox/commit/c32cdaf38de64f45de1285463f4265da2e95b438)) +- settings page logic + ([20d66b3](https://github.com/chenasraf/nextcloud-jukebox/commit/20d66b3650f53701a9a9ec54ac9cf15961592ced)) +- update name + ([ab7c03b](https://github.com/chenasraf/nextcloud-jukebox/commit/ab7c03b42475f701a151f383f06170f999d51c75)) + +### Bug Fixes + +- app info + ([aebc8a5](https://github.com/chenasraf/nextcloud-jukebox/commit/aebc8a52cc49cb736ce5e78f23ddc0626006a4d1)) +- build cmd + ([4046b7b](https://github.com/chenasraf/nextcloud-jukebox/commit/4046b7b8df01bce39fa4f31947971166b8f4aa56)) +- cron + ([5aebc7a](https://github.com/chenasraf/nextcloud-jukebox/commit/5aebc7a2aa46fba0daf403f11baa731620e335ae)) +- tests + use IAppConfig + ([b6058ef](https://github.com/chenasraf/nextcloud-jukebox/commit/b6058eff576790620f8b8166550d903872731f1d)) +- typescript version + ([c4f625a](https://github.com/chenasraf/nextcloud-jukebox/commit/c4f625a19236df7834a68b6a7d75c8b27d5113e6)) +- updateSettings ep + types + ([aae1dbc](https://github.com/chenasraf/nextcloud-jukebox/commit/aae1dbc141ab9c6ee8d57682e283d8615e3c4c91)) diff --git a/appinfo/info.xml b/appinfo/info.xml index 0e246a2..2a823fe 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -47,7 +47,10 @@ It supports music files, podcasts (with gPodder sync), audiobooks, YouTube video OCA\Jukebox\Cron\FetchCurrenciesJob --> - OCA\Jukebox\Settings\CurrencyAdmin - OCA\Jukebox\Sections\CurrencyAdmin + OCA\Jukebox\Settings\JukeboxUserSettings + OCA\Jukebox\Sections\JukeboxUserSection + + OCA\Jukebox\Command\ScanMusic + diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 99cd5a6..27a1a45 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -21,6 +21,7 @@ class Application extends App implements IBootstrap { } public function register(IRegistrationContext $context): void { + include_once __DIR__ . '/../../vendor/autoload.php'; } public function boot(IBootContext $context): void { diff --git a/lib/Command/ScanMusic.php b/lib/Command/ScanMusic.php new file mode 100644 index 0000000..9f0a610 --- /dev/null +++ b/lib/Command/ScanMusic.php @@ -0,0 +1,51 @@ + + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\Jukebox\Command; + +use OCA\Jukebox\Service\MusicScanner; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class ScanMusic extends Command { + public function __construct( + private MusicScanner $service, + ) { + parent::__construct(); + } + + protected function configure(): void { + $this + ->setName('jukebox:scan-music') + ->setDescription('Scan music files for a user') + ->addArgument('uid', InputArgument::OPTIONAL, 'User ID to scan. If not provided, uses the current session user.'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $uid = $input->getArgument('uid'); + + try { + if ($uid) { + $output->writeln("Scanning music files for user '$uid'..."); + $this->service->scanUserByUID($uid); + } else { + $output->writeln('Scanning music files for the current session user...'); + $this->service->scanMusicFiles(); + } + + $output->writeln('Scan completed successfully.'); + return Command::SUCCESS; + } catch (\Throwable $e) { + $output->writeln('Scan failed: ' . $e->getMessage() . ''); + return Command::FAILURE; + } + } +} diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php new file mode 100644 index 0000000..cf330fe --- /dev/null +++ b/lib/Controller/SettingsController.php @@ -0,0 +1,82 @@ +config = $config; + $this->userSession = $userSession; + } + + /** + * Save user-specific settings + * + * @param array $data + * @return DataResponse + * + * 200: Settings saved + */ + #[ApiRoute(verb: 'PUT', url: '/api/settings')] + public function saveSettings(mixed $data): DataResponse { + $user = $this->userSession->getUser(); + if ($user === null) { + return new DataResponse(['status' => 'unauthenticated'], Http::STATUS_UNAUTHORIZED); + } + + $uid = $user->getUID(); + + if (array_key_exists('music_folder_path', $data)) { + $this->config->setValueString(Application::APP_ID, 'music_folder_path_' . $uid, $data['music_folder_path']); + } + + return new DataResponse(['status' => 'OK']); + } + + /** + * Fetch all user-specific settings + * + * @return DataResponse, array{}> + * + * 200: Current settings + */ + #[ApiRoute(verb: 'GET', url: '/api/settings')] + public function getSettings(): DataResponse { + $user = $this->userSession->getUser(); + if ($user === null) { + return new DataResponse([], Http::STATUS_UNAUTHORIZED); + } + + $uid = $user->getUID(); + $result = []; + + $musicPath = $this->config->getValueString(Application::APP_ID, 'music_folder_path_' . $uid, 'Music'); + if ($musicPath !== null) { + $result['music_folder_path'] = $musicPath; + } + + return new DataResponse($result); + } +} diff --git a/lib/Sections/JukeboxUserSection.php b/lib/Sections/JukeboxUserSection.php new file mode 100644 index 0000000..acf9227 --- /dev/null +++ b/lib/Sections/JukeboxUserSection.php @@ -0,0 +1,36 @@ +l = $l; + $this->urlGenerator = $urlGenerator; + } + + public function getIcon(): string { + return $this->urlGenerator->imagePath('core', 'actions/settings-dark.svg'); + } + + public function getID(): string { + return Application::APP_ID; + } + + public function getName(): string { + return $this->l->t('Jukebox'); + } + + public function getPriority(): int { + return 50; + } +} diff --git a/lib/Service/MusicScanner.php b/lib/Service/MusicScanner.php index f09d4f1..b5ff01b 100644 --- a/lib/Service/MusicScanner.php +++ b/lib/Service/MusicScanner.php @@ -5,11 +5,12 @@ declare(strict_types=1); namespace OCA\Jukebox\Service; use getID3; +use OCA\Jukebox\AppInfo\Application; use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; -use OCP\IUser; +use OCP\IAppConfig; use OCP\IUserSession; use Psr\Log\LoggerInterface; @@ -22,37 +23,55 @@ class MusicScanner { private IRootFolder $rootFolder; private IUserSession $userSession; - /** - * @param IRootFolder $rootFolder - * @param IUserSession $userSession - * @param LoggerInterface $logger - */ public function __construct( IRootFolder $rootFolder, IUserSession $userSession, private LoggerInterface $logger, + private IAppConfig $appConfig, ) { $this->rootFolder = $rootFolder; $this->userSession = $userSession; } /** - * Starts scanning the authenticated user's folder for music files. + * Starts scanning the user's configured music directory for audio files. * * @return void */ + public function scanMusicFiles(): void { $user = $this->userSession->getUser(); if ($user === null) { - // Handle unauthenticated user + $this->logger->warning('Music scan aborted: no user session.'); return; } + $this->scanUserByUID($user->getUID()); + } + + /** + * Scans the music directory for a specific user by UID. + * + * @param string $uid + * @return void + */ + public function scanUserByUID(string $uid): void { try { - $userFolder = $this->rootFolder->getUserFolder($user->getUID()); - $this->traverseFolder($userFolder, $user); + $userFolder = $this->rootFolder->getUserFolder($uid); + + $relativePath = $this->appConfig->getValueString(Application::APP_ID, 'music_folder_path_' . $uid, 'Music'); + + /** @var Folder $musicFolder */ + $musicFolder = $userFolder->get($relativePath); + if (!($musicFolder instanceof Folder)) { + $this->logger->warning("Configured music path '$relativePath' for user $uid is not a folder."); + return; + } + + $this->logger->info("Starting music scan for user '$uid' in folder '$relativePath'"); + $this->traverseFolder($musicFolder, $uid); } catch (NotFoundException $e) { - // Handle folder not found + $this->logger->error("Could not find music folder for user $uid: " . $e->getMessage()); } } @@ -60,18 +79,21 @@ class MusicScanner { * Recursively traverses a folder and processes audio files. * * @param Folder $folder - * @param IUser $user + * @param string $uid * @return void */ - private function traverseFolder(Folder $folder, IUser $user): void { + private function traverseFolder(Folder $folder, string $uid): void { + $this->logger->info('Scanning folder: ' . $folder->getPath()); + foreach ($folder->getDirectoryListing() as $node) { if ($node instanceof File) { $mimeType = $node->getMimeType(); if (str_starts_with($mimeType, 'audio/')) { - $this->processAudioFile($node, $user); + $this->logger->info('Found audio file: ' . $node->getPath() . " (MIME: $mimeType)"); + $this->processAudioFile($node, $uid); } } elseif ($node instanceof Folder) { - $this->traverseFolder($node, $user); + $this->traverseFolder($node, $uid); } } } @@ -80,24 +102,28 @@ class MusicScanner { * Downloads an audio file, reads metadata, and processes it. * * @param File $file - * @param IUser $user + * @param string $uid * @return void */ - private function processAudioFile(File $file, IUser $user): void { + private function processAudioFile(File $file, string $uid): void { + $this->logger->info('Processing audio file: ' . $file->getPath()); + $tempPath = tempnam(sys_get_temp_dir(), 'jukebox_'); if ($tempPath === false) { $this->logger->error('Could not create temporary file for audio processing.'); - return; // Could not create temp file + return; } $handle = fopen($tempPath, 'w'); if ($handle === false) { + $this->logger->error('Failed to open temporary file handle.'); return; } $stream = $file->fopen('r'); if ($stream === false) { fclose($handle); + $this->logger->error('Failed to open file stream for ' . $file->getPath()); return; } @@ -108,17 +134,24 @@ class MusicScanner { $getID3 = new getID3(); $info = $getID3->analyze($tempPath); - $artist = $info['tags']['id3v2']['artist'][0] ?? ''; + $songArtist = $info['tags']['id3v2']['artist'][0] ?? ''; + $albumArtist = + $info['tags']['id3v2']['band'][0] ?? + $info['tags']['id3v2']['album_artist'][0] ?? + $info['tags']['quicktime']['album_artist'][0] ?? + $info['tags']['asf']['WM/AlbumArtist'][0] ?? + ''; $album = $info['tags']['id3v2']['album'][0] ?? ''; $title = $info['tags']['id3v2']['title'][0] ?? $file->getName(); - // Clean up temp file + $this->logger->info("Scanned metadata for '{$file->getPath()}': Song Artist='{$songArtist}', Album Artist='{$albumArtist}', Album='{$album}', Title='{$title}'"); + unlink($tempPath); - // Stub: Save metadata to DB - $this->saveMetadataToDatabase($user->getUID(), $file->getId(), $artist, $album, $title); + $this->saveMetadataToDatabase($uid, $file->getId(), $songArtist, $album, $title); } + /** * Placeholder method to save music metadata. * diff --git a/lib/Settings/JukeboxAdmin.php b/lib/Settings/JukeboxAdmin.php new file mode 100644 index 0000000..66f5931 --- /dev/null +++ b/lib/Settings/JukeboxAdmin.php @@ -0,0 +1,44 @@ +config = $config; + $this->l = $l; + } + + /** + * @return TemplateResponse + */ + public function getForm(): TemplateResponse { + Util::addScript(Application::APP_ID, Application::JS_DIR . '/Jukebox-main'); + Util::addStyle(Application::APP_ID, Application::CSS_DIR . '/Jukebox-style'); + return new TemplateResponse(Application::APP_ID, 'settings', [], ''); + } + + public function getSection(): string { + return Application::APP_ID; + } + + /** + * @return int whether the form should be rather on the top or bottom of + * the admin section. The forms are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + * + * E.g.: 70 + */ + public function getPriority(): int { + return 10; + } +} diff --git a/lib/Settings/JukeboxUserSettings.php b/lib/Settings/JukeboxUserSettings.php new file mode 100644 index 0000000..9a1db4b --- /dev/null +++ b/lib/Settings/JukeboxUserSettings.php @@ -0,0 +1,39 @@ +config = $config; + $this->l = $l; + } + + public function getForm(): TemplateResponse { + Util::addScript(Application::APP_ID, Application::JS_DIR . '/Jukebox-settings'); + Util::addStyle(Application::APP_ID, Application::CSS_DIR . '/Jukebox-style'); + return new TemplateResponse(Application::APP_ID, 'settings', []); + } + + public function getSection(): string { + return Application::APP_ID; + } + + public function getPriority(): int { + return 10; + } +} diff --git a/package.json b/package.json index 24f8b45..6fd2a09 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ ], "dependencies": { "@nextcloud/axios": "^2.5.1", + "@nextcloud/dialogs": "7.0.0-rc.0", "@nextcloud/l10n": "^3.3.0", "@nextcloud/router": "^3.0.1", "@nextcloud/vite-config": "^2.3.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 20b33c4..8aa2864 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@nextcloud/axios': specifier: ^2.5.1 version: 2.5.1 + '@nextcloud/dialogs': + specifier: 7.0.0-rc.0 + version: 7.0.0-rc.0(typescript@5.8.3) '@nextcloud/l10n': specifier: ^3.3.0 version: 3.3.0 @@ -165,6 +168,9 @@ packages: '@bufbuild/protobuf@2.5.2': resolution: {integrity: sha512-foZ7qr0IsUBjzWIq+SuBLfdQCpJ1j8cTuNNT4owngTHoN5KsJb8L9t65fzz7SCeSWzescoOil/0ldqiL041ABg==} + '@buttercup/fetch@0.2.1': + resolution: {integrity: sha512-sCgECOx8wiqY8NN1xN22BqqKzXYIG2AicNLlakOAI4f0WgyLVUbAigMf8CZhBtJxdudTcB1gD5lciqi44jwJvg==} + '@ckpack/vue-color@1.6.0': resolution: {integrity: sha512-b9kFTKhYbNArfgP1lmnaVm0VNsWdZjqIbyHUYry7mZ+E7JeTQclbjq1+2xWn0SE3wzqRYlXmAVjECPOgteWmMQ==} engines: {node: '>=12'} @@ -443,6 +449,9 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@mdi/js@7.4.47': + resolution: {integrity: sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==} + '@microsoft/api-extractor-model@7.30.6': resolution: {integrity: sha512-znmFn69wf/AIrwHya3fxX6uB5etSIn6vg4Q4RB/tb5VDDs1rqREc+AvMC/p19MUN13CZ7+V/8pkYPTj7q8tftg==} @@ -476,6 +485,10 @@ packages: resolution: {integrity: sha512-L1NQtOfHWzkfj0Ple1MEJt6HmOHWAi3y4qs+OnwSWexqJT0DtXTVPyRxi7ADyITwRxS5H9R/HMl6USAj4Nr1nQ==} engines: {node: ^20.0.0, npm: ^10.0.0} + '@nextcloud/dialogs@7.0.0-rc.0': + resolution: {integrity: sha512-RqoXbSBRzxPUU4g4f5c3sRWwpL6tnuSifZSW+L/xpT5GRfLbzF+tGkMgN1ghC9rmiLb0B5ovPWqABZiwrid9Rw==} + engines: {node: ^20 || ^22, npm: ^10.0.0} + '@nextcloud/eslint-config@8.4.2': resolution: {integrity: sha512-zsDcBxvp2Vr/BgasK/vNYJ84LOXjl4RseJPrcp93zcnaB2WnygV50Sd0nQ5JN0ngTyPjiIlGd92MMzrMTofjRA==} engines: {node: ^20.0.0, npm: ^10.0.0} @@ -505,6 +518,10 @@ packages: resolution: {integrity: sha512-1Qfs6i7Tz2qd1A33NpBQOt810ydHIRjhyXMFwSEkYX2yUI80lAk/sWO8HIB2Fqp+iffhyviPPcQYoytMDRyDNw==} engines: {node: ^20.0.0, npm: ^10.0.0} + '@nextcloud/files@3.10.2': + resolution: {integrity: sha512-8k6zN3nvGW8nEV5Db5DyfqcyK99RWw1iOSPIafi2RttiRQGpFzHlnF2EoM4buH5vWzI39WEvJnfuLZpkPX0cFw==} + engines: {node: ^20.0.0, npm: ^10.0.0} + '@nextcloud/initial-state@2.2.0': resolution: {integrity: sha512-cDW98L5KGGgpS8pzd+05304/p80cyu8U2xSDQGa+kGPTpUFmCbv2qnO5WrwwGTauyjYijCal2bmw82VddSH+Pg==} engines: {node: ^20.0.0, npm: ^10.0.0} @@ -517,6 +534,10 @@ packages: resolution: {integrity: sha512-wByt0R0/6QC44RBpaJr1MWghjjOxk/pRbACHo/ZWWKht1qYbJRHB4GtEi+35KEIHY07ZpqxiDk6dIRuN7sXYWQ==} engines: {node: ^20.0.0, npm: ^10.0.0} + '@nextcloud/paths@2.2.1': + resolution: {integrity: sha512-M3ShLjrxR7B48eKThLMoqbxTqTKyQXcwf9TgeXQGbCIhiHoXU6as5j8l5qNv/uZlANokVdowpuWHBi3b2+YNNA==} + engines: {node: ^20.0.0, npm: ^10.0.0} + '@nextcloud/router@3.0.1': resolution: {integrity: sha512-Ci/uD3x8OKHdxSqXL6gRJ+mGJOEXjeiHjj7hqsZqVTsT7kOrCjDf0/J8z5RyLlokKZ0IpSe+hGxgi3YB7Gpw3Q==} engines: {node: ^20.0.0, npm: ^10.0.0} @@ -851,6 +872,9 @@ packages: '@types/sizzle@2.3.9': resolution: {integrity: sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==} + '@types/toastify-js@1.12.4': + resolution: {integrity: sha512-zfZHU4tKffPCnZRe7pjv/eFKzTVHozKewFCKaCjZ4gFinKgJRz/t0bkZiMCXJxPhv/ZoeDGNOeRD09R0kQZ/nw==} + '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -1235,6 +1259,9 @@ packages: balanced-match@2.0.0: resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} + base-64@1.0.0: + resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -1317,6 +1344,9 @@ packages: builtins@5.1.0: resolution: {integrity: sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==} + byte-length@1.0.2: + resolution: {integrity: sha512-ovBpjmsgd/teRmgcPh23d4gJvxDoXtAzEL9xTfMU8Yc2kqCDb7L9jAG0XHl1nzuGl+h3ebCIF1i62UFyA9V/2Q==} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -1333,6 +1363,9 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + cancelable-promise@4.3.1: + resolution: {integrity: sha512-A/8PwLk/T7IJDfUdQ68NR24QHa8rIlnN/stiJEBo6dmVUkD4K14LswG0w3VwdeK/o7qOwRUR1k2MhK5Rpy2m7A==} + caniuse-lite@1.0.30001721: resolution: {integrity: sha512-cOuvmUVtKrtEaoKiO0rSc29jcjwMwX5tOHDy4MgVFEWiUXj4uBMJkwI8MDySkgXidpMiHUcviogAvFi4pA2hDQ==} @@ -1359,6 +1392,9 @@ packages: character-reference-invalid@2.0.1: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + charenc@0.0.2: + resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} + chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -1471,6 +1507,9 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + crypt@0.0.2: + resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} + crypto-browserify@3.12.1: resolution: {integrity: sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==} engines: {node: '>= 0.10'} @@ -1491,6 +1530,10 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -1623,6 +1666,10 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + entities@6.0.0: + resolution: {integrity: sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==} + engines: {node: '>=0.12'} + env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} @@ -1902,6 +1949,10 @@ packages: picomatch: optional: true + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -1958,6 +2009,10 @@ packages: resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} engines: {node: '>= 6'} + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + fs-extra@11.3.0: resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} engines: {node: '>=14.14'} @@ -2106,6 +2161,9 @@ packages: hmac-drbg@1.0.1: resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + hot-patcher@2.0.1: + resolution: {integrity: sha512-ECg1JFG0YzehicQaogenlcs2qg6WsXQsxtnbr1i696u5tLUjtJdQAh0u2g0Q5YV45f263Ta1GnUJsc8WIfJf4Q==} + html-tags@3.3.1: resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} engines: {node: '>=8'} @@ -2200,6 +2258,9 @@ packages: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} + is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + is-builtin-module@3.2.1: resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} engines: {node: '>=6'} @@ -2301,6 +2362,10 @@ packages: resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} engines: {node: '>= 0.4'} + is-svg@5.1.0: + resolution: {integrity: sha512-uVg5yifaTxHoefNf5Jcx+i9RZe2OBYd/UStp1umx+EERa4xGRa3LLGXjoEph43qUORC0qkafUgrXZ6zzK89yGA==} + engines: {node: '>=14.16'} + is-symbol@1.1.1: resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} engines: {node: '>= 0.4'} @@ -2399,6 +2464,9 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + layerr@3.0.0: + resolution: {integrity: sha512-tv754Ki2dXpPVApOrjTyRo4/QegVb9eVFq4mjqp4+NM5NaX7syQvN5BBNfV/ZpAHCEHV24XdUVrBAoka4jt3pA==} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -2472,6 +2540,9 @@ packages: md5.js@1.3.5: resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + md5@2.3.0: + resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} + mdast-squeeze-paragraphs@6.0.0: resolution: {integrity: sha512-6NDbJPTg0M0Ye+TlYwX1KJ1LFbp515P2immRJyJQhc9Na9cetHzSoHNYIQcXpANEAP1sm9yd/CTZU2uHqR5A+w==} @@ -2642,9 +2713,21 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + nested-property@4.0.0: + resolution: {integrity: sha512-yFehXNWRs4cM0+dz7QxCd06hTbWbSkV0ISsqBfkntU6TOY4Qm3Q88fRRLOddkGh2Qq6dZvnKVAahfhjcUvLnyA==} + node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} @@ -2754,6 +2837,9 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-posix@1.0.0: + resolution: {integrity: sha512-1gJ0WpNIiYcQydgg3Ed8KzvIqTsDpNwq+cjBCssvBtuTWjEqY1AW+i+OepiEMqDCzyro9B2sLAe4RBPajMYFiA==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -2884,6 +2970,9 @@ packages: resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} engines: {node: '>=0.4.x'} + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -2941,6 +3030,9 @@ packages: resolution: {integrity: sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==} engines: {node: '>=0.10.5'} + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -3426,6 +3518,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toastify-js@1.12.0: + resolution: {integrity: sha512-HeMHCO9yLPvP9k0apGSdPUWrUbLnxUKNFzgUoZp1PHCLploIX/4DSQ7V8H25ef+h4iO9n0he7ImfcndnN6nDrQ==} + tributejs@5.1.3: resolution: {integrity: sha512-B5CXihaVzXw+1UHhNFyAwUTMDk1EfoLP5Tj1VhD9yybZ1I8DZJEv8tZ1l0RJo0t0tk9ZhR8eG5tEsaCvRigmdQ==} @@ -3487,6 +3582,9 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + typescript-event-target@1.1.1: + resolution: {integrity: sha512-dFSOFBKV6uwaloBCCUhxlD3Pr/P1a/tJdcmPrTXCHlEFD3faj0mztjcGn6VBAhQ0/Bdy8K3VWrrqwbt/ffsYsg==} + typescript@5.8.2: resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} engines: {node: '>=14.17'} @@ -3544,6 +3642,13 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + url-join@5.0.0: + resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + url@0.11.4: resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} engines: {node: '>= 0.4'} @@ -3663,6 +3768,14 @@ packages: typescript: optional: true + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + webdav@5.8.0: + resolution: {integrity: sha512-iuFG7NamJ41Oshg4930iQgfIpRrUiatPWIekeznYgEf2EOraTRcDPTjy7gIOMtkdpKTaqPk1E68NO5PAGtJahA==} + engines: {node: '>=14'} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -3843,6 +3956,10 @@ snapshots: '@bufbuild/protobuf@2.5.2': {} + '@buttercup/fetch@0.2.1': + optionalDependencies: + node-fetch: 3.3.2 + '@ckpack/vue-color@1.6.0(vue@3.5.16(typescript@5.8.3))': dependencies: '@ctrl/tinycolor': 3.6.1 @@ -4038,6 +4155,8 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@mdi/js@7.4.47': {} + '@microsoft/api-extractor-model@7.30.6(@types/node@20.17.10)': dependencies: '@microsoft/tsdoc': 0.15.1 @@ -4096,6 +4215,33 @@ snapshots: dependencies: '@nextcloud/initial-state': 2.2.0 + '@nextcloud/dialogs@7.0.0-rc.0(typescript@5.8.3)': + dependencies: + '@mdi/js': 7.4.47 + '@nextcloud/auth': 2.5.1 + '@nextcloud/axios': 2.5.1 + '@nextcloud/event-bus': 3.3.2 + '@nextcloud/files': 3.10.2 + '@nextcloud/initial-state': 2.2.0 + '@nextcloud/l10n': 3.3.0 + '@nextcloud/paths': 2.2.1 + '@nextcloud/router': 3.0.1 + '@nextcloud/sharing': 0.2.4 + '@nextcloud/typings': 1.9.1 + '@nextcloud/vue': 9.0.0-rc.2(typescript@5.8.3) + '@types/toastify-js': 1.12.4 + '@vueuse/core': 13.3.0(vue@3.5.16(typescript@5.8.3)) + cancelable-promise: 4.3.1 + p-queue: 8.1.0 + toastify-js: 1.12.0 + vue: 3.5.16(typescript@5.8.3) + webdav: 5.8.0 + transitivePeerDependencies: + - '@nuxt/kit' + - debug + - supports-color + - typescript + '@nextcloud/eslint-config@8.4.2(@babel/core@7.26.0)(@babel/eslint-parser@7.25.9(@babel/core@7.26.0)(eslint@9.28.0))(@nextcloud/eslint-plugin@2.2.1(eslint@9.28.0))(@vue/eslint-config-typescript@13.0.0(eslint-plugin-vue@9.32.0(eslint@9.28.0))(eslint@9.28.0)(typescript@5.8.3))(eslint-config-standard@17.1.0(eslint-plugin-import@2.31.0)(eslint-plugin-n@16.6.2(eslint@9.28.0))(eslint-plugin-promise@6.6.0(eslint@9.28.0))(eslint@9.28.0))(eslint-import-resolver-exports@1.0.0-beta.5(eslint-plugin-import@2.31.0)(eslint@9.28.0))(eslint-import-resolver-typescript@3.6.3)(eslint-plugin-import@2.31.0)(eslint-plugin-jsdoc@46.10.1(eslint@9.28.0))(eslint-plugin-n@16.6.2(eslint@9.28.0))(eslint-plugin-promise@6.6.0(eslint@9.28.0))(eslint-plugin-vue@9.32.0(eslint@9.28.0))(eslint@9.28.0)(typescript@5.8.3)': dependencies: '@babel/core': 7.26.0 @@ -4125,6 +4271,20 @@ snapshots: '@types/semver': 7.7.0 semver: 7.7.2 + '@nextcloud/files@3.10.2': + dependencies: + '@nextcloud/auth': 2.5.1 + '@nextcloud/capabilities': 1.2.0 + '@nextcloud/l10n': 3.3.0 + '@nextcloud/logger': 3.0.2 + '@nextcloud/paths': 2.2.1 + '@nextcloud/router': 3.0.1 + '@nextcloud/sharing': 0.2.4 + cancelable-promise: 4.3.1 + is-svg: 5.1.0 + typescript-event-target: 1.1.1 + webdav: 5.8.0 + '@nextcloud/initial-state@2.2.0': {} '@nextcloud/l10n@3.3.0': @@ -4139,6 +4299,8 @@ snapshots: dependencies: '@nextcloud/auth': 2.5.1 + '@nextcloud/paths@2.2.1': {} + '@nextcloud/router@3.0.1': dependencies: '@nextcloud/typings': 1.9.1 @@ -4479,6 +4641,8 @@ snapshots: '@types/sizzle@2.3.9': {} + '@types/toastify-js@1.12.4': {} + '@types/trusted-types@2.0.7': optional: true @@ -4968,6 +5132,8 @@ snapshots: balanced-match@2.0.0: {} + base-64@1.0.0: {} + base64-js@1.5.1: {} blurhash@2.0.5: {} @@ -5071,6 +5237,8 @@ snapshots: dependencies: semver: 7.7.2 + byte-length@1.0.2: {} + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -5090,6 +5258,8 @@ snapshots: callsites@3.1.0: {} + cancelable-promise@4.3.1: {} + caniuse-lite@1.0.30001721: {} ccount@2.0.1: {} @@ -5109,6 +5279,8 @@ snapshots: character-reference-invalid@2.0.1: {} + charenc@0.0.2: {} + chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -5216,6 +5388,8 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crypt@0.0.2: {} + crypto-browserify@3.12.1: dependencies: browserify-cipher: 1.0.1 @@ -5242,6 +5416,8 @@ snapshots: csstype@3.1.3: {} + data-uri-to-buffer@4.0.1: {} + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 @@ -5381,6 +5557,8 @@ snapshots: entities@4.5.0: {} + entities@6.0.0: {} + env-paths@2.2.1: {} environment@1.1.0: {} @@ -5786,6 +5964,11 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -5839,6 +6022,10 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + fs-extra@11.3.0: dependencies: graceful-fs: 4.2.11 @@ -6018,6 +6205,8 @@ snapshots: minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 + hot-patcher@2.0.1: {} + html-tags@3.3.1: {} htmlparser2@8.0.2: @@ -6103,6 +6292,8 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-buffer@1.1.6: {} + is-builtin-module@3.2.1: dependencies: builtin-modules: 3.3.0 @@ -6195,6 +6386,10 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-svg@5.1.0: + dependencies: + fast-xml-parser: 4.5.3 + is-symbol@1.1.1: dependencies: call-bound: 1.0.4 @@ -6272,6 +6467,8 @@ snapshots: kolorist@1.8.0: {} + layerr@3.0.0: {} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -6363,6 +6560,12 @@ snapshots: inherits: 2.0.4 safe-buffer: 5.2.1 + md5@2.3.0: + dependencies: + charenc: 0.0.2 + crypt: 0.0.2 + is-buffer: 1.1.6 + mdast-squeeze-paragraphs@6.0.0: dependencies: '@types/mdast': 4.0.4 @@ -6663,9 +6866,19 @@ snapshots: natural-compare@1.4.0: {} + nested-property@4.0.0: {} + node-addon-api@7.1.1: optional: true + node-domexception@1.0.0: {} + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + node-releases@2.0.19: {} node-stdlib-browser@1.3.1: @@ -6820,6 +7033,8 @@ snapshots: path-parse@1.0.7: {} + path-posix@1.0.0: {} + path-type@4.0.0: {} pathe@2.0.3: {} @@ -6936,6 +7151,8 @@ snapshots: querystring-es3@0.2.1: {} + querystringify@2.2.0: {} + queue-microtask@1.2.3: {} randombytes@2.1.0: @@ -7043,6 +7260,8 @@ snapshots: requireindex@1.2.0: {} + requires-port@1.0.0: {} + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -7605,6 +7824,8 @@ snapshots: dependencies: is-number: 7.0.0 + toastify-js@1.12.0: {} + tributejs@5.1.3: {} trim-lines@3.0.1: {} @@ -7679,6 +7900,8 @@ snapshots: transitivePeerDependencies: - supports-color + typescript-event-target@1.1.1: {} + typescript@5.8.2: {} typescript@5.8.3: {} @@ -7749,6 +7972,13 @@ snapshots: dependencies: punycode: 2.3.1 + url-join@5.0.0: {} + + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + url@0.11.4: dependencies: punycode: 1.4.1 @@ -7868,6 +8098,25 @@ snapshots: optionalDependencies: typescript: 5.8.3 + web-streams-polyfill@3.3.3: {} + + webdav@5.8.0: + dependencies: + '@buttercup/fetch': 0.2.1 + base-64: 1.0.0 + byte-length: 1.0.2 + entities: 6.0.0 + fast-xml-parser: 4.5.3 + hot-patcher: 2.0.1 + layerr: 3.0.0 + md5: 2.3.0 + minimatch: 9.0.5 + nested-property: 4.0.0 + node-fetch: 3.3.2 + path-posix: 1.0.0 + url-join: 5.0.0 + url-parse: 1.5.10 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 diff --git a/scaffold.config.cjs b/scaffold.config.cjs new file mode 100644 index 0000000..0562c2e --- /dev/null +++ b/scaffold.config.cjs @@ -0,0 +1,79 @@ +/* eslint-disable @typescript-eslint/no-require-imports */ + +// eslint-disable-next-line no-undef +const { format } = require('date-fns') +// eslint-disable-next-line no-undef +const fs = require('node:fs') + +function getLatestMigration() { + const migrationDir = 'lib/migration' + const files = fs.readdirSync(migrationDir) + const migrationFiles = files.sort((a, b) => a.localeCompare(b)) + const latestMigration = migrationFiles[migrationFiles.length - 1] + const matches = /Version(\d+)/.exec(latestMigration) + const version = matches ? Number(matches[1]) + 1 : 0 + return version +} + +// eslint-disable-next-line no-undef +module.exports = () => { + return { + component: { + templates: ['gen/component'], + output: 'src/components', + subDir: false, + }, + page: { + templates: ['gen/page'], + output: 'src/pages', + subDir: false, + }, + command: { + templates: ['gen/command'], + output: 'lib/Command', + subDir: false, + }, + model: { + templates: ['gen/model'], + output: 'lib/Db', + subDir: false, + }, + 'task-queued': { + templates: ['gen/task-queued'], + output: 'lib/Cron', + subDir: false, + }, + 'task-timed': { + templates: ['gen/task-timed'], + output: 'lib/Cron', + subDir: false, + }, + service: { + templates: ['gen/service'], + output: 'lib/Service', + subDir: false, + }, + util: { + templates: ['gen/util'], + output: 'lib/Util', + subDir: false, + }, + api: { + templates: ['gen/api'], + output: 'lib/Controller', + subDir: false, + }, + migration: () => { + const latestMigrationVersion = getLatestMigration() + return { + templates: ['gen/migration'], + output: 'lib/Migration', + name: '-', + data: { + version: latestMigrationVersion, + dt: format(new Date(), 'yyyyMMddHHmmss'), + }, + } + }, + } +} diff --git a/src/Settings.vue b/src/Settings.vue index 6b131d6..97a100f 100644 --- a/src/Settings.vue +++ b/src/Settings.vue @@ -1,220 +1,71 @@ - diff --git a/src/axios.ts b/src/axios.ts new file mode 100644 index 0000000..5c3c0fd --- /dev/null +++ b/src/axios.ts @@ -0,0 +1,7 @@ +import { generateOcsUrl } from '@nextcloud/router' +import axios from '@nextcloud/axios' + +const baseURL = generateOcsUrl('/apps/jukebox/api') +export const settingsAxios = axios.create({ + baseURL, +}) diff --git a/src/settings.ts b/src/settings.ts index bcbc4ec..9811b89 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,12 +1,8 @@ +import { settingsAxios } from './axios' import Settings from './Settings.vue' import './style.scss' import { createApp } from 'vue' -import axios from '@nextcloud/axios' -import { generateOcsUrl } from '@nextcloud/router' - -const baseURL = generateOcsUrl('/apps/jukebox/api') -axios.defaults.baseURL = baseURL console.log('[DEBUG] Mounting jukebox Settings') -console.log('[DEBUG] Base URL:', baseURL) +console.log('[DEBUG] Base URL:', settingsAxios.defaults.baseURL) createApp(Settings).mount('#jukebox-settings') diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..6e8bf73 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +0.1.0 diff --git a/vite.config.ts b/vite.config.ts index b315d0f..545a442 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -15,8 +15,15 @@ export default createAppConfig( cssCodeSplit: false, rollupOptions: { output: { - manualChunks: { - vendor: ['vue', 'vue-router'], + manualChunks(id) { + if (id.includes('node_modules')) { + if (id.includes('@nextcloud/dialogs')) return 'nextcloud-dialogs' + if (id.includes('@nextcloud/vue')) return 'nextcloud-vue' + if (id.includes('vue')) return 'vue' + if (id.includes('vue-router')) return 'vue-router' + if (id.includes('axios')) return 'axios' + return 'vendor' // fallback for other deps + } }, }, },