Passed
Push — master ( 012442...4c4644 )
by Pauli
03:10 queued 15s
created

Track::setFolderPath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 2
rs 10
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 - 2024
13
 */
14
15
namespace OCA\Music\Db;
16
17
use OCA\Music\Utility\Util;
18
use OCP\IL10N;
19
use OCP\IURLGenerator;
20
21
/**
22
 * @method string getTitle()
23
 * @method void setTitle(string $title)
24
 * @method ?int getNumber()
25
 * @method void setNumber(?int $number)
26
 * @method ?int getDisk()
27
 * @method void setDisk(?int $disk)
28
 * @method ?int getYear()
29
 * @method void setYear(?int $year)
30
 * @method int getArtistId()
31
 * @method void setArtistId(int $artistId)
32
 * @method int getAlbumId()
33
 * @method void setAlbumId(int $albumId)
34
 * @method ?int getLength()
35
 * @method void setLength(?int $length)
36
 * @method int getFileId()
37
 * @method void setFileId(int $fileId)
38
 * @method ?int getBitrate()
39
 * @method void setBitrate(?int $bitrate)
40
 * @method string getMimetype()
41
 * @method void setMimetype(string $mimetype)
42
 * @method ?string getMbid()
43
 * @method void setMbid(?string $mbid)
44
 * @method ?string getStarred()
45
 * @method void setStarred(?string $timestamp)
46
 * @method ?int getRating()
47
 * @method setRating(?int $rating)
48
 * @method ?int getGenreId()
49
 * @method void setGenreId(?int $genreId)
50
 * @method int getPlayCount()
51
 * @method void setPlayCount(int $count)
52
 * @method ?string getLastPlayed()
53
 * @method void setLastPlayed(?string $timestamp)
54
 * @method int getDirty()
55
 * @method void setDirty(int $dirty)
56
 *
57
 * @method string getFilename()
58
 * @method int getSize()
59
 * @method int getFileModTime()
60
 * @method ?string getAlbumName()
61
 * @method ?string getArtistName()
62
 * @method ?string getGenreName()
63
 * @method int getFolderId()
64
 */
