Passed
Push — feature/786_podcasts ( 30025c...d8f350 )
by Pauli
02:43
created

PodcastEpisode::getEpisodeWithSeason()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 8
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 Pauli Järvinen <[email protected]>
10
 * @copyright Pauli Järvinen 2021
11
 */
12
13
namespace OCA\Music\Db;
14
15
use \OCP\AppFramework\Db\Entity;
16
use \OCA\Music\Utility\Util;
17
18
/**
19
 * @method string getUserId()
20
 * @method void setUserId(string $userId)
21
 * @method int getChannelId()
22
 * @method void setChannelId(int $id)
23
 * @method string getStreamUrl()
24
 * @method setStreamUrl(string $url)
25
 * @method string getMimetype()
26
 * @method setMimetype(string $mime)
27
 * @method int getSize()
28
 * @method void setSize(int $size)
29
 * @method int getDuration()
30
 * @method void setDuration(int $duration)
31
 * @method string getGuid()
32
 * @method void setGuid(string $guid)
33
 * @method string getGuidHash()
34
 * @method void setGuidHash(string $guidHash)
35
 * @method string getTitle()
36
 * @method void setTitle(string $title)
37
 * @method int getEpisode()
38
 * @method void setEpisode(int $episode)
39
 * @method int getSeason()
40
 * @method void setSeason(int $season)
41
 * @method string getLinkUrl()
42
 * @method void setLinkUrl(string $url)
43
 * @method string getPublished()
44
 * @method void setPublished(string $timestamp)
45
 * @method string getKeywords()
46
 * @method void setKeywords(string $keywords)
47
 * @method string getCopyright()
48
 * @method void setCopyright(string $copyright)
49
 * @method string getAuthor()
50
 * @method void setAuthor(string $author)
51
 * @method string getDescription()
52
 * @method void setDescription(string $description)
53
 * @method string getStarred()
54
 * @method void setStarred(string $timestamp)
55
 * @method string getCreated()
56
 * @method void setCreated(string $timestamp)
57
 * @method string getUpdated()
58
 * @method void setUpdated(string $timestamp)
59
 */
