Track::getArtistNameString()   A
last analyzed

Complexity

Conditions 2
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 1
c 1
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 - 2025
13
 */
14
15
namespace OCA\Music\Db;
16
17
use OCA\Music\Utility\StringUtil;
18
use OCA\Music\Utility\Util;
19
use OCP\IL10N;
20
use OCP\IURLGenerator;
21
22
/**
23
 * @method string getTitle()
24
 * @method void setTitle(string $title)
25
 * @method ?int getNumber()
26
 * @method void setNumber(?int $number)
27
 * @method ?int getDisk()
28
 * @method void setDisk(?int $disk)
29
 * @method ?int getYear()
30
 * @method void setYear(?int $year)
31
 * @method int getArtistId()
32
 * @method void setArtistId(int $artistId)
33
 * @method int getAlbumId()
34
 * @method void setAlbumId(int $albumId)
35
 * @method ?int getLength()
36
 * @method void setLength(?int $length)
37
 * @method int getFileId()
38
 * @method void setFileId(int $fileId)
39
 * @method ?int getBitrate()
40
 * @method void setBitrate(?int $bitrate)
41
 * @method string getMimetype()
42
 * @method void setMimetype(string $mimetype)
43
 * @method ?string getMbid()
44
 * @method void setMbid(?string $mbid)
45
 * @method ?string getStarred()
46
 * @method void setStarred(?string $timestamp)
47
 * @method int getRating()
48
 * @method void setRating(int $rating)
49
 * @method ?int getGenreId()
50
 * @method void setGenreId(?int $genreId)
51
 * @method int getPlayCount()
52
 * @method void setPlayCount(int $count)
53
 * @method ?string getLastPlayed()
54
 * @method void setLastPlayed(?string $timestamp)
55
 * @method int getDirty()
56
 * @method void setDirty(int $dirty)
57
 *
58
 * @method string getFilename()
59
 * @method int getSize()
60
 * @method int getFileModTime()
61
 * @method ?string getAlbumName()
62
 * @method ?string getArtistName()
63
 * @method ?string getGenreName()
64
 * @method int getFolderId()
65
 */