65
class Track extends Entity {
66
	public $title;
67
	public $number;
68
	public $disk;
69
	public $year;
70
	public $artistId;
71
	public $albumId;
72
	public $length;
73
	public $fileId;
74
	public $bitrate;
75
	public $mimetype;
76
	public $mbid;
77
	public $starred;
78
	public $rating;
79
	public $genreId;
80
	public $playCount;
81
	public $lastPlayed;
82
	public $dirty;
83
84
	// not from the music_tracks table but still part of the standard content of this entity:
85
	public $filename;
86
	public $size;
87
	public $fileModTime;
88
	public $albumName;
89
	public $artistName;
90
	public $genreName;
91
	public $folderId;
92
93
	// the rest of the variables are injected separately when needed
94
	private ?Album $album = null;
95
	private ?int $numberOnPlaylist = null;
96
	private ?string $folderPath = null;
97
98
	public function __construct() {
99
		$this->addType('number', 'int');
100
		$this->addType('disk', 'int');
101
		$this->addType('year', 'int');
102
		$this->addType('artistId', 'int');
103
		$this->addType('albumId', 'int');
104
		$this->addType('length', 'int');
105
		$this->addType('bitrate', 'int');
106
		$this->addType('fileId', 'int');
107
		$this->addType('genreId', 'int');
108
		$this->addType('playCount', 'int');
109
		$this->addType('rating', 'int');
110
		$this->addType('dirty', 'int');
111
		$this->addType('size', 'int');
112
		$this->addType('fileModTime', 'int');
113
		$this->addType('folderId', 'int');
114
	}
115
116
	public function getAlbum() : ?Album {
117
		return $this->album;
118
	}
119
120
	public function setAlbum(?Album $album) : void {
121
		$this->album = $album;
122
	}
123
124
	public function getNumberOnPlaylist() : ?int {
125
		return $this->numberOnPlaylist;
126
	}
127
128
	public function setNumberOnPlaylist(int $number) {
129
		$this->numberOnPlaylist = $number;
130
	}
131
132
	public function setFolderPath(string $path) : void {
133
		$this->folderPath = $path;
134
	}
135
136
	public function getPath() : ?string {
137
		return ($this->folderPath ?? '') . '/' . $this->filename;
138
	}
139
140
	public function getUri(IURLGenerator $urlGenerator) : string {
141
		return $urlGenerator->linkToRoute(
142
			'music.shivaApi.track',
143
			['trackId' => $this->id]
144
		);
145
	}
146
147
	public function getArtistWithUri(IURLGenerator $urlGenerator) : array {
148
		return [
149
			'id' => $this->artistId,
150
			'uri' => $urlGenerator->linkToRoute(
151
				'music.shivaApi.artist',
152
				['artistId' => $this->artistId]
153
			)
154
		];
155
	}
156
157
	public function getAlbumWithUri(IURLGenerator $urlGenerator) : array {
158
		return [
159
			'id' => $this->albumId,
160
			'uri' => $urlGenerator->linkToRoute(
161
				'music.shivaApi.album',
162
				['albumId' => $this->albumId]
163
			)
164
		];
165
	}
166
167
	public function getArtistNameString(IL10N $l10n) : string {
168
		return $this->getArtistName() ?: Artist::unknownNameString($l10n);
169
	}
170
171
	public function getAlbumNameString(IL10N $l10n) : string {
172
		return $this->getAlbumName() ?: Album::unknownNameString($l10n);
173
	}
174
175
	public function getGenreNameString(IL10N $l10n) : string {
176
		return $this->getGenreName() ?: Genre::unknownNameString($l10n);
177
	}
178
179
	public function toCollection() : array {
180
		return [
181
			'title' => $this->getTitle(),
182
			'number' => $this->getNumber(),
183
			'disk' => $this->getDisk(),
184
			'artistId' => $this->getArtistId(),
185
			'length' => $this->getLength(),
186
			'files' => [$this->getMimetype() => $this->getFileId()],
187
			'id' => $this->getId(),
188
		];
189
	}
190
191
	public function toAPI(IURLGenerator $urlGenerator) : array {
192
		return [
193
			'title' => $this->getTitle(),
194
			'ordinal' => $this->getAdjustedTrackNumber(),
195
			'artist' => $this->getArtistWithUri($urlGenerator),
196
			'album' => $this->getAlbumWithUri($urlGenerator),
197
			'length' => $this->getLength(),
198
			'files' => [$this->getMimetype() => $urlGenerator->linkToRoute(
199
				'music.musicApi.download',
200
				['fileId' => $this->getFileId()]
201
			)],
202
			'bitrate' => $this->getBitrate(),
203
			'id' => $this->getId(),
204
			'slug' => $this->slugify('title'),
205
			'uri' => $this->getUri($urlGenerator)
206
		];
207
	}
208
209
	public function toAmpacheApi(
210
			IL10N $l10n,
211
			callable $createPlayUrl,
212
			callable $createImageUrl,
213
			callable $renderAlbumOrArtistRef,
214
			string $genreKey,
215
			bool $includeArtists) : array {
216
		$album = $this->getAlbum();
217
218
		$result = [
219
			'id' => (string)$this->getId(),
220
			'title' => $this->getTitle() ?: '',
221
			'name' => $this->getTitle() ?: '',
222
			'artist' => $renderAlbumOrArtistRef($this->getArtistId() ?: 0, $this->getArtistNameString($l10n)),
223
			'albumartist' => $renderAlbumOrArtistRef($album->getAlbumArtistId() ?: 0, $album->getAlbumArtistNameString($l10n)),
224
			'album' => $renderAlbumOrArtistRef($album->getId() ?: 0, $album->getNameString($l10n)),
225
			'url' => $createPlayUrl($this),
226
			'time' => $this->getLength(),
227
			'year' => $this->getYear(),
228
			'track' => $this->getAdjustedTrackNumber(), // TODO: maybe there should be a user setting to select plain or adjusted number
229
			'playlisttrack' => $this->getAdjustedTrackNumber(),
230
			'disk' => $this->getDisk(),
231
			'filename' => $this->getFilename(),
232
			'format' => $this->getFileExtension(),
233
			'stream_format' => $this->getFileExtension(),
234
			'bitrate' => $this->getBitrate(),
235
			'stream_bitrate' => $this->getBitrate(),
236
			'mime' => $this->getMimetype(),
237
			'stream_mime' => $this->getMimetype(),
238
			'size' => $this->getSize(),
239
			'art' => $createImageUrl($this),
240
			'rating' => $this->getRating() ?? 0,
241
			'preciserating' => $this->getRating() ?? 0,
242
			'playcount' => $this->getPlayCount(),
243
			'flag' => !empty($this->getStarred()),
244
			'language' => null,
245
			'lyrics' => null,
246
			'mode' => null, // cbr/vbr
247
			'rate' => null, // sample rate [Hz]
248
			'replaygain_album_gain' => null,
249
			'replaygain_album_peak' => null,
250
			'replaygain_track_gain' => null,
251
			'replaygain_track_peak' => null,
252
			'r128_album_gain' => null,
253
			'r128_track_gain' => null,
254
		];
255
256
		$result['has_art'] = !empty($result['art']);
257
258
		$genreId = $this->getGenreId();
259
		if ($genreId !== null) {
260
			$result[$genreKey] = [[
261
				'id' => (string)$genreId,
262
				'text' => $this->getGenreNameString($l10n),
263
				'count' => 1
264
			]];
265
		}
266
267
		if ($includeArtists) {
268
			// Add another property `artists`. Apparently, it exists to support multiple artists per song
269
			// but we don't have such possibility and this is always just a 1-item array.
270
			$result['artists'] = [$result['artist']];
271
		}
272
	
273
		return $result;
274
	}
275
276
	/**
277
	 * The same API format is used both on "old" and "new" API methods. The "new" API adds some
278
	 * new fields for the songs, but providing some extra fields shouldn't be a problem for the
279
	 * older clients. The $track entity must have the Album reference injected prior to calling this.
280
	 * 
281
	 * @param string[] $ignoredArticles
282
	 */
283
	public function toSubsonicApi(IL10N $l10n, array $ignoredArticles) : array {
284
		$albumId = $this->getAlbumId();
285
		$album = $this->getAlbum();
286
		$hasCoverArt = ($album !== null && !empty($album->getCoverFileId()));
287
288
		return [
289
			'id' => 'track-' . $this->getId(),
290
			'parent' => 'album-' . $albumId,
291
			'discNumber' => $this->getDisk(),
292
			'title' => $this->getTitle(),
293
			'artist' => $this->getArtistNameString($l10n),
294
			'isDir' => false,
295
			'album' => $this->getAlbumNameString($l10n),
296
			'year' => $this->getYear(),
297
			'size' => $this->getSize(),
298
			'contentType' => $this->getMimetype(),
299
			'suffix' => $this->getFileExtension(),
300
			'duration' => $this->getLength() ?? 0,
301
			'bitRate' => empty($this->getBitrate()) ? null : (int)\round($this->getBitrate()/1000), // convert bps to kbps
302
			'path' => $this->getPath(),
303
			'isVideo' => false,
304
			'albumId' => 'album-' . $albumId,
305
			'artistId' => 'artist-' . $this->getArtistId(),
306
			'type' => 'music',
307
			'created' => Util::formatZuluDateTime($this->getCreated()),
308
			'track' => $this->getAdjustedTrackNumber(false), // DSub would get confused of playlist numbering, https://github.com/owncloud/music/issues/994
309
			'starred' => Util::formatZuluDateTime($this->getStarred()),
310
			'userRating' => $this->getRating() ?: null,
311
			'averageRating' => $this->getRating() ?: null,
312
			'genre' => empty($this->getGenreId()) ? null : $this->getGenreNameString($l10n),
313
			'coverArt' => !$hasCoverArt ? null : 'album-' . $albumId,
314
			'playCount' => $this->getPlayCount(),
315
			'played' => Util::formatZuluDateTime($this->getLastPlayed()) ?? '', // OpenSubsonic
316
			'sortName' => Util::splitPrefixAndBasename($this->getTitle(), $ignoredArticles)['basename'], // OpenSubsonic
317
		];
318
	}
319
320
	public function getAdjustedTrackNumber(bool $enablePlaylistNumbering=true) : ?int {
321
		// Unless disabled, the number on playlist overrides the track number if it is set.
322
		if ($enablePlaylistNumbering && $this->numberOnPlaylist !== null) {
323
			$trackNumber = $this->numberOnPlaylist;
324
		} else {
325
			// On single-disk albums, the track number is given as-is.
326
			// On multi-disk albums, the disk-number is applied to the track number.
327
			// In case we have no Album reference, the best we can do is to apply the
328
			// disk number if it is greater than 1. For disk 1, we don't know if this
329
			// is a multi-disk album or not.
330
			$numberOfDisks = ($this->album) ? $this->album->getNumberOfDisks() : null;
331
			$trackNumber = $this->getNumber();
332
333
			if ($this->disk > 1 || $numberOfDisks > 1) {
334
				$trackNumber = $trackNumber ?: 0;
335
				$trackNumber += (100 * $this->disk);
336
			}
337
		}
338
339
		return $trackNumber;
340
	}
341
342
	public function getFileExtension() : string {
343
		$parts = Util::explode('.', $this->getFilename());
344
		return empty($parts) ? '' : \end($parts);
345
	}
346
347
}
348