1
|
|
|
<?php declare(strict_types=1); |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* ownCloud - Music app |
5
|
|
|
* |
6
|
|
|
* This file is licensed under the Affero General Public License version 3 or |
7
|
|
|
* later. See the COPYING file. |
8
|
|
|
* |
9
|
|
|
* @author Morris Jobke <[email protected]> |
10
|
|
|
* @author Pauli Järvinen <[email protected]> |
11
|
|
|
* @copyright Morris Jobke 2013, 2014 |
12
|
|
|
* @copyright Pauli Järvinen 2016 - 2023 |
13
|
|
|
*/ |
14
|
|
|
|
15
|
|
|
namespace OCA\Music\Db; |
16
|
|
|
|
17
|
|
|
use OCP\IDBConnection; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @phpstan-extends BaseMapper<Track> |
21
|
|
|
*/ |
22
|
|
|
class TrackMapper extends BaseMapper { |
23
|
|
|
public function __construct(IDBConnection $db) { |
24
|
|
|
parent::__construct($db, 'music_tracks', Track::class, 'title'); |
25
|
|
|
} |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Override the base implementation to include data from multiple tables |
29
|
|
|
* |
30
|
|
|
* {@inheritdoc} |
31
|
|
|
* @see BaseMapper::selectEntities() |
32
|
|
|
*/ |
33
|
|
|
protected function selectEntities(string $condition, string $extension=null) : string { |
34
|
|
|
return "SELECT `*PREFIX*music_tracks`.*, `file`.`name` AS `filename`, `file`.`size`, `file`.`mtime` AS `file_mod_time`, |
35
|
|
|
`album`.`name` AS `album_name`, `artist`.`name` AS `artist_name`, `genre`.`name` AS `genre_name` |
36
|
|
|
FROM `*PREFIX*music_tracks` |
37
|
|
|
INNER JOIN `*PREFIX*filecache` `file` |
38
|
|
|
ON `*PREFIX*music_tracks`.`file_id` = `file`.`fileid` |
39
|
|
|
INNER JOIN `*PREFIX*music_albums` `album` |
40
|
|
|
ON `*PREFIX*music_tracks`.`album_id` = `album`.`id` |
41
|
|
|
INNER JOIN `*PREFIX*music_artists` `artist` |
42
|
|
|
ON `*PREFIX*music_tracks`.`artist_id` = `artist`.`id` |
43
|
|
|
LEFT JOIN `*PREFIX*music_genres` `genre` |
44
|
|
|
ON `*PREFIX*music_tracks`.`genre_id` = `genre`.`id` |
45
|
|
|
WHERE $condition $extension"; |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Overridden from the base implementation to add support for sorting by artist, play_count, and last_played. |
50
|
|
|
* |
51
|
|
|
* {@inheritdoc} |
52
|
|
|
* @see BaseMapper::formatSortingClause() |
53
|
|
|
*/ |
54
|
|
|
protected function formatSortingClause(int $sortBy, bool $invertSort = false) : ?string { |
55
|
|
|
$dir = $invertSort ? 'DESC' : 'ASC'; |
56
|
|
|
switch ($sortBy) { |
57
|
|
|
case SortBy::Parent: |
58
|
|
|
// Note: the alternative form "LOWER(`artist_name`) wouldn't work on PostgreSQL, see https://github.com/owncloud/music/issues/1046 for a similar case |
59
|
|
|
return "ORDER BY LOWER(`artist`.`name`) $dir, LOWER(`title`) $dir"; |
60
|
|
|
case SortBy::PlayCount: |
61
|
|
|
return "ORDER BY LOWER(`play_count`) $dir"; |
62
|
|
|
case SortBy::LastPlayed: |
63
|
|
|
return "ORDER BY LOWER(`last_played`) $dir"; |
64
|
|
|
default: |
65
|
|
|
return parent::formatSortingClause($sortBy, $invertSort); |
66
|
|
|
} |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Returns all tracks of the given artist (both album and track artists are considered) |
71
|
|
|
* @return Track[] |
72
|
|
|
*/ |
73
|
|
|
public function findAllByArtist(int $artistId, string $userId, ?int $limit=null, ?int $offset=null) : array { |
74
|
|
|
$sql = $this->selectUserEntities( |
75
|
|
|
'`artist_id` = ? OR `album_id` IN (SELECT `id` from `*PREFIX*music_albums` WHERE `album_artist_id` = ?) ', |
76
|
|
|
'ORDER BY LOWER(`title`)'); |
77
|
|
|
$params = [$userId, $artistId, $artistId]; |
78
|
|
|
return $this->findEntities($sql, $params, $limit, $offset); |
|
|
|
|
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @return Track[] |
83
|
|
|
*/ |
84
|
|
|
public function findAllByAlbum(int $albumId, string $userId, ?int $artistId=null, ?int $limit=null, ?int $offset=null) : array { |
85
|
|
|
$condition = '`album_id` = ?'; |
86
|
|
|
$params = [$userId, $albumId]; |
87
|
|
|
|
88
|
|
|
if ($artistId !== null) { |
89
|
|
|
$condition .= ' AND `artist_id` = ? '; |
90
|
|
|
$params[] = $artistId; |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
$sql = $this->selectUserEntities($condition, |
94
|
|
|
'ORDER BY `*PREFIX*music_tracks`.`disk`, `number`, LOWER(`title`)'); |
95
|
|
|
return $this->findEntities($sql, $params, $limit, $offset); |
|
|
|
|
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @return Track[] |
100
|
|
|
*/ |
101
|
|
|
public function findAllByFolder(int $folderId, string $userId, ?int $limit=null, ?int $offset=null) : array { |
102
|
|
|
$sql = $this->selectUserEntities('`file`.`parent` = ?', 'ORDER BY LOWER(`title`)'); |
103
|
|
|
$params = [$userId, $folderId]; |
104
|
|
|
return $this->findEntities($sql, $params, $limit, $offset); |
|
|
|
|
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* @return Track[] |
109
|
|
|
*/ |
110
|
|
|
public function findAllByGenre(int $genreId, string $userId, ?int $limit=null, ?int $offset=null) : array { |
111
|
|
|
$sql = $this->selectUserEntities('`genre_id` = ?', 'ORDER BY LOWER(`title`)'); |
112
|
|
|
$params = [$userId, $genreId]; |
113
|
|
|
return $this->findEntities($sql, $params, $limit, $offset); |
|
|
|
|
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* @param string $userId |
118
|
|
|
* @return int[] |
119
|
|
|
*/ |
120
|
|
|
public function findAllFileIds(string $userId) : array { |
121
|
|
|
$sql = 'SELECT `file_id` FROM `*PREFIX*music_tracks` WHERE `user_id` = ?'; |
122
|
|
|
$result = $this->execute($sql, [$userId]); |
|
|
|
|
123
|
|
|
|
124
|
|
|
return \array_map(function ($i) { |
125
|
|
|
return (int)$i['file_id']; |
126
|
|
|
}, $result->fetchAll()); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Find a track of user matching a file ID |
131
|
|
|
* @throws \OCP\AppFramework\Db\DoesNotExistException if not found |
132
|
|
|
*/ |
133
|
|
|
public function findByFileId(int $fileId, string $userId) : Track { |
134
|
|
|
$sql = $this->selectUserEntities('`file_id` = ?'); |
135
|
|
|
$params = [$userId, $fileId]; |
136
|
|
|
return $this->findEntity($sql, $params); |
|
|
|
|
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* Find tracks of user with multiple file IDs |
141
|
|
|
* @param integer[] $fileIds |
142
|
|
|
* @param string[] $userIds |
143
|
|
|
* @return Track[] |
144
|
|
|
*/ |
145
|
|
|
public function findByFileIds(array $fileIds, array $userIds) : array { |
146
|
|
|
$sql = $this->selectEntities( |
147
|
|
|
'`*PREFIX*music_tracks`.`user_id` IN ' . $this->questionMarks(\count($userIds)) . |
148
|
|
|
' AND `file_id` IN '. $this->questionMarks(\count($fileIds))); |
149
|
|
|
$params = \array_merge($userIds, $fileIds); |
150
|
|
|
return $this->findEntities($sql, $params); |
|
|
|
|
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* Finds tracks of all users matching one or multiple file IDs |
155
|
|
|
* @param integer[] $fileIds |
156
|
|
|
* @return Track[] |
157
|
|
|
*/ |
158
|
|
|
public function findAllByFileIds(array $fileIds) : array { |
159
|
|
|
$sql = $this->selectEntities('`file_id` IN '. |
160
|
|
|
$this->questionMarks(\count($fileIds))); |
161
|
|
|
return $this->findEntities($sql, $fileIds); |
|
|
|
|
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
public function countByArtist(int $artistId) : int { |
165
|
|
|
$sql = 'SELECT COUNT(*) AS `count` FROM `*PREFIX*music_tracks` WHERE `artist_id` = ?'; |
166
|
|
|
$result = $this->execute($sql, [$artistId]); |
|
|
|
|
167
|
|
|
$row = $result->fetch(); |
168
|
|
|
return (int)$row['count']; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
public function countByAlbum(int $albumId) : int { |
172
|
|
|
$sql = 'SELECT COUNT(*) AS `count` FROM `*PREFIX*music_tracks` WHERE `album_id` = ?'; |
173
|
|
|
$result = $this->execute($sql, [$albumId]); |
|
|
|
|
174
|
|
|
$row = $result->fetch(); |
175
|
|
|
return (int)$row['count']; |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* @return integer Duration in seconds |
180
|
|
|
*/ |
181
|
|
|
public function totalDurationOfAlbum(int $albumId) : int { |
182
|
|
|
$sql = 'SELECT SUM(`length`) AS `duration` FROM `*PREFIX*music_tracks` WHERE `album_id` = ?'; |
183
|
|
|
$result = $this->execute($sql, [$albumId]); |
|
|
|
|
184
|
|
|
$row = $result->fetch(); |
185
|
|
|
return (int)$row['duration']; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Get durations of the given tracks. |
190
|
|
|
* @param integer[] $trackIds |
191
|
|
|
* @return array {int => int} where keys are track IDs and values are corresponding durations |
192
|
|
|
*/ |
193
|
|
|
public function getDurations(array $trackIds) : array { |
194
|
|
|
$result = []; |
195
|
|
|
|
196
|
|
|
if (!empty($trackIds)) { |
197
|
|
|
$sql = 'SELECT `id`, `length` FROM `*PREFIX*music_tracks` WHERE `id` IN ' . |
198
|
|
|
$this->questionMarks(\count($trackIds)); |
199
|
|
|
$rows = $this->execute($sql, $trackIds)->fetchAll(); |
|
|
|
|
200
|
|
|
foreach ($rows as $row) { |
201
|
|
|
$result[$row['id']] = (int)$row['length']; |
202
|
|
|
} |
203
|
|
|
} |
204
|
|
|
return $result; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* @return Track[] |
209
|
|
|
*/ |
210
|
|
|
public function findAllByNameRecursive(string $name, string $userId, ?int $limit=null, ?int $offset=null) { |
211
|
|
|
$condition = '( LOWER(`artist`.`name`) LIKE LOWER(?) OR |
212
|
|
|
LOWER(`album`.`name`) LIKE LOWER(?) OR |
213
|
|
|
LOWER(`title`) LIKE LOWER(?) )'; |
214
|
|
|
$sql = $this->selectUserEntities($condition, 'ORDER BY LOWER(`title`)'); |
215
|
|
|
$name = BaseMapper::prepareSubstringSearchPattern($name); |
216
|
|
|
$params = [$userId, $name, $name, $name]; |
217
|
|
|
return $this->findEntities($sql, $params, $limit, $offset); |
|
|
|
|
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* Returns all tracks specified by name and/or artist name |
222
|
|
|
* @param string|null $name the name of the track |
223
|
|
|
* @param string|null $artistName the name of the artist |
224
|
|
|
* @param string $userId the name of the user |
225
|
|
|
* @return Track[] Tracks matching the criteria |
226
|
|
|
*/ |
227
|
|
|
public function findAllByNameAndArtistName(?string $name, ?string $artistName, string $userId) : array { |
228
|
|
|
$sqlConditions = []; |
229
|
|
|
$params = [$userId]; |
230
|
|
|
|
231
|
|
|
if (!empty($name)) { |
232
|
|
|
$sqlConditions[] = '`title` = ?'; |
233
|
|
|
$params[] = $name; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
if (!empty($artistName)) { |
237
|
|
|
$sqlConditions[] = '`artist`.`name` = ?'; |
238
|
|
|
$params[] = $artistName; |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
// at least one condition has to be given, otherwise return an empty set |
242
|
|
|
if (\count($sqlConditions) > 0) { |
243
|
|
|
$sql = $this->selectUserEntities(\implode(' AND ', $sqlConditions)); |
244
|
|
|
return $this->findEntities($sql, $params); |
|
|
|
|
245
|
|
|
} else { |
246
|
|
|
return []; |
247
|
|
|
} |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
/** |
251
|
|
|
* Returns all tracks specified by various criteria, all of which are optional |
252
|
|
|
* @param int[]|null $genres Array of genre IDs |
253
|
|
|
* @param int|null $fromYear Earliest release year to include |
254
|
|
|
* @param int|null $toYear Latest release year to include |
255
|
|
|
* @param int $sortBy Sorting rule as defined in the class SortBy |
256
|
|
|
* @param string $userId the name of the user |
257
|
|
|
* @return Track[] Tracks matching the criteria |
258
|
|
|
*/ |
259
|
|
|
public function findAllByCriteria(array $genres, ?int $fromYear, ?int $toYear, int $sortBy, bool $invertSort, string $userId, ?int $limit=null, ?int $offset=null) : array { |
260
|
|
|
$sqlConditions = []; |
261
|
|
|
$params = [$userId]; |
262
|
|
|
|
263
|
|
|
if (!empty($genres)) { |
264
|
|
|
$sqlConditions[] = '`genre_id` IN ' . $this->questionMarks(\count($genres)); |
265
|
|
|
$params = \array_merge($params, $genres); |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
if (!empty($fromYear)) { |
269
|
|
|
$sqlConditions[] = '`year` >= ?'; |
270
|
|
|
$params[] = $fromYear; |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
if (!empty($toYear)) { |
274
|
|
|
$sqlConditions[] = '`year` <= ?'; |
275
|
|
|
$params[] = $toYear; |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
$sql = $this->selectUserEntities(\implode(' AND ', $sqlConditions), self::formatSortingClause($sortBy, $invertSort)); |
|
|
|
|
279
|
|
|
return $this->findEntities($sql, $params, $limit, $offset); |
|
|
|
|
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* Find most frequently played tracks |
284
|
|
|
* @return Track[] |
285
|
|
|
*/ |
286
|
|
|
public function findFrequentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array { |
287
|
|
|
$sql = $this->selectUserEntities('`play_count` > 0', 'ORDER BY `play_count` DESC, LOWER(`title`)'); |
288
|
|
|
return $this->findEntities($sql, [$userId], $limit, $offset); |
|
|
|
|
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* Find most recently played tracks |
293
|
|
|
* @return Track[] |
294
|
|
|
*/ |
295
|
|
|
public function findRecentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array { |
296
|
|
|
$sql = $this->selectUserEntities('`last_played` IS NOT NULL', 'ORDER BY `last_played` DESC'); |
297
|
|
|
return $this->findEntities($sql, [$userId], $limit, $offset); |
|
|
|
|
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
/** |
301
|
|
|
* Find least recently played tracks |
302
|
|
|
* @return Track[] |
303
|
|
|
*/ |
304
|
|
|
public function findNotRecentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array { |
305
|
|
|
$sql = $this->selectUserEntities(null, 'ORDER BY `last_played` ASC'); |
306
|
|
|
return $this->findEntities($sql, [$userId], $limit, $offset); |
|
|
|
|
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
/** |
310
|
|
|
* Finds all track IDs of the user along with the parent folder ID of each track |
311
|
|
|
* @return array where keys are folder IDs and values are arrays of track IDs |
312
|
|
|
*/ |
313
|
|
|
public function findTrackAndFolderIds(string $userId) : array { |
314
|
|
|
$sql = 'SELECT `track`.`id` AS id, `file`.`name` AS `filename`, `file`.`parent` AS parent |
315
|
|
|
FROM `*PREFIX*music_tracks` `track` |
316
|
|
|
JOIN `*PREFIX*filecache` `file` |
317
|
|
|
ON `track`.`file_id` = `file`.`fileid` |
318
|
|
|
WHERE `track`.`user_id` = ?'; |
319
|
|
|
|
320
|
|
|
$rows = $this->execute($sql, [$userId])->fetchAll(); |
|
|
|
|
321
|
|
|
|
322
|
|
|
// Sort the results according the file names. This can't be made using ORDERBY in the |
323
|
|
|
// SQL query because then we couldn't use the "natural order" comparison algorithm |
324
|
|
|
\usort($rows, function ($a, $b) { |
325
|
|
|
return \strnatcasecmp($a['filename'], $b['filename']); |
326
|
|
|
}); |
327
|
|
|
|
328
|
|
|
// group the files to parent folder "buckets" |
329
|
|
|
$result = []; |
330
|
|
|
foreach ($rows as $row) { |
331
|
|
|
$result[(int)$row['parent']][] = (int)$row['id']; |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
return $result; |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
/** |
338
|
|
|
* Find names and parents of the file system nodes with given IDs within the given storage |
339
|
|
|
* @param int[] $nodeIds |
340
|
|
|
* @param string $storageId |
341
|
|
|
* @return array where keys are the node IDs and values are associative arrays |
342
|
|
|
* like { 'name' => string, 'parent' => int }; |
343
|
|
|
*/ |
344
|
|
|
public function findNodeNamesAndParents(array $nodeIds, string $storageId) : array { |
345
|
|
|
$result = []; |
346
|
|
|
|
347
|
|
|
if (!empty($nodeIds)) { |
348
|
|
|
$sql = 'SELECT `fileid`, `name`, `parent` '. |
349
|
|
|
'FROM `*PREFIX*filecache` `filecache` '. |
350
|
|
|
'JOIN `*PREFIX*storages` `storages` '. |
351
|
|
|
'ON `filecache`.`storage` = `storages`.`numeric_id` '. |
352
|
|
|
'WHERE `storages`.`id` = ? '. |
353
|
|
|
'AND `filecache`.`fileid` IN '. $this->questionMarks(\count($nodeIds)); |
354
|
|
|
|
355
|
|
|
$rows = $this->execute($sql, \array_merge([$storageId], $nodeIds))->fetchAll(); |
|
|
|
|
356
|
|
|
|
357
|
|
|
foreach ($rows as $row) { |
358
|
|
|
$result[$row['fileid']] = [ |
359
|
|
|
'name' => $row['name'], |
360
|
|
|
'parent' => (int)$row['parent'] |
361
|
|
|
]; |
362
|
|
|
} |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
return $result; |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
/** |
369
|
|
|
* Returns all genre IDs associated with the given artist |
370
|
|
|
* @return int[] |
371
|
|
|
*/ |
372
|
|
|
public function getGenresByArtistId(int $artistId, string $userId) : array { |
373
|
|
|
$sql = 'SELECT DISTINCT(`genre_id`) FROM `*PREFIX*music_tracks` WHERE |
374
|
|
|
`genre_id` IS NOT NULL AND `user_id` = ? AND `artist_id` = ?'; |
375
|
|
|
$rows = $this->execute($sql, [$userId, $artistId]); |
|
|
|
|
376
|
|
|
return $rows->fetchAll(\PDO::FETCH_COLUMN); |
377
|
|
|
} |
378
|
|
|
|
379
|
|
|
/** |
380
|
|
|
* Returns all tracks IDs of the user, organized by the genre_id. |
381
|
|
|
* @return array where keys are genre IDs and values are arrays of track IDs |
382
|
|
|
*/ |
383
|
|
|
public function mapGenreIdsToTrackIds(string $userId) : array { |
384
|
|
|
$sql = 'SELECT `id`, `genre_id` FROM `*PREFIX*music_tracks` |
385
|
|
|
WHERE `genre_id` IS NOT NULL and `user_id` = ?'; |
386
|
|
|
$rows = $this->execute($sql, [$userId])->fetchAll(); |
|
|
|
|
387
|
|
|
|
388
|
|
|
$result = []; |
389
|
|
|
foreach ($rows as $row) { |
390
|
|
|
$result[(int)$row['genre_id']][] = (int)$row['id']; |
391
|
|
|
} |
392
|
|
|
|
393
|
|
|
return $result; |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
/** |
397
|
|
|
* Returns file IDs of the tracks which do not have genre scanned. This is not the same |
398
|
|
|
* thing as unknown genre, which means that the genre has been scanned but was not found |
399
|
|
|
* from the track metadata. |
400
|
|
|
* @return int[] |
401
|
|
|
*/ |
402
|
|
|
public function findFilesWithoutScannedGenre(string $userId) : array { |
403
|
|
|
$sql = 'SELECT `track`.`file_id` FROM `*PREFIX*music_tracks` `track` |
404
|
|
|
INNER JOIN `*PREFIX*filecache` `file` |
405
|
|
|
ON `track`.`file_id` = `file`.`fileid` |
406
|
|
|
WHERE `genre_id` IS NULL and `user_id` = ?'; |
407
|
|
|
$rows = $this->execute($sql, [$userId]); |
|
|
|
|
408
|
|
|
return $rows->fetchAll(\PDO::FETCH_COLUMN); |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
/** |
412
|
|
|
* Update "last played" timestamp and increment the total play count of the track. |
413
|
|
|
* The DB row is updated *without* updating the `updated` column. |
414
|
|
|
* @return bool true if the track was found and updated, false otherwise |
415
|
|
|
*/ |
416
|
|
|
public function recordTrackPlayed(int $trackId, string $userId, \DateTime $timeOfPlay) : bool { |
417
|
|
|
$sql = 'UPDATE `*PREFIX*music_tracks` |
418
|
|
|
SET `last_played` = ?, `play_count` = `play_count` + 1 |
419
|
|
|
WHERE `user_id` = ? AND `id` = ?'; |
420
|
|
|
$params = [$timeOfPlay->format(BaseMapper::SQL_DATE_FORMAT), $userId, $trackId]; |
421
|
|
|
$result = $this->execute($sql, $params); |
|
|
|
|
422
|
|
|
return ($result->rowCount() > 0); |
423
|
|
|
} |
424
|
|
|
|
425
|
|
|
/** |
426
|
|
|
* @see \OCA\Music\Db\BaseMapper::findUniqueEntity() |
427
|
|
|
* @param Track $track |
428
|
|
|
* @return Track |
429
|
|
|
*/ |
430
|
|
|
protected function findUniqueEntity(Entity $track) : Entity { |
431
|
|
|
return $this->findByFileId($track->getFileId(), $track->getUserId()); |
|
|
|
|
432
|
|
|
} |
433
|
|
|
} |
434
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.