Passed
Push — master ( 124be7...f358a5 )
by Pauli
03:35 queued 16s
created

PodcastApiController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 7
nc 1
nop 8
dl 0
loc 15
rs 10
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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 - 2025
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
use OCP\AppFramework\Http\RedirectResponse;
19
20
use OCP\Files\IRootFolder;
21
22
use OCP\IConfig;
23
use OCP\IRequest;
24
use OCP\IURLGenerator;
25
26
use OCA\Music\AppFramework\Core\Logger;
27
use OCA\Music\AppFramework\Utility\FileExistsException;
28
use OCA\Music\Http\ErrorResponse;
29
use OCA\Music\Http\RelayStreamResponse;
30
use OCA\Music\Utility\PodcastService;
31
32
class PodcastApiController extends Controller {
33
	private IConfig $config;
34
	private IURLGenerator $urlGenerator;
35
	private IRootFolder $rootFolder;
36
	private PodcastService $podcastService;
37
	private string $userId;
38
	private Logger $logger;
39
40
	public function __construct(string $appname,
41
								IRequest $request,
42
								IConfig $config,
43
								IURLGenerator $urlGenerator,
44
								IRootFolder $rootFolder,
45
								PodcastService $podcastService,
46
								?string $userId,
47
								Logger $logger) {
48
		parent::__construct($appname, $request);
49
		$this->config = $config;
50
		$this->urlGenerator = $urlGenerator;
51
		$this->rootFolder = $rootFolder;
52
		$this->podcastService = $podcastService;
53
		$this->userId = $userId ?? ''; // ensure non-null to satisfy Scrutinizer; the null case should happen only when the user has already logged out
54
		$this->logger = $logger;
55
	}
56
57
	/**
58
	 * lists all podcasts
59
	 *
60
	 * @NoAdminRequired
61
	 * @NoCSRFRequired
62
	 */
63
	public function getAll() {
64
		$channels = $this->podcastService->getAllChannels($this->userId, /*$includeEpisodes=*/ true);
65
		return \array_map(fn($c) => $c->toApi($this->urlGenerator), $channels);
66
	}
67
68
	/**
69
	 * add a followed podcast
70
	 *
71
	 * @NoAdminRequired
72
	 * @NoCSRFRequired
73
	 */
74
	public function subscribe(?string $url) {
75
		if ($url === null) {
76
			return new ErrorResponse(Http::STATUS_BAD_REQUEST, "Mandatory argument 'url' not given");
77
		}
78
79
		$result = $this->podcastService->subscribe($url, $this->userId);
80
81
		switch ($result['status']) {
82
			case PodcastService::STATUS_OK:
83
				return $result['channel']->toApi($this->urlGenerator);
84
			case PodcastService::STATUS_INVALID_URL:
85
				return new ErrorResponse(Http::STATUS_BAD_REQUEST, "Invalid URL $url");
86
			case PodcastService::STATUS_INVALID_RSS:
87
				return new ErrorResponse(Http::STATUS_BAD_REQUEST, "The document at URL $url is not a valid podcast RSS feed");
88
			case PodcastService::STATUS_ALREADY_EXISTS:
89
				return new ErrorResponse(Http::STATUS_CONFLICT, 'User already has this podcast channel subscribed');
90
			default:
91
				return new ErrorResponse(Http::STATUS_INTERNAL_SERVER_ERROR, "Unexpected status code {$result['status']}");
92
		}
93
	}
94
95
	/**
96
	 * deletes a station
97
	 *
98
	 * @NoAdminRequired
99
	 * @NoCSRFRequired
100
	 */
101
	public function unsubscribe(int $id) {
102
		$status = $this->podcastService->unsubscribe($id, $this->userId);
103
104
		switch ($status) {
105
			case PodcastService::STATUS_OK:
106
				return new JSONResponse(['success' => true]);
107
			case PodcastService::STATUS_NOT_FOUND:
108
				return new ErrorResponse(Http::STATUS_NOT_FOUND);
109
			default:
110
				return new ErrorResponse(Http::STATUS_INTERNAL_SERVER_ERROR, "Unexpected status code $status");
111
		}
112
	}
113
114
	/**
115
	 * get a single podcast channel
116
	 *
117
	 * @NoAdminRequired
118
	 * @NoCSRFRequired
119
	 */
120
	public function get(int $id) {
121
		$channel = $this->podcastService->getChannel($id, $this->userId, /*includeEpisodes=*/ true);
122
123
		if ($channel !== null) {
124
			return $channel->toApi($this->urlGenerator);
125
		} else {
126
			return new ErrorResponse(Http::STATUS_NOT_FOUND);
127
		}
128
	}
129
130
	/**
131
	 * get details for a podcast channel
132
	 *
133
	 * @NoAdminRequired
134
	 * @NoCSRFRequired
135
	 */
136
	public function channelDetails(int $id) {
137
		$channel = $this->podcastService->getChannel($id, $this->userId, /*includeEpisodes=*/ false);
138
139
		if ($channel !== null) {
140
			return $channel->detailsToApi($this->urlGenerator);
141
		} else {
142
			return new ErrorResponse(Http::STATUS_NOT_FOUND);
143
		}
144
	}
145
146
	/**
147
	 * get details for a podcast episode
148
	 *
149
	 * @NoAdminRequired
150
	 * @NoCSRFRequired
151
	 */
152
	public function episodeDetails(int $id) {
153
		$episode = $this->podcastService->getEpisode($id, $this->userId);
154
155
		if ($episode !== null) {
156
			return $episode->detailsToApi();
157
		} else {
158
			return new ErrorResponse(Http::STATUS_NOT_FOUND);
159
		}
160
	}
161
162
	/**
163
	 * get audio stream for a podcast episode
164
	 *
165
	 * @NoAdminRequired
166
	 * @NoCSRFRequired
167
	 */
168
	public function episodeStream(int $id) {
169
		$episode = $this->podcastService->getEpisode($id, $this->userId);
170
171
		if ($episode !== null) {
172
			$streamUrl = $episode->getStreamUrl();
173
			if ($streamUrl === null) {
174
				return new ErrorResponse(Http::STATUS_NOT_FOUND, "The podcast episode $id has no stream URL");
175
			} elseif ($this->config->getSystemValue('music.relay_podcast_stream', true)) {
176
				return new RelayStreamResponse($streamUrl);
177
			} else {
178
				return new RedirectResponse($streamUrl);
179
			}
180
		} else {
181
			return new ErrorResponse(Http::STATUS_NOT_FOUND);
182
		}
183
	}
184
185
	/**
186
	 * check a single channel for updates
187
	 * @param int $id Channel ID
188
	 * @param string|null $prevHash Previous content hash known by the client. If given, the result will tell
189
	 *								if the channel content has updated from this state. If omitted, the result
190
	 *								will tell if the channel changed from its previous server-known state.
191
	 *
192
	 * @NoAdminRequired
193
	 * @NoCSRFRequired
194
	 */
195
	public function updateChannel(int $id, ?string $prevHash) {
196
		$updateResult = $this->podcastService->updateChannel($id, $this->userId, $prevHash);
197
198
		$response = [
199
			'success' => ($updateResult['status'] === PodcastService::STATUS_OK),
200
			'updated' => $updateResult['updated']
201
		];
202
		if ($updateResult['updated']) {
203
			$response['channel'] = $updateResult['channel']->toApi($this->urlGenerator);
204
		}
205
206
		return new JSONResponse($response);
207
	}
208
209
	/**
210
	 * reset all the subscribed podcasts of the user
211
	 *
212
	 * @NoAdminRequired
213
	 * @NoCSRFRequired
214
	 */
215
	public function resetAll() {
216
		$this->podcastService->resetAll($this->userId);
217
		return new JSONResponse(['success' => true]);
218
	}
219
220
	/**
221
	 * export all podcast channels to an OPML file
222
	 *
223
	 * @param string $name target file name
224
	 * @param string $path parent folder path
225
	 * @param string $oncollision action to take on file name collision,
226
	 *								supported values:
227
	 *								- 'overwrite' The existing file will be overwritten
228
	 *								- 'keepboth' The new file is named with a suffix to make it unique
229
	 *								- 'abort' (default) The operation will fail
230
	 *
231
	 * @NoAdminRequired
232
	 * @NoCSRFRequired
233
	 */
234
	public function exportAllToFile(string $name, string $path, string $oncollision) {
235
		try {
236
			$userFolder = $this->rootFolder->getUserFolder($this->userId);
237
			$exportedFilePath = $this->podcastService->exportToFile(
238
					$this->userId, $userFolder, $path, $name, $oncollision);
239
			return new JSONResponse(['wrote_to_file' => $exportedFilePath]);
240
		} catch (\OCP\Files\NotFoundException $ex) {
241
			return new ErrorResponse(Http::STATUS_NOT_FOUND, 'folder not found');
242
		} catch (FileExistsException $ex) {
243
			return new ErrorResponse(Http::STATUS_CONFLICT, 'file already exists', ['path' => $ex->getPath(), 'suggested_name' => $ex->getAltName()]);
244
		} catch (\OCP\Files\NotPermittedException $ex) {
245
			return new ErrorResponse(Http::STATUS_FORBIDDEN, 'user is not allowed to write to the target file');
246
		}
247
	}
248
249
	/**
250
	 * parse an OPML file and return list of contained channels
251
	 *
252
	 * @param string $filePath path of the file to parse
253
	 *
254
	 * @NoAdminRequired
255
	 * @NoCSRFRequired
256
	 */
257
	public function parseListFile(string $filePath) {
258
		try {
259
			$userFolder = $this->rootFolder->getUserFolder($this->userId);
260
			$list = $this->podcastService->parseOpml($userFolder, $filePath);
261
			return $list;
262
		} catch (\UnexpectedValueException $ex) {
263
			return new ErrorResponse(Http::STATUS_UNSUPPORTED_MEDIA_TYPE, $ex->getMessage());
264
		}
265
	}
266
}
267