66
class Track extends Entity {
67
	public string $title = '';
68
	public ?int $number = null;
69
	public ?int $disk = null;
70
	public ?int $year = null;
71
	public ?int $artistId = null;
72
	public ?int $albumId = null;
73
	public ?int $length = null;
74
	public int $fileId = 0;
75
	public ?int $bitrate = null;
76
	public string $mimetype = '';
77
	public ?string $mbid = null;
78
	public ?string $starred = null;
79
	public int $rating = 0;
80
	public ?int $genreId = null;
81
	public int $playCount = 0;
82
	public ?string $lastPlayed = null;
83
	public int $dirty = 0;
84
85
	// not from the music_tracks table but still part of the standard content of this entity:
86
	public string $filename = '';
87
	public int $size = 0;
88
	public int $fileModTime = 0;
89
	public ?string $albumName = null;
90
	public ?string $artistName = null;
91
	public ?string $genreName = null;
92
	public int $folderId = 0;
93
94
	// the rest of the variables are injected separately when needed
95
	private ?Album $album = null;
96
	private ?int $numberOnPlaylist = null;
97
	private ?string $folderPath = null;
98
	private ?string $lyrics = null;
99
100
	public function __construct() {
101
		$this->addType('number', 'int');
102
		$this->addType('disk', 'int');
103
		$this->addType('year', 'int');
104
		$this->addType('artistId', 'int');
105
		$this->addType('albumId', 'int');
106
		$this->addType('length', 'int');
107
		$this->addType('bitrate', 'int');
108
		$this->addType('fileId', 'int');
109
		$this->addType('genreId', 'int');
110
		$this->addType('playCount', 'int');
111
		$this->addType('rating', 'int');
112
		$this->addType('dirty', 'int');
113
		$this->addType('size', 'int');
114
		$this->addType('fileModTime', 'int');
115
		$this->addType('folderId', 'int');
116
	}
117
118
	public function getAlbum() : ?Album {
119
		return $this->album;
120
	}
121
122
	public function setAlbum(?Album $album) : void {
123
		$this->album = $album;
124
	}
125
126
	public function getNumberOnPlaylist() : ?int {
127
		return $this->numberOnPlaylist;
128
	}
129
130
	public function setNumberOnPlaylist(int $number) : void {
131
		$this->numberOnPlaylist = $number;
132
	}
133
134
	public function setFolderPath(string $path) : void {
135
		$this->folderPath = $path;
136
	}
137
138
	public function setLyrics(?string $lyrics) : void {
139
		$this->lyrics = $lyrics;
140
	}
141
142
	public function getPath() : ?string {
143
		return ($this->folderPath ?? '') . '/' . $this->filename;
144
	}
145
146
	public function getUri(IURLGenerator $urlGenerator) : string {
147
		return $urlGenerator->linkToRoute(
148
			'music.shivaApi.track',
149
			['id' => $this->id]
150
		);
151
	}
152
153
	public function getArtistWithUri(IURLGenerator $urlGenerator) : array {
154
		return [
155
			'id' => $this->artistId,
156
			'uri' => $urlGenerator->linkToRoute(
157
				'music.shivaApi.artist',
158
				['id' => $this->artistId]
159
			)
160
		];
161
	}
162
163
	public function getAlbumWithUri(IURLGenerator $urlGenerator) : array {
164
		return [
165
			'id' => $this->albumId,
166
			'uri' => $urlGenerator->linkToRoute(
167
				'music.shivaApi.album',
168
				['id' => $this->albumId]
169
			)
170
		];
171
	}
172
173
	public function getArtistNameString(IL10N $l10n) : string {
174
		return $this->getArtistName() ?: Artist::unknownNameString($l10n);
175
	}
176
177
	public function getAlbumNameString(IL10N $l10n) : string {
178
		return $this->getAlbumName() ?: Album::unknownNameString($l10n);
179
	}
180
181
	public function getGenreNameString(IL10N $l10n) : string {
182
		return $this->getGenreName() ?: Genre::unknownNameString($l10n);
183
	}
184
185
	public function toCollection() : array {
186
		return [
187
			'title' => $this->getTitle(),
188
			'number' => $this->getNumber(),
189
			'disk' => $this->getDisk(),
190
			'artistId' => $this->getArtistId(),
191
			'length' => $this->getLength(),
192
			'files' => [$this->getMimetype() => $this->getFileId()],
193
			'id' => $this->getId(),
194
		];
195
	}
196
197
	public function toShivaApi(IURLGenerator $urlGenerator) : array {
198
		return [
199
			'title' => $this->getTitle(),
200
			'ordinal' => $this->getAdjustedTrackNumber(),
201
			'artist' => $this->getArtistWithUri($urlGenerator),
202
			'album' => $this->getAlbumWithUri($urlGenerator),
203
			'length' => $this->getLength(),
204
			'files' => [$this->getMimetype() => $urlGenerator->linkToRoute(
205
				'music.musicApi.download',
206
				['fileId' => $this->getFileId()]
207
			)],
208
			'bitrate' => $this->getBitrate(),
209
			'id' => $this->getId(),
210
			'slug' => $this->slugify('title'),
0 ignored issues
show
Deprecated Code introduced by
The function OCP\AppFramework\Db\Entity::slugify() has been deprecated: 24.0.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

210
			'slug' => /** @scrutinizer ignore-deprecated */ $this->slugify('title'),

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.

Loading history...
211
			'uri' => $this->getUri($urlGenerator)
212
		];
213
	}
214
215
	public function toAmpacheApi(
216
			IL10N $l10n,
217
			callable $createPlayUrl,
218
			callable $createImageUrl,
219
			callable $renderAlbumOrArtistRef,
220
			string $genreKey,
221
			bool $includeArtists) : array {
222
		$album = $this->getAlbum();
223
224
		$result = [
225
			'id' => (string)$this->getId(),
226
			'title' => $this->getTitle() ?: '',
227
			'name' => $this->getTitle() ?: '',
228
			'artist' => $renderAlbumOrArtistRef($this->getArtistId() ?: 0, $this->getArtistNameString($l10n)),
229
			'albumartist' => $renderAlbumOrArtistRef($album->getAlbumArtistId() ?: 0, $album->getAlbumArtistNameString($l10n)),
230
			'album' => $renderAlbumOrArtistRef($album->getId() ?: 0, $album->getNameString($l10n)),
231
			'url' => $createPlayUrl($this),
232
			'time' => $this->getLength(),
233
			'year' => $this->getYear(),
234
			'track' => $this->getAdjustedTrackNumber(), // TODO: maybe there should be a user setting to select plain or adjusted number
235
			'playlisttrack' => $this->getAdjustedTrackNumber(),
236
			'disk' => $this->getDisk(),
237
			'filename' => $this->getFilename(),
238
			'format' => $this->getFileExtension(),
239
			'stream_format' => $this->getFileExtension(),
240
			'bitrate' => $this->getBitrate(),
241
			'stream_bitrate' => $this->getBitrate(),
242
			'mime' => $this->getMimetype(),
243
			'stream_mime' => $this->getMimetype(),
244
			'size' => $this->getSize(),
245
			'art' => $createImageUrl($this),
246
			'rating' => $this->getRating(),
247
			'preciserating' => $this->getRating(),
248
			'playcount' => $this->getPlayCount(),
249
			'flag' => !empty($this->getStarred()),
250
			'language' => null,
251
			'lyrics' => $this->lyrics,
252
			'mode' => null, // cbr/vbr
253
			'rate' => null, // sample rate [Hz]
254
			'replaygain_album_gain' => null,
255
			'replaygain_album_peak' => null,
256
			'replaygain_track_gain' => null,
257
			'replaygain_track_peak' => null,
258
			'r128_album_gain' => null,
259
			'r128_track_gain' => null,
260
		];
261
262
		$result['has_art'] = !empty($result['art']);
263
264
		$genreId = $this->getGenreId();
265
		if ($genreId !== null) {
266
			$result[$genreKey] = [[
267
				'id' => (string)$genreId,
268
				'text' => $this->getGenreNameString($l10n),
269
				'count' => 1
270
			]];
271
		}
272
273
		if ($includeArtists) {
274
			// Add another property `artists`. Apparently, it exists to support multiple artists per song
275
			// but we don't have such possibility and this is always just a 1-item array.
276
			$result['artists'] = [$result['artist']];
277
		}
278
	
279
		return $result;
280
	}
281
282
	/**
283
	 * The same API format is used both on "old" and "new" API methods. The "new" API adds some
284
	 * new fields for the songs, but providing some extra fields shouldn't be a problem for the
285
	 * older clients. The $track entity must have the Album reference injected prior to calling this.
286
	 *
287
	 * @param string[] $ignoredArticles
288
	 */
289
	public function toSubsonicApi(IL10N $l10n, array $ignoredArticles) : array {
290
		$albumId = $this->getAlbumId();
291
		$album = $this->getAlbum();
292
		$hasCoverArt = ($album !== null && !empty($album->getCoverFileId()));
293
294
		return [
295
			'id' => 'track-' . $this->getId(),
296
			'parent' => 'album-' . $albumId,
297
			'discNumber' => $this->getDisk(),
298
			'title' => $this->getTitle(),
299
			'artist' => $this->getArtistNameString($l10n),
300
			'isDir' => false,
301
			'album' => $this->getAlbumNameString($l10n),
302
			'year' => $this->getYear(),
303
			'size' => $this->getSize(),
304
			'contentType' => $this->getMimetype(),
305
			'suffix' => $this->getFileExtension(),
306
			'duration' => $this->getLength() ?? 0,
307
			'bitRate' => empty($this->getBitrate()) ? null : (int)\round($this->getBitrate()/1000), // convert bps to kbps
308
			'path' => $this->getPath(),
309
			'isVideo' => false,
310
			'albumId' => 'album-' . $albumId,
311
			'artistId' => 'artist-' . $this->getArtistId(),
312
			'type' => 'music',
313
			'mediaType' => 'song', // OpenSubsonic
314
			'created' => Util::formatZuluDateTime($this->getCreated()),
315
			'track' => $this->getAdjustedTrackNumber(false), // DSub would get confused of playlist numbering, https://github.com/owncloud/music/issues/994
316
			'starred' => Util::formatZuluDateTime($this->getStarred()),
317
			'userRating' => $this->getRating() ?: null,
318
			'averageRating' => $this->getRating() ?: null,
319
			'genre' => empty($this->getGenreId()) ? null : $this->getGenreNameString($l10n),
320
			'coverArt' => !$hasCoverArt ? null : 'album-' . $albumId,
321
			'playCount' => $this->getPlayCount(),
322
			'played' => Util::formatZuluDateTime($this->getLastPlayed()) ?? '', // OpenSubsonic
323
			'sortName' => StringUtil::splitPrefixAndBasename($this->getTitle(), $ignoredArticles)['basename'], // OpenSubsonic
324
		];
325
	}
326
327
	public function getAdjustedTrackNumber(bool $enablePlaylistNumbering=true) : ?int {
328
		// Unless disabled, the number on playlist overrides the track number if it is set.
329
		if ($enablePlaylistNumbering && $this->numberOnPlaylist !== null) {
330
			$trackNumber = $this->numberOnPlaylist;
331
		} else {
332
			// On single-disk albums, the track number is given as-is.
333
			// On multi-disk albums, the disk-number is applied to the track number.
334
			// In case we have no Album reference, the best we can do is to apply the
335
			// disk number if it is greater than 1. For disk 1, we don't know if this
336
			// is a multi-disk album or not.
337
			$numberOfDisks = ($this->album) ? $this->album->getNumberOfDisks() : null;
338
			$trackNumber = $this->getNumber();
339
340
			if ($this->disk > 1 || $numberOfDisks > 1) {
341
				$trackNumber = $trackNumber ?: 0;
342
				$trackNumber += (100 * $this->disk);
343
			}
344
		}
345
346
		return $trackNumber;
347
	}
348
349
	public function getFileExtension() : string {
350
		$parts = Util::explode('.', $this->getFilename());
351
		return empty($parts) ? '' : \end($parts);
352
	}
353
354
	/**
355
	 * Get an instance which has all the mandatory fields set to valid but empty values
356
	 */
357
	public static function emptyInstance() : Track {
358
		$track = new self();
359
360
		$track->id = -1;
361
		$track->title = '';
362
		$track->artistId = -1;
363
		$track->albumId = -1;
364
		$track->fileId = -1;
365
		$track->mimetype = '';
366
		$track->playCount = 0;
367
		$track->dirty = 0;
368
369
		$track->filename = '';
370
		$track->size = 0;
371
		$track->fileModTime = 0;
372
		$track->folderId = -1;
373
374
		return $track;
375
	}
376
}
377