60
class PodcastEpisode extends Entity {
61
	public $userId;
62
	public $channelId;
63
	public $streamUrl;
64
	public $mimetype;
65
	public $size;
66
	public $duration;
67
	public $guid;
68
	public $guidHash;
69
	public $title;
70
	public $episode;
71
	public $season;
72
	public $linkUrl;
73
	public $published;
74
	public $keywords;
75
	public $copyright;
76
	public $author;
77
	public $description;
78
	public $starred;
79
	public $created;
80
	public $updated;
81
82
	public function __construct() {
83
		$this->addType('channelId', 'int');
84
		$this->addType('size', 'int');
85
		$this->addType('duration', 'int');
86
		$this->addType('episode', 'int');
87
	}
88
89
	public function toApi() : array {
90
		return [
91
			'id' => $this->getId(),
92
			'title' => $this->getTitle(),
93
			'ordinal' => $this->getEpisodeWithSeason(),
94
			'stream_url' => $this->getStreamUrl(),
95
			'mimetype' => $this->getMimetype()
96
		];
97
	}
98
99
	public function detailsToApi() : array {
100
		return [
101
			'id' => $this->getId(),
102
			'title' => $this->getTitle(),
103
			'episode' => $this->getEpisode(),
104
			'season' => $this->getSeason(),
105
			'description' => $this->getDescription(),
106
			'channel_id' => $this->getChannelId(),
107
			'link_url' => $this->getLinkUrl(),
108
			'stream_url' => $this->getStreamUrl(),
109
			'mimetype' => $this->getMimetype(),
110
			'author' => $this->getAuthor(),
111
			'copyright' => $this->getCopyright(),
112
			'duration' => $this->getDuration(),
113
			'size' => $this->getSize(),
114
			'bit_rate' => $this->getBitrate(),
115
			'guid' => $this->getGuid(),
116
			'keywords' => $this->getKeywords(),
117
			'published' => $this->getPublished(),
118
		];
119
	}
120
121
	public function toAmpacheApi() : array {
122
		return [
123
			'id' => (string)$this->getId(),
124
			'name' => $this->getTitle(),
125
			'title' => $this->getTitle(),
126
			'description' => $this->getDescription(),
127
			'author' => $this->getAuthor(),
128
			'author_full' => $this->getAuthor(),
129
			'website' => $this->getLinkUrl(),
130
			'pubdate' => $this->getPublished(), // TODO: format?
131
			'state' => 'Completed',
132
			'filelength' => Util::formatTime($this->getDuration()),
133
			'filesize' => Util::formatFileSize($this->getSize(), 2) . 'B',
134
			'mime' => $this->getMimetype(),
135
			'url' => $this->getStreamUrl(),
136
			'flag' => empty($this->getStarred()) ? 0 : 1,
137
		];
138
	}
139
140
	public function toSubsonicApi() : array {
141
		$result = [
142
			'id' => 'podcast_episode-' . $this->getId(),
143
			'streamId' => 'podcast_episode-' . $this->getId(),
144
			'channelId' => 'podcast_channel-' . $this->getChannelId(),
145
			'title' => $this->getTitle(),
146
			'track' => $this->getEpisode(),
147
			'description' => $this->getDescription(),
148
			'publishDate' => Util::formatZuluDateTime($this->getPublished()),
149
			'status' => 'completed',
150
			'parent' => 'podcast_channel-' . $this->getChannelId(),
151
			'isDir' => false,
152
			'year' => $this->getYear(),
153
			'genre' => 'Podcast',
154
			'coverArt' => 'podcast_channel-' . $this->getChannelId(),
155
			'size' => $this->getSize(),
156
			'contentType' => $this->getMimetype(),
157
			'suffix' => $this->getSuffix(),
158
			'duration' => $this->getDuration(),
159
			'bitRate' => empty($this->getBitrate()) ? 0 : (int)\round($this->getBitrate()/1000), // convert bps to kbps
160
		];
161
162
		if (!empty($this->starred)) {
163
			$result['starred'] = Util::formatZuluDateTime($this->starred);
164
		}
165
166
		return $result;
167
	}
168
169
	public function getEpisodeWithSeason() : ?string {
170
		$result = (string)$this->getEpisode();
171
		// the season is considered only if there actually is an episode
172
		$season = $this->getSeason();
173
		if ($result !== null && $season !== null) {
0 ignored issues
show
introduced by
The condition $season !== null is always true.
Loading history...
174
			$result = "$season-$result";
175
		}
176
		return $result;
177
	}
178
179
	public function getYear() : ?int {
180
		$matches = null;
181
		if (\is_string($this->published) && \preg_match('/^(\d\d\d\d)-\d\d-\d\d.*/', $this->published, $matches) === 1) {
182
			return (int)$matches[1];
183
		} else {
184
			return null;
185
		}
186
	}
187
188
	/** @return ?int bits per second (bps) */
189
	public function getBitrate() : ?float {
190
		if (empty($this->size) || empty($this->duration)) {
191
			return null;
192
		} else {
193
			return $this->size / $this->duration * 8;
194
		}
195
	}
196
197
	public function getSuffix() : ?string {
198
		return self::mimeToSuffix($this->mimetype) ?? self::extractSuffixFromUrl($this->streamUrl);
199
	}
200
201
	private static function mimeToSuffix(?string $mime) : ?string {
202
		// a relevant subset from https://stackoverflow.com/a/53662733/4348850 wit a few additions
203
		$mime_map = [
204
			'audio/x-acc'					=> 'aac',
205
			'audio/ac3'						=> 'ac3',
206
			'audio/x-aiff'					=> 'aif',
207
			'audio/aiff'					=> 'aif',
208
			'audio/x-au'					=> 'au',
209
			'audio/x-flac'					=> 'flac',
210
			'audio/x-m4a'					=> 'm4a',
211
			'audio/mp4'						=> 'm4a',
212
			'audio/midi'					=> 'mid',
213
			'audio/mpeg'					=> 'mp3',
214
			'audio/mpg'						=> 'mp3',
215
			'audio/mpeg3'					=> 'mp3',
216
			'audio/mp3'						=> 'mp3',
217
			'audio/ogg'						=> 'ogg',
218
			'application/ogg'				=> 'ogg',
219
			'audio/x-realaudio'				=> 'ra',
220
			'audio/x-pn-realaudio'			=> 'ram',
221
			'audio/x-wav'					=> 'wav',
222
			'audio/wave'					=> 'wav',
223
			'audio/wav'						=> 'wav',
224
			'audio/x-ms-wma'				=> 'wma',
225
			'audio/m4b'						=> 'm4b',
226
			'application/vnd.apple.mpegurl'	=> 'm3u',
227
			'audio/mpegurl'					=> 'm3u',
228
		];
229
230
		return $mime_map[$mime] ?? null;
231
	}
232
233
	private static function extractSuffixFromUrl(?string $url) : ?string {
234
		if ($url === null) {
235
			return null;
236
		} else {
237
			$path = \parse_url($url, PHP_URL_PATH);
238
			$ext = \pathinfo($path, PATHINFO_EXTENSION);
239
			if (\is_string($ext) && !empty($ext)) {
240
				return $ext;
241
			} else {
242
				return null;
243
			}
244
		}
245
	}
246
}
247