Passed
Push — master ( 1723b5...d7de35 )
by Pauli
11:26
created

RadioApiController::getChannelInfo()   B

Complexity

Conditions 6
Paths 17

Size

Total Lines 29
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 25
c 1
b 0
f 0
nc 17
nop 2
dl 0
loc 29
rs 8.8977
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 2020 - 2022
11
 */
12
13
namespace OCA\Music\Controller;
14
15
use OCP\AppFramework\Controller;
16
use OCP\AppFramework\Http;
17
use OCP\AppFramework\Http\JSONResponse;
18
19
use OCP\Files\Folder;
20
use OCP\IRequest;
21
22
use OCA\Music\AppFramework\BusinessLayer\BusinessLayerException;
23
use OCA\Music\AppFramework\Core\Logger;
24
use OCA\Music\BusinessLayer\RadioStationBusinessLayer;
25
use OCA\Music\Http\ErrorResponse;
26
use OCA\Music\Utility\PlaylistFileService;
27
use OCA\Music\Utility\Util;
28
use OCA\Music\Utility\RadioMetadata;
29
30
class RadioApiController extends Controller {
31
	private $businessLayer;
32
	private $metadata;
33
	private $playlistFileService;
34
	private $userId;
35
	private $userFolder;
36
	private $logger;
37
38
	public function __construct(string $appname,
39
								IRequest $request,
40
								RadioStationBusinessLayer $businessLayer,
41
								RadioMetadata $metadata,
42
								PlaylistFileService $playlistFileService,
43
								?string $userId,
44
								?Folder $userFolder,
45
								Logger $logger) {
46
		parent::__construct($appname, $request);
47
		$this->businessLayer = $businessLayer;
48
		$this->metadata = $metadata;
49
		$this->playlistFileService = $playlistFileService;
50
		$this->userId = $userId ?? ''; // ensure non-null to satisfy Scrutinizer; the null case should happen only when the user has already logged out
51
		$this->userFolder = $userFolder;
52
		$this->logger = $logger;
53
	}
54
55
	/**
56
	 * lists all radio stations
57
	 *
58
	 * @NoAdminRequired
59
	 * @NoCSRFRequired
60
	 */
61
	public function getAll() {
62
		$stations = $this->businessLayer->findAll($this->userId);
63
		return Util::arrayMapMethod($stations, 'toApi');
64
	}
65
66
	/**
67
	 * creates a station
68
	 *
69
	 * @NoAdminRequired
70
	 * @NoCSRFRequired
71
	 */
72
	public function create($name, $streamUrl, $homeUrl) {
73
		if ($streamUrl === null) {
74
			return new ErrorResponse(Http::STATUS_BAD_REQUEST, "Mandatory argument 'streamUrl' not given");
75
		} else {
76
			$station = $this->businessLayer->create($this->userId, $name, $streamUrl, $homeUrl);
77
			return $station->toApi();
78
		}
79
	}
80
81
	/**
82
	 * deletes a station
83
	 *
84
	 * @NoAdminRequired
85
	 * @NoCSRFRequired
86
	 */
87
	public function delete(int $id) {
88
		try {
89
			$this->businessLayer->delete($id, $this->userId);
90
			return [];
91
		} catch (BusinessLayerException $ex) {
92
			return new ErrorResponse(Http::STATUS_NOT_FOUND, $ex->getMessage());
93
		}
94
	}
95
96
	/**
97
	 * get a single radio station
98
	 *
99
	 * @NoAdminRequired
100
	 * @NoCSRFRequired
101
	 */
102
	public function get(int $id) {
103
		try {
104
			$station = $this->businessLayer->find($id, $this->userId);
105
			return $station->toAPI();
106
		} catch (BusinessLayerException $ex) {
107
			return new ErrorResponse(Http::STATUS_NOT_FOUND, $ex->getMessage());
108
		}
109
	}
110
111
	/**
112
	 * update a station
113
	 *
114
	 * @NoAdminRequired
115
	 * @NoCSRFRequired
116
	 */
117
	public function update(int $id, string $name = null, string $streamUrl = null, string $homeUrl = null) {
118
		if ($name === null && $streamUrl === null && $homeUrl === null) {
119
			return new ErrorResponse(Http::STATUS_BAD_REQUEST, "at least one of the args ['name', 'streamUrl', 'homrUrl'] must be given");
120
		}
121
122
		try {
123
			$station = $this->businessLayer->find($id, $this->userId);
124
			if ($name !== null) {
125
				$station->setName($name);
126
			}
127
			if ($streamUrl !== null) {
128
				$station->setStreamUrl($streamUrl);
129
			}
130
			if ($homeUrl !== null) {
131
				$station->setHomeUrl($homeUrl);
132
			}
133
			$this->businessLayer->update($station);
134
135
			return new JSONResponse($station->toApi());
136
		} catch (BusinessLayerException $ex) {
137
			return new ErrorResponse(Http::STATUS_NOT_FOUND, $ex->getMessage());
138
		}
139
	}
140
141
	/**
142
	 * export all radio stations to a file
143
	 *
144
	 * @param string $name target file name without the file extension
145
	 * @param string $path parent folder path
146
	 * @param string $oncollision action to take on file name collision,
147
	 *								supported values:
148
	 *								- 'overwrite' The existing file will be overwritten
149
	 *								- 'keepboth' The new file is named with a suffix to make it unique
150
	 *								- 'abort' (default) The operation will fail
151
	 *
152
	 * @NoAdminRequired
153
	 * @NoCSRFRequired
154
	 */
155
	public function exportAllToFile(string $name, string $path, string $oncollision) {
156
		if ($this->userFolder === null) {
157
			// This shouldn't get actually run. The folder may be null in case the user has already logged out.
158
			// But in that case, the framework should block the execution before it reaches here.
159
			return new ErrorResponse(Http::STATUS_UNAUTHORIZED, 'no valid user folder got');
160
		}
161
		try {
162
			$exportedFilePath = $this->playlistFileService->exportRadioStationsToFile(
163
					$this->userId, $this->userFolder, $path, $name . '.m3u8', $oncollision);
164
			return new JSONResponse(['wrote_to_file' => $exportedFilePath]);
165
		} catch (\OCP\Files\NotFoundException $ex) {
166
			return new ErrorResponse(Http::STATUS_NOT_FOUND, 'folder not found');
167
		} catch (\RuntimeException $ex) {
168
			return new ErrorResponse(Http::STATUS_CONFLICT, $ex->getMessage());
169
		} catch (\OCP\Files\NotPermittedException $ex) {
170
			return new ErrorResponse(Http::STATUS_FORBIDDEN, 'user is not allowed to write to the target file');
171
		}
172
	}
173
174
	/**
175
	 * import radio stations from a file
176
	 * @param string $filePath path of the file to import
177
	 *
178
	 * @NoAdminRequired
179
	 * @NoCSRFRequired
180
	 */
181
	public function importFromFile(string $filePath) {
182
		if ($this->userFolder === null) {
183
			// This shouldn't get actually run. The folder may be null in case the user has already logged out.
184
			// But in that case, the framework should block the execution before it reaches here.
185
			return new ErrorResponse(Http::STATUS_UNAUTHORIZED, 'no valid user folder got');
186
		}
187
		try {
188
			$result = $this->playlistFileService->importRadioStationsFromFile($this->userId, $this->userFolder, $filePath);
189
			$result['stations'] = Util::arrayMapMethod($result['stations'], 'toApi');
190
			return $result;
191
		} catch (\OCP\Files\NotFoundException $ex) {
192
			return new ErrorResponse(Http::STATUS_NOT_FOUND, 'playlist file not found');
193
		} catch (\UnexpectedValueException $ex) {
194
			return new ErrorResponse(Http::STATUS_UNSUPPORTED_MEDIA_TYPE, $ex->getMessage());
195
		}
196
	}
197
198
	/**
199
	 * reset all the radio stations of the user
200
	 *
201
	 * @NoAdminRequired
202
	 * @NoCSRFRequired
203
	 */
204
	public function resetAll() {
205
		$this->businessLayer->deleteAll($this->userId);
206
		return new JSONResponse(['success' => true]);
207
	}
208
209
	/**
210
	* get radio metadata from stream
211
	*
212
	* @NoAdminRequired
213
	* @NoCSRFRequired
214
	*/
215
	public function getChannelInfo(int $id, ?string $type=null) {
216
		try {
217
			$station = $this->businessLayer->find($id, $this->userId);
218
			$streamUrl = $station->getStreamUrl();
219
220
			switch ($type) {
221
				case 'icy':
222
					$metadata = $this->metadata->readIcyMetadata($streamUrl, 1, 1);
223
					break;
224
				case 'shoutcast-v1':
225
					$metadata = $this->metadata->readShoutcastV1Metadata($streamUrl);
226
					break;
227
				case 'shoutcast-v2':
228
					$metadata = $this->metadata->readShoutcastV2Metadata($streamUrl);
229
					break;
230
				case 'icecast':
231
					$metadata = $this->metadata->readIcecastMetadata($streamUrl);
232
					break;
233
				default:
234
					$metadata = $this->metadata->readIcyMetadata($streamUrl, 1, 1)
235
							?? $this->metadata->readShoutcastV2Metadata($streamUrl)
236
							?? $this->metadata->readIcecastMetadata($streamUrl)
237
							?? $this->metadata->readShoutcastV1Metadata($streamUrl);
238
					break;
239
			}
240
241
			return new JSONResponse($metadata);
242
		} catch (BusinessLayerException $ex) {
243
			return new ErrorResponse(Http::STATUS_NOT_FOUND, $ex->getMessage());
244
		}
245
	}
246
}
247