From de7910e49625f1a64528a3f499a8c40b6375b39e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pauli=20J=C3=A4rvinen?= Date: Fri, 19 Jun 2026 16:25:27 +0300 Subject: [PATCH 1/5] Replace calls deprecated in PHP 8.3 --- CHANGELOG.md | 5 +++++ lib/BackgroundJob/Cleanup.php | 4 ++-- lib/BackgroundJob/PodcastUpdateCheck.php | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c7acf876..0065df7d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [Unreleased] + +### Fixed +- Deprecation warnings printed on PHP 8.3+ while executing the Music background tasks + ## 2.5.2 - 2026-05-10 ### Changed diff --git a/lib/BackgroundJob/Cleanup.php b/lib/BackgroundJob/Cleanup.php index 74bb4dfff..bda6dbb5b 100644 --- a/lib/BackgroundJob/Cleanup.php +++ b/lib/BackgroundJob/Cleanup.php @@ -9,7 +9,7 @@ * @author Morris Jobke * @author Pauli Järvinen * @copyright Morris Jobke 2013, 2014 - * @copyright Pauli Järvinen 2017 - 2025 + * @copyright Pauli Järvinen 2017 - 2026 */ namespace OCA\Music\BackgroundJob; @@ -32,7 +32,7 @@ public function run($arguments) { $app = \OC::$server->query(Application::class); $logger = $app->get(Logger::class); - $logger->debug('Run ' . \get_class()); + $logger->debug('Run ' . static::class); // remove orphaned entities $app->get(Maintenance::class)->cleanUp(); diff --git a/lib/BackgroundJob/PodcastUpdateCheck.php b/lib/BackgroundJob/PodcastUpdateCheck.php index 77fa50845..443408a9e 100644 --- a/lib/BackgroundJob/PodcastUpdateCheck.php +++ b/lib/BackgroundJob/PodcastUpdateCheck.php @@ -7,7 +7,7 @@ * later. See the COPYING file. * * @author Pauli Järvinen - * @copyright Pauli Järvinen 2021 - 2025 + * @copyright Pauli Järvinen 2021 - 2026 */ namespace OCA\Music\BackgroundJob; @@ -30,7 +30,7 @@ public function run($arguments) { $app = \OC::$server->query(Application::class); $logger = $app->get(Logger::class); - $logger->debug('Run ' . \get_class()); + $logger->debug('Run ' . static::class); $minInterval = (float)$app->get(IConfig::class)->getSystemValue('music.podcast_auto_update_interval', 24); // hours // negative interval values can be used to disable the auto-update From d40480f9b516ffea6cb07434b7a491216b631e0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pauli=20J=C3=A4rvinen?= Date: Fri, 19 Jun 2026 18:46:54 +0300 Subject: [PATCH 2/5] Fix web UI trying to load an invalid image URL upon page load The `src` attribute of `` doesn't work quite correctly with AngularJS interpolation: the browser first tries to load the image using the uninterpolated raw string before AngularJS has the chance to interpolate the attribute. To work around this, AngularJS has introduced the `ng-src` directive. --- CHANGELOG.md | 1 + templates/partials/controls.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0065df7d2..5f523bf4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Fixed - Deprecation warnings printed on PHP 8.3+ while executing the Music background tasks +- Web UI trying to load an invalid image URL upon page load ## 2.5.2 - 2026-05-10 diff --git a/templates/partials/controls.php b/templates/partials/controls.php index 247049a75..549228c74 100644 --- a/templates/partials/controls.php +++ b/templates/partials/controls.php @@ -71,7 +71,7 @@ {{ 'Shuffle' | translate }} {{ 'Repeat' | translate }} From f5328a2ef20e3c692caa14acc5a6a80804036e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pauli=20J=C3=A4rvinen?= Date: Fri, 19 Jun 2026 22:22:42 +0300 Subject: [PATCH 3/5] Replace function calls removed in Doctrine/DBAL v3 The ownCloud v11 has updated Doctrine/DBAL to version 3.x and some previously deprecated functions have been removed. Also Nextcloud uses DBAL 3.x but it apparently provides wrappers for the DBAL classes, providing a legacy-compatible API. --- lib/AppFramework/Db/Mapper.php | 19 +++++----- lib/Db/AlbumMapper.php | 30 ++++++++-------- lib/Db/AmpacheSessionMapper.php | 6 ++-- lib/Db/ArtistMapper.php | 8 ++--- lib/Db/BaseMapper.php | 38 ++++++++++---------- lib/Db/Cache.php | 6 ++-- lib/Db/Maintenance.php | 2 +- lib/Db/PodcastChannelMapper.php | 2 +- lib/Db/TrackMapper.php | 30 ++++++++-------- stubs/Doctrine/DBAL/Driver/Statement.php | 8 ++--- tests/php/integration/db/MaintenanceTest.php | 2 +- 11 files changed, 74 insertions(+), 77 deletions(-) diff --git a/lib/AppFramework/Db/Mapper.php b/lib/AppFramework/Db/Mapper.php index e16c5422e..9faa89b9d 100644 --- a/lib/AppFramework/Db/Mapper.php +++ b/lib/AppFramework/Db/Mapper.php @@ -89,7 +89,7 @@ public function getTableName() { public function delete(Entity $entity) { $sql = 'DELETE FROM `' . $this->tableName . '` WHERE `id` = ?'; $stmt = $this->execute($sql, [$entity->getId()]); - $stmt->closeCursor(); + $stmt->free(); return $entity; } @@ -135,7 +135,7 @@ public function insert(Entity $entity) { $entity->setId((int) $this->db->lastInsertId($this->tableName)); - $stmt->closeCursor(); + $stmt->free(); return $entity; } @@ -194,7 +194,7 @@ public function update(Entity $entity) { $params[] = $id; $stmt = $this->execute($sql, $params); - $stmt->closeCursor(); + $stmt->free(); return $entity; } @@ -232,7 +232,7 @@ private function getPDOType($value) { * @param array $params the params which should replace the ? in the sql query * @param int $limit the maximum number of rows * @param int $offset from which row we want to start - * @return \Doctrine\DBAL\Driver\Statement the database query result + * @return mixed the database query result (depending on the Doctrine DBAL version, this can be either a Statement or a Result) * @since 7.0.0 */ protected function execute($sql, array $params=[], $limit=null, $offset=null) { @@ -252,9 +252,8 @@ protected function execute($sql, array $params=[], $limit=null, $offset=null) { } } - $query->execute(); - - return $query; + $result = $query->execute(); + return \is_bool($result) ? $query : $result; } /** @@ -275,7 +274,7 @@ protected function findOneQuery($sql, array $params=[], $limit=null, $offset=nul $row = $stmt->fetch(); if ($row === false || $row === null) { - $stmt->closeCursor(); + $stmt->free(); $msg = $this->buildDebugMessage( 'Did expect one result but found none when executing', $sql, @@ -286,7 +285,7 @@ protected function findOneQuery($sql, array $params=[], $limit=null, $offset=nul throw new DoesNotExistException($msg); } $row2 = $stmt->fetch(); - $stmt->closeCursor(); + $stmt->free(); //MDB2 returns null, PDO and doctrine false when no row is available if (! ($row2 === false || $row2 === null)) { $msg = $this->buildDebugMessage( @@ -353,7 +352,7 @@ protected function findEntities($sql, array $params=[], $limit=null, $offset=nul $entities[] = $this->mapRowToEntity($row); } - $stmt->closeCursor(); + $stmt->free(); return $entities; } diff --git a/lib/Db/AlbumMapper.php b/lib/Db/AlbumMapper.php index 064b64816..dff10d187 100644 --- a/lib/Db/AlbumMapper.php +++ b/lib/Db/AlbumMapper.php @@ -84,7 +84,7 @@ public function getPerformingArtistsByAlbumId(?array $albumIds, string $userId) while ($row = $result->fetch()) { $artistIds[$row['album_id']][] = (int)$row['artist_id']; } - $result->closeCursor(); + $result->free(); return $artistIds; } @@ -112,7 +112,7 @@ public function getYearsByAlbumId(?array $albumIds, string $userId) : array { while ($row = $result->fetch()) { $years[$row['album_id']][] = (int)$row['year']; } - $result->closeCursor(); + $result->free(); return $years; } @@ -146,7 +146,7 @@ public function getGenresByAlbumId(?array $albumIds, string $userId) : array { $genre->setName($row['genre_name']); $genres[$row['album_id']][] = $genre; } - $result->closeCursor(); + $result->free(); return $genres; } @@ -174,7 +174,7 @@ public function getDiscCountByAlbumId(?array $albumIds, string $userId) : array while ($row = $result->fetch()) { $diskCountByAlbum[$row['album_id']] = (int)$row['disc_count']; } - $result->closeCursor(); + $result->free(); return $diskCountByAlbum; } @@ -195,7 +195,7 @@ public function getAlbumTracksPlayCount(string $userId, ?int $limit=null, ?int $ while ($row = $result->fetch()) { $playCountByAlbum[$row['album_id']] = (int)$row['sum_count']; } - $result->closeCursor(); + $result->free(); return $playCountByAlbum; } @@ -216,7 +216,7 @@ public function getLatestAlbumPlayTimes(string $userId, ?int $limit=null, ?int $ while ($row = $result->fetch()) { $latestTimeByAlbum[$row['album_id']] = $row['latest_time']; } - $result->closeCursor(); + $result->free(); return $latestTimeByAlbum; } @@ -238,7 +238,7 @@ public function getFurthestAlbumPlayTimes(string $userId, ?int $limit=null, ?int while ($row = $result->fetch()) { $latestTimeByAlbum[$row['album_id']] = $row['latest_time']; } - $result->closeCursor(); + $result->free(); return $latestTimeByAlbum; } @@ -314,7 +314,7 @@ public function updateFolderCover(int $coverFileId, int $folderId) : bool { $params = [$folderId]; $result = $this->execute($sql, $params); $albumIds = $result->fetchAll(\PDO::FETCH_COLUMN); - $result->closeCursor(); + $result->free(); $updated = false; if (\count($albumIds) > 0) { @@ -324,7 +324,7 @@ public function updateFolderCover(int $coverFileId, int $folderId) : bool { $params = \array_merge([$coverFileId], $albumIds); $result = $this->execute($sql, $params); $updated = $result->rowCount() > 0; - $result->closeCursor(); + $result->free(); } return $updated; @@ -339,7 +339,7 @@ public function setCover(?int $coverFileId, int $albumId) : void { WHERE `id` = ?'; $params = [$coverFileId, $albumId]; $result = $this->execute($sql, $params); - $result->closeCursor(); + $result->free(); } /** @@ -367,7 +367,7 @@ public function removeCovers(array $coverFileIds, ?array $userIds=null) : array WHERE `id` IN ' . $this->questionMarks($count); $params = ArrayUtil::extractIds($albums); $result = $this->execute($sql, $params); - $result->closeCursor(); + $result->free(); } return $albums; @@ -403,7 +403,7 @@ public function getAlbumsWithoutCover(?string $userId = null, ?array $parentIds 'parentFolderId' => (int)$row['parent'] ]; } - $result->closeCursor(); + $result->free(); return $return; } @@ -420,7 +420,7 @@ public function findAlbumCover(int $albumId, int $parentFolderId) : bool { $params = [$parentFolderId]; $result = $this->execute($imagesSql, $params); $images = $result->fetchAll(); - $result->closeCursor(); + $result->free(); if (\count($images) > 0) { $getImageRank = function($imageName) { $coverNames = ['cover', 'albumart', 'album', 'front', 'folder']; @@ -478,7 +478,7 @@ public function countByArtist(int $artistId) : int { $params = [$artistId, $artistId]; $result = $this->execute($sql, $params); $row = $result->fetch(); - $result->closeCursor(); + $result->free(); return (int)$row['count']; } @@ -494,7 +494,7 @@ public function countByAlbumArtist(int $artistId) : int { $params = [$artistId]; $result = $this->execute($sql, $params); $row = $result->fetch(); - $result->closeCursor(); + $result->free(); return (int)$row['count']; } diff --git a/lib/Db/AmpacheSessionMapper.php b/lib/Db/AmpacheSessionMapper.php index 4fae52396..e9d1cbbb2 100644 --- a/lib/Db/AmpacheSessionMapper.php +++ b/lib/Db/AmpacheSessionMapper.php @@ -47,7 +47,7 @@ public function extend(string $token, int $expiry) : void { $params = [$expiry, $token]; $result = $this->execute($sql, $params); - $result->closeCursor(); + $result->free(); } public function cleanUp() : void { @@ -55,7 +55,7 @@ public function cleanUp() : void { WHERE `expiry` < ?'; $params = [\time()]; $result = $this->execute($sql, $params); - $result->closeCursor(); + $result->free(); } public function revokeSessions(int $ampacheUserId) : void { @@ -63,6 +63,6 @@ public function revokeSessions(int $ampacheUserId) : void { WHERE `ampache_user_id` = ?'; $params = [$ampacheUserId]; $result = $this->execute($sql, $params); - $result->closeCursor(); + $result->free(); } } diff --git a/lib/Db/ArtistMapper.php b/lib/Db/ArtistMapper.php index 7dd04832f..b2dd4d28c 100644 --- a/lib/Db/ArtistMapper.php +++ b/lib/Db/ArtistMapper.php @@ -117,7 +117,7 @@ public function getArtistTracksPlayCount(string $userId, ?int $limit=null, ?int while ($row = $result->fetch()) { $playCountByArtist[$row['artist_id']] = (int)$row['sum_count']; } - $result->closeCursor(); + $result->free(); return $playCountByArtist; } @@ -138,7 +138,7 @@ public function getLatestArtistPlayTimes(string $userId, ?int $limit=null, ?int while ($row = $result->fetch()) { $latestTimeByArtist[$row['artist_id']] = $row['latest_time']; } - $result->closeCursor(); + $result->free(); return $latestTimeByArtist; } @@ -160,7 +160,7 @@ public function getFurthestArtistPlayTimes(string $userId, ?int $limit=null, ?in while ($row = $result->fetch()) { $latestTimeByArtist[$row['artist_id']] = $row['latest_time']; } - $result->closeCursor(); + $result->free(); return $latestTimeByArtist; } @@ -189,7 +189,7 @@ public function removeCovers(array $coverFileIds, ?array $userIds=null) : array WHERE `id` IN ' . $this->questionMarks($count); $params = ArrayUtil::extractIds($artists); $result = $this->execute($sql, $params); - $result->closeCursor(); + $result->free(); } return $artists; diff --git a/lib/Db/BaseMapper.php b/lib/Db/BaseMapper.php index cff826b20..f5644cf75 100644 --- a/lib/Db/BaseMapper.php +++ b/lib/Db/BaseMapper.php @@ -175,7 +175,7 @@ public function findAllStarredIds(string $userId) : array { $sql = "SELECT `id` FROM `{$this->getTableName()}` WHERE `starred` IS NOT NULL AND `user_id` = ?"; $result = $this->execute($sql, [$userId]); $return = \array_map('intval', $result->fetchAll(\PDO::FETCH_COLUMN)); - $result->closeCursor(); + $result->free(); return $return; } else { return []; @@ -248,7 +248,7 @@ public function findAllIds(string $userId, ?array $ids = null) : array { $result = $this->execute($sql, $params); $return = \array_map('intval', $result->fetchAll(\PDO::FETCH_COLUMN)); - $result->closeCursor(); + $result->free(); return $return; } @@ -270,7 +270,7 @@ public function findAllIdsByParentIds(string $userId, array $parentIds) : ?array $params = \array_merge([$userId], $parentIds); $result = $this->execute($sql, $params); $rows = $result->fetchAll(); - $result->closeCursor(); + $result->free(); // ensure that the result contains also "parents" with no children and has the same order as $parentIds $return = \array_fill_keys($parentIds, []); @@ -331,7 +331,7 @@ public function findAllIdsAndNames(string $userId, ?int $parentId, ?int $limit=n $result = $this->execute($sql, $params); $rows = $result->fetchAll(); - $result->closeCursor(); + $result->free(); return $rows; } @@ -344,7 +344,7 @@ public function findAllUsers() : array { $sql = "SELECT DISTINCT(`user_id`) FROM `{$this->getTableName()}`"; $result = $this->execute($sql); $rows = $result->fetchAll(\PDO::FETCH_COLUMN); - $result->closeCursor(); + $result->free(); return $rows; } @@ -368,7 +368,7 @@ public function deleteById(array $ids) : void { protected function deleteByCond(string $condition, array $params) : void { $sql = "DELETE FROM `{$this->getTableName()}` WHERE ". $condition; $result = $this->execute($sql, $params); - $result->closeCursor(); + $result->free(); } /** @@ -377,7 +377,7 @@ protected function deleteByCond(string $condition, array $params) : void { public function deleteAll(string $userId) : void { $sql = "DELETE FROM `{$this->getTableName()}` WHERE `user_id` = ?"; $result = $this->execute($sql, [$userId]); - $result->closeCursor(); + $result->free(); } /** @@ -387,7 +387,7 @@ public function exists(int $id, string $userId) : bool { $sql = "SELECT 1 FROM `{$this->getTableName()}` WHERE `id` = ? AND `user_id` = ?"; $result = $this->execute($sql, [$id, $userId]); $row = $result->fetch(); - $result->closeCursor(); + $result->free(); return (bool)$row; } @@ -398,7 +398,7 @@ public function count(string $userId) : int { $sql = "SELECT COUNT(*) AS count FROM `{$this->getTableName()}` WHERE `user_id` = ?"; $result = $this->execute($sql, [$userId]); $row = $result->fetch(); - $result->closeCursor(); + $result->free(); return \intval($row['count']); } @@ -409,7 +409,7 @@ public function maxId(string $userId) : ?int { $sql = "SELECT MAX(`id`) AS max_id FROM `{$this->getTableName()}` WHERE `user_id` = ?"; $result = $this->execute($sql, [$userId]); $row = $result->fetch(); - $result->closeCursor(); + $result->free(); $max = $row['max_id']; return $max === null ? null : (int)$max; } @@ -526,7 +526,7 @@ public function setStarredDate(?\DateTime $date, array $ids, string $userId) : i $params = \array_merge([$date], $ids, [$userId]); $result = $this->execute($sql, $params); $modCount = $result->rowCount(); - $result->closeCursor(); + $result->free(); return $modCount; } @@ -535,7 +535,7 @@ public function latestInsertTime(string $userId) : ?\DateTime { $sql = "SELECT MAX(`{$this->getTableName()}`.`created`) FROM `{$this->getTableName()}` WHERE `user_id` = ?"; $result = $this->execute($sql, [$userId]); $createdTime = $result->fetch(\PDO::FETCH_COLUMN); - $result->closeCursor(); + $result->free(); return ($createdTime === null) ? null : new \DateTime($createdTime); } @@ -544,7 +544,7 @@ public function latestUpdateTime(string $userId) : ?\DateTime { $sql = "SELECT MAX(`{$this->getTableName()}`.`updated`) FROM `{$this->getTableName()}` WHERE `user_id` = ?"; $result = $this->execute($sql, [$userId]); $createdTime = $result->fetch(\PDO::FETCH_COLUMN); - $result->closeCursor(); + $result->free(); return ($createdTime === null) ? null : new \DateTime($createdTime); } @@ -838,7 +838,7 @@ private function funcExistsInSqlite(string $funcName) : bool { try { $result = $this->execute('SELECT EXISTS(SELECT 1 FROM `pragma_function_list` WHERE `NAME` = ?)', [$funcName]); $row = $result->fetch(); - $result->closeCursor(); + $result->free(); return (bool)\current($row); } catch (\Exception $e) { return false; @@ -860,9 +860,8 @@ private function findIdOfConflict(Entity $entity) : int { $sql = "SELECT `id` FROM {$this->getTableName()} WHERE " . \implode(' AND ', $conds); $result = $this->execute($sql, $values); - /** @var string|false $id */ // phpdoc for \Doctrine\DBAL\Driver\Statement::fetchColumn is erroneous and omits the `false` - $id = $result->fetchColumn(); - $result->closeCursor(); + $id = $result->fetchOne(); + $result->free(); if ($id === false) { throw new DoesNotExistException('Conflicting entity not found'); @@ -874,9 +873,8 @@ private function findIdOfConflict(Entity $entity) : int { private function getCreated(int $id) : string { $sql = "SELECT `created` FROM {$this->getTableName()} WHERE `id` = ?"; $result = $this->execute($sql, [$id]); - /** @var string|false $created */ // phpdoc for \Doctrine\DBAL\Driver\Statement::fetchColumn is erroneous and omits the `false` - $created = $result->fetchColumn(); - $result->closeCursor(); + $created = $result->fetchOne(); + $result->free(); if ($created === false) { throw new DoesNotExistException('ID not found'); } diff --git a/lib/Db/Cache.php b/lib/Db/Cache.php index 920642871..b0b19a93b 100644 --- a/lib/Db/Cache.php +++ b/lib/Db/Cache.php @@ -92,7 +92,7 @@ public function get(string $userId, string $key) : ?string { WHERE `user_id` = ? AND `key` = ?'; $result = $this->db->executeQuery($sql, [$userId, $key]); $rows = $result->fetchAll(); - $result->closeCursor(); + $result->free(); return \count($rows) ? $rows[0]['data'] : null; } @@ -115,7 +115,7 @@ public function getAll(string $userId, ?string $prefix = null) : array { $result = $this->db->executeQuery($sql, $params); $rows = $result->fetchAll(); - $result->closeCursor(); + $result->free(); return $rows; } @@ -128,7 +128,7 @@ public function getOwner(string $key, string $data) : ?string { WHERE `key` = ? AND `data` = ?'; $result = $this->db->executeQuery($sql, [$key, $data]); $rows = $result->fetchAll(); - $result->closeCursor(); + $result->free(); return \count($rows) ? $rows[0]['user_id'] : null; } diff --git a/lib/Db/Maintenance.php b/lib/Db/Maintenance.php index b2c2fab55..070640e47 100644 --- a/lib/Db/Maintenance.php +++ b/lib/Db/Maintenance.php @@ -37,7 +37,7 @@ private function removeStrayScanningStatus() : int { WHERE `key` = \'scanning\''; $result = $this->db->executeQuery($sql); $rows = $result->fetchAll(); - $result->closeCursor(); + $result->free(); $now = \time(); $modRows = 0; diff --git a/lib/Db/PodcastChannelMapper.php b/lib/Db/PodcastChannelMapper.php index 0c703bc7f..bfd9e0714 100644 --- a/lib/Db/PodcastChannelMapper.php +++ b/lib/Db/PodcastChannelMapper.php @@ -32,7 +32,7 @@ public function findAllIdsWithNoUpdateSince(string $userId, \DateTime $timeLimit $sql = "SELECT `id` FROM `{$this->getTableName()}` WHERE `user_id` = ? AND `update_checked` < ?"; $result = $this->execute($sql, [$userId, $timeLimit->format(BaseMapper::SQL_DATE_FORMAT)]); $rows = $result->fetchAll(\PDO::FETCH_COLUMN); - $result->closeCursor(); + $result->free(); return \array_map('intval', $rows); } diff --git a/lib/Db/TrackMapper.php b/lib/Db/TrackMapper.php index 86c1666b0..3b87dd13c 100644 --- a/lib/Db/TrackMapper.php +++ b/lib/Db/TrackMapper.php @@ -144,7 +144,7 @@ public function findAllFileIds(string $userId, ?array $parentIds=null) : array { } $result = $this->execute($sql, $params); $rows = $result->fetchAll(\PDO::FETCH_COLUMN); - $result->closeCursor(); + $result->free(); return $rows; } @@ -171,7 +171,7 @@ public function findDirtyFileIds(string $userId, ?array $parentIds=null) : array $result = $this->execute($sql, $params); $rows = $result->fetchAll(\PDO::FETCH_COLUMN); - $result->closeCursor(); + $result->free(); return $rows; } @@ -215,7 +215,7 @@ public function countByArtist(int $artistId) : int { $sql = 'SELECT COUNT(*) AS `count` FROM `*PREFIX*music_tracks` WHERE `artist_id` = ?'; $result = $this->execute($sql, [$artistId]); $row = $result->fetch(); - $result->closeCursor(); + $result->free(); return (int)$row['count']; } @@ -223,7 +223,7 @@ public function countByAlbum(int $albumId) : int { $sql = 'SELECT COUNT(*) AS `count` FROM `*PREFIX*music_tracks` WHERE `album_id` = ?'; $result = $this->execute($sql, [$albumId]); $row = $result->fetch(); - $result->closeCursor(); + $result->free(); return (int)$row['count']; } @@ -234,7 +234,7 @@ public function totalDurationOfAlbum(int $albumId) : int { $sql = 'SELECT SUM(`length`) AS `duration` FROM `*PREFIX*music_tracks` WHERE `album_id` = ?'; $result = $this->execute($sql, [$albumId]); $row = $result->fetch(); - $result->closeCursor(); + $result->free(); return (int)$row['duration']; } @@ -245,7 +245,7 @@ public function totalDurationByArtist(int $artistId) : int { $sql = 'SELECT SUM(`length`) AS `duration` FROM `*PREFIX*music_tracks` WHERE `artist_id` = ?'; $result = $this->execute($sql, [$artistId]); $row = $result->fetch(); - $result->closeCursor(); + $result->free(); return (int)$row['duration']; } @@ -262,7 +262,7 @@ public function getDurations(array $trackIds) : array { $this->questionMarks(\count($trackIds)); $result = $this->execute($sql, $trackIds); $rows = $result->fetchAll(); - $result->closeCursor(); + $result->free(); foreach ($rows as $row) { $return[$row['id']] = (int)$row['length']; @@ -419,7 +419,7 @@ public function findTrackAndFolderIds(string $userId) : array { $result = $this->execute($sql, [$userId]); $rows = $result->fetchAll(); - $result->closeCursor(); + $result->free(); // Sort the results according the file names. This can't be made using ORDERBY in the // SQL query because then we couldn't use the "natural order" comparison algorithm @@ -449,7 +449,7 @@ public function findNodeNamesAndParents(array $nodeIds) : array { $result = $this->execute($sql, $nodeIds); $rows = $result->fetchAll(); - $result->closeCursor(); + $result->free(); foreach ($rows as $row) { $return[$row['fileid']] = [ @@ -480,7 +480,7 @@ public function findSubFolderIds(array $folderIds) : array { $result = $this->execute($sql, $folderIds); $rows = $result->fetchAll(\PDO::FETCH_COLUMN); - $result->closeCursor(); + $result->free(); } return $rows; @@ -495,7 +495,7 @@ public function getGenresByArtistId(int $artistId, string $userId) : array { `genre_id` IS NOT NULL AND `user_id` = ? AND `artist_id` = ?'; $result = $this->execute($sql, [$userId, $artistId]); $rows = $result->fetchAll(\PDO::FETCH_COLUMN); - $result->closeCursor(); + $result->free(); return $rows; } @@ -508,7 +508,7 @@ public function mapGenreIdsToTrackIds(string $userId) : array { WHERE `genre_id` IS NOT NULL and `user_id` = ?'; $result = $this->execute($sql, [$userId]); $rows = $result->fetchAll(); - $result->closeCursor(); + $result->free(); $return = []; foreach ($rows as $row) { @@ -531,7 +531,7 @@ public function findFilesWithoutScannedGenre(string $userId) : array { WHERE `genre_id` IS NULL and `user_id` = ?'; $result = $this->execute($sql, [$userId]); $rows = $result->fetchAll(\PDO::FETCH_COLUMN); - $result->closeCursor(); + $result->free(); return $rows; } @@ -548,7 +548,7 @@ public function recordTrackPlayed(int $trackId, string $userId, \DateTime $timeO $result = $this->execute($sql, $params); $updated = ($result->rowCount() > 0); - $result->closeCursor(); + $result->free(); return $updated; } @@ -573,7 +573,7 @@ public function markTracksDirty(array $fileIds, ?array $userIds=null) : int { $result = $this->execute($sql, $params); $updated = $result->rowCount(); - $result->closeCursor(); + $result->free(); return $updated; } diff --git a/stubs/Doctrine/DBAL/Driver/Statement.php b/stubs/Doctrine/DBAL/Driver/Statement.php index 6cb8b6402..ece7e3309 100644 --- a/stubs/Doctrine/DBAL/Driver/Statement.php +++ b/stubs/Doctrine/DBAL/Driver/Statement.php @@ -82,7 +82,7 @@ function bindParam($column, &$variable, $type = null); * * @return boolean Returns TRUE on success or FALSE on failure. */ - function closeCursor(); + function free(); /** * columnCount @@ -172,17 +172,17 @@ function fetch($fetchStyle = PDO::FETCH_BOTH); function fetchAll($fetchStyle = PDO::FETCH_BOTH); /** - * fetchColumn + * fetchOne * Returns a single column from the next row of a * result set or FALSE if there are no more rows. * * @param integer $columnIndex 0-indexed number of the column you wish to retrieve from the row. If no - * value is supplied, PDOStatement->fetchColumn() + * value is supplied, PDOStatement->fetchOne() * fetches the first column. * * @return string returns a single column in the next row of a result set. */ - function fetchColumn($columnIndex = 0); + function fetchOne($columnIndex = 0); /** * rowCount diff --git a/tests/php/integration/db/MaintenanceTest.php b/tests/php/integration/db/MaintenanceTest.php index 3b16d22d4..70a5500a6 100644 --- a/tests/php/integration/db/MaintenanceTest.php +++ b/tests/php/integration/db/MaintenanceTest.php @@ -68,7 +68,7 @@ protected function checkForEmptyTables($user) { ->setParameter('user_id', $user); $stmt = $qb->execute(); $row = $stmt->fetch(); - $stmt->closeCursor(); + $stmt->free(); $count = $row['count']; $this->assertEquals(0, $count); From a2a45e8ab0d7cd6b35b4975394e4cac9eeda7a55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pauli=20J=C3=A4rvinen?= Date: Fri, 19 Jun 2026 23:03:01 +0300 Subject: [PATCH 4/5] Update occ commands to work with symfony/console 7.4 The ownCloud 11 updates the symfony/console version used from the previous 5.4 to 7.4. With 7.4, the `execute` function of each command must declare the return type as `int`. Apparently Nextcloud is still using some older version of symfony/console but the exact version used was not easy to find out. --- lib/Command/BaseCommand.php | 12 +++--------- lib/Command/Cleanup.php | 12 +++--------- lib/Command/RegisterMimeTypes.php | 4 ++-- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/lib/Command/BaseCommand.php b/lib/Command/BaseCommand.php index 51afdfb24..132fffbef 100644 --- a/lib/Command/BaseCommand.php +++ b/lib/Command/BaseCommand.php @@ -7,7 +7,7 @@ * later. See the COPYING file. * * @author Pauli Järvinen - * @copyright Pauli Järvinen 2018 - 2025 + * @copyright Pauli Järvinen 2018 - 2026 */ namespace OCA\Music\Command; @@ -28,10 +28,7 @@ public function __construct(\OCP\IUserManager $userManager, \OCP\IGroupManager $ parent::__construct(); } - /** - * @return void - */ - protected function configure() { + protected function configure() : void { $this ->addArgument( 'user_id', @@ -54,10 +51,7 @@ protected function configure() { $this->doConfigure(); } - /** - * @return int - */ - protected function execute(InputInterface $input, OutputInterface $output) { + protected function execute(InputInterface $input, OutputInterface $output) : int { try { self::ensureUsersGiven($input); $argUsers = $this->getArgumentUsers($input); diff --git a/lib/Command/Cleanup.php b/lib/Command/Cleanup.php index e2fabd2b2..b0d989b37 100644 --- a/lib/Command/Cleanup.php +++ b/lib/Command/Cleanup.php @@ -7,7 +7,7 @@ * later. See the COPYING file. * * @author Pauli Järvinen - * @copyright Pauli Järvinen 2017 - 2025 + * @copyright Pauli Järvinen 2017 - 2026 */ namespace OCA\Music\Command; @@ -26,20 +26,14 @@ public function __construct(Maintenance $maintenance) { parent::__construct(); } - /** - * @return void - */ - protected function configure() { + protected function configure() : void { $this ->setName('music:cleanup') ->setDescription('clean up orphaned DB entries (this happens also periodically on the background)') ; } - /** - * @return int - */ - protected function execute(InputInterface $input, OutputInterface $output) { + protected function execute(InputInterface $input, OutputInterface $output) : int { $output->writeln('Running cleanup task...'); $removedEntries = $this->maintenance->cleanUp(); $output->writeln("Removed entries: " . \json_encode($removedEntries)); diff --git a/lib/Command/RegisterMimeTypes.php b/lib/Command/RegisterMimeTypes.php index d67496c3b..40a347ba3 100644 --- a/lib/Command/RegisterMimeTypes.php +++ b/lib/Command/RegisterMimeTypes.php @@ -39,7 +39,7 @@ public function __construct(IMimeTypeLoader $mimeTypeLoader) { /** * @return void */ - protected function configure() { + protected function configure() : void { $this ->setName('music:register-mime-types') ->setDescription('map following file extensions to proper MIME types: ' . \json_encode(\array_keys($this->mimeMappings))); @@ -49,7 +49,7 @@ protected function configure() { /** * @return int */ - protected function execute(InputInterface $input, OutputInterface $output) { + protected function execute(InputInterface $input, OutputInterface $output) : int { try { $output->writeln('Registering MIME types for existing files...'); $this->registerForExistingFiles($output); From 7fe076d23df1affd538afb73c791b703af889305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pauli=20J=C3=A4rvinen?= Date: Fri, 19 Jun 2026 23:04:02 +0300 Subject: [PATCH 5/5] Declare compatibility for ownCloud 11 After the two previous commits, the Music app now seems to work fine on ownCloud 11.0.0-prealpha. --- CHANGELOG.md | 3 +++ appinfo/info.xml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f523bf4e..f5d991bff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## [Unreleased] +### Added +- Support for ownCloud 11 (tested on 11.0.0-prealpha) + ### Fixed - Deprecation warnings printed on PHP 8.3+ while executing the Music background tasks - Web UI trying to load an invalid image URL upon page load diff --git a/appinfo/info.xml b/appinfo/info.xml index c93718ce6..8de28a5ce 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -34,7 +34,7 @@ https://github.com/owncloud/music.git - +