From 1d71064c313bc121f45fec5c724170610d0b362b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pauli=20J=C3=A4rvinen?= Date: Thu, 28 Oct 2021 22:54:23 +0300 Subject: [PATCH] Add `occ` command `playlist-import` The command may be used to import one or several playlists from file(s) for one or multiple users. If the user already has a playlist with the same name as the file, then either option `overwrite` or `append` has to be used. refs https://github.com/owncloud/music/issues/832 --- appinfo/register_command.php | 7 ++ lib/BusinessLayer/PlaylistBusinessLayer.php | 7 ++ lib/Command/PlaylistImport.php | 132 ++++++++++++++++++++ lib/Utility/PlaylistFileService.php | 12 +- 4 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 lib/Command/PlaylistImport.php diff --git a/appinfo/register_command.php b/appinfo/register_command.php index b53fb7e77..bc772c008 100644 --- a/appinfo/register_command.php +++ b/appinfo/register_command.php @@ -60,3 +60,10 @@ $c->query('PlaylistBusinessLayer'), $c->query('PlaylistFileService') )); +$application->add(new OCA\Music\Command\PlaylistImport( + $c->query('UserManager'), + $c->query('GroupManager'), + $c->query('RootFolder'), + $c->query('PlaylistBusinessLayer'), + $c->query('PlaylistFileService') +)); diff --git a/lib/BusinessLayer/PlaylistBusinessLayer.php b/lib/BusinessLayer/PlaylistBusinessLayer.php index e23a3186f..662aefce3 100644 --- a/lib/BusinessLayer/PlaylistBusinessLayer.php +++ b/lib/BusinessLayer/PlaylistBusinessLayer.php @@ -68,6 +68,13 @@ public function removeTracks($trackIndices, $playlistId, $userId) { return $playlist; } + public function removeAllTracks($playlistId, $userId) { + $playlist = $this->find($playlistId, $userId); + $playlist->setTrackIdsFromArray([]); + $this->mapper->update($playlist); + return $playlist; + } + public function moveTrack($fromIndex, $toIndex, $playlistId, $userId) { $playlist = $this->find($playlistId, $userId); $trackIds = $playlist->getTrackIdsAsArray(); diff --git a/lib/Command/PlaylistImport.php b/lib/Command/PlaylistImport.php new file mode 100644 index 000000000..5b685c25f --- /dev/null +++ b/lib/Command/PlaylistImport.php @@ -0,0 +1,132 @@ + + * @copyright Pauli Järvinen 2021 + */ + +namespace OCA\Music\Command; + +use OCA\Music\AppFramework\BusinessLayer\BusinessLayerException; +use OCA\Music\BusinessLayer\PlaylistBusinessLayer; +use OCA\Music\Db\Playlist; +use OCA\Music\Utility\PlaylistFileService; + +use OCP\Files\Folder; +use OCP\Files\IRootFolder; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class PlaylistImport extends BaseCommand { + /** @var IRootFolder */ + private $rootFolder; + /** @var PlaylistBusinessLayer */ + private $businessLayer; + /** @var PlaylistFileService */ + private $playlistFileService; + + public function __construct( + \OCP\IUserManager $userManager, + \OCP\IGroupManager $groupManager, + IRootFolder $rootFolder, + PlaylistBusinessLayer $playlistBusinessLayer, + PlaylistFileService $playlistFileService) { + $this->rootFolder = $rootFolder; + $this->businessLayer = $playlistBusinessLayer; + $this->playlistFileService = $playlistFileService; + parent::__construct($userManager, $groupManager); + } + + protected function doConfigure() : void { + $this + ->setName('music:playlist-import') + ->setDescription('import user playlist(s) from file(s)') + ->addOption( + 'file', + null, + InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'path of the playlist file, relative to the user home folder' + ) + ->addOption( + 'overwrite', + null, + InputOption::VALUE_NONE, + 'overwrite the target playlist if it already exists' + ) + ->addOption( + 'append', + null, + InputOption::VALUE_NONE, + 'append imported tracks to an existing playlist if found' + ) + ; + } + + protected function doExecute(InputInterface $input, OutputInterface $output, array $users) : void { + $files = $input->getOption('file'); + $overwrite = (bool)$input->getOption('overwrite'); + $append = (bool)$input->getOption('append'); + + if (empty($files)) { + throw new \InvalidArgumentException('At least one file argument must be given'); + } + + if ($overwrite && $append) { + throw new \InvalidArgumentException('The options overwrite and append are mutually exclusive'); + } + + if ($input->getOption('all')) { + $this->userManager->callForAllUsers(function($user) use ($output, $files, $overwrite, $append) { + $this->executeForUser($user->getUID(), $files, $overwrite, $append, $output); + }); + } else { + foreach ($users as $userId) { + $this->executeForUser($userId, $files, $overwrite, $append, $output); + } + } + } + + private function executeForUser(string $userId, array $files, bool $overwrite, bool $append, OutputInterface $output) : void { + $output->writeln("Importing playlist(s) for $userId..."); + + $userFolder = $this->rootFolder->getUserFolder($userId); + + foreach ($files as $filePath) { + $name = \pathinfo($filePath, PATHINFO_FILENAME); + $existingLists = $this->businessLayer->findAllByName($name, $userId); + if (\count($existingLists) === 0) { + $playlist = $this->businessLayer->create($name, $userId); + } elseif (!$overwrite && !$append) { + $output->writeln(" The playlist $name already exists, give argument overwrite or append"); + $playlist = null; + } else { + $playlist = $existingLists[0]; + } + + if ($playlist !== null) { + $this->importPlaylist($filePath, $playlist, $userId, $userFolder, $overwrite, $output); + } + } + } + + private function importPlaylist(string $filePath, Playlist $playlist, string $userId, Folder $userFolder, bool $overwrite, OutputInterface $output) : void { + try { + $id = $playlist->getId(); + $result = $this->playlistFileService->importFromFile($playlist->getId(), $userId, $userFolder, $filePath, $overwrite ? 'overwrite' : 'append'); + $output->writeln(" {$result['imported_count']} tracks were imported to playlist $id from $filePath"); + } catch (BusinessLayerException $ex) { + $output->writeln(" User $userId has no playlist with id $id"); + } catch (\OCP\Files\NotFoundException $ex) { + $output->writeln(" Invalid file path $filePath"); + } catch (\UnexpectedValueException $ex) { + $output->writeln(" The file $filePath is not a supported playlist file"); + } + } +} diff --git a/lib/Utility/PlaylistFileService.php b/lib/Utility/PlaylistFileService.php index 2f04f382f..7c8344f79 100644 --- a/lib/Utility/PlaylistFileService.php +++ b/lib/Utility/PlaylistFileService.php @@ -66,7 +66,7 @@ public function __construct( * @throws \OCP\Files\NotPermittedException if the user is not allowed to write to the given folder */ public function exportToFile( - int $id, string $userId, Folder $userFolder, string $folderPath, string $collisionMode) : string { + int $id, string $userId, Folder $userFolder, string $folderPath, string $collisionMode='abort') : string { $playlist = $this->playlistBusinessLayer->find($id, $userId); $tracks = $this->playlistBusinessLayer->getPlaylistTracks($id, $userId); $targetFolder = Util::getFolderFromRelativePath($userFolder, $folderPath); @@ -113,7 +113,7 @@ public function exportToFile( * @throws \OCP\Files\NotPermittedException if the user is not allowed to write to the given folder */ public function exportRadioStationsToFile( - string $userId, Folder $userFolder, string $folderPath, string $filename, string $collisionMode) : string { + string $userId, Folder $userFolder, string $folderPath, string $filename, string $collisionMode='abort') : string { $targetFolder = Util::getFolderFromRelativePath($userFolder, $folderPath); $filename = self::checkFileNameConflict($targetFolder, $filename, $collisionMode); @@ -137,6 +137,9 @@ public function exportRadioStationsToFile( * @param string $userId owner of the playlist * @param Folder $userFolder user home dir * @param string $filePath path of the file to import + * @parma string $mode one of the following: + * - 'append' (dafault) Append the imported tracks after the existing tracks on the list + * - 'overwrite' Replace any previous tracks on the list with the imported tracks * @return array with three keys: * - 'playlist': The Playlist entity after the modification * - 'imported_count': An integer showing the number of tracks imported @@ -145,7 +148,7 @@ public function exportRadioStationsToFile( * @throws \OCP\Files\NotFoundException if the $filePath is not a valid file * @throws \UnexpectedValueException if the $filePath points to a file of unsupported type */ - public function importFromFile(int $id, string $userId, Folder $userFolder, string $filePath) : array { + public function importFromFile(int $id, string $userId, Folder $userFolder, string $filePath, string $mode='append') : array { $parsed = self::doParseFile(self::getFile($userFolder, $filePath), $userFolder, self::PARSE_LOCAL_FILES_ONLY); $trackFilesAndCaptions = $parsed['files']; $invalidPaths = $parsed['invalid_paths']; @@ -160,6 +163,9 @@ public function importFromFile(int $id, string $userId, Folder $userFolder, stri } } + if ($mode === 'overwrite') { + $this->playlistBusinessLayer->removeAllTracks($id, $userId); + } $playlist = $this->playlistBusinessLayer->addTracks($trackIds, $id, $userId); if (\count($invalidPaths) > 0) {