@omelhorsite/sdk 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -130
- package/dist/index.js +4800 -610
- package/dist/types/client.d.ts +60 -3
- package/dist/types/http.d.ts +444 -19
- package/dist/types/index.d.ts +5 -1
- package/dist/types/internal/attachments.d.ts +17 -0
- package/dist/types/internal/helpers.d.ts +9 -0
- package/dist/types/listing.d.ts +90 -0
- package/dist/types/resources/account.d.ts +71 -5
- package/dist/types/resources/admin/authorizedApplications.d.ts +133 -0
- package/dist/types/resources/admin/chests.d.ts +51 -0
- package/dist/types/resources/admin/eventAlerts.d.ts +60 -0
- package/dist/types/resources/admin/identities.d.ts +99 -0
- package/dist/types/resources/admin/index.d.ts +77 -0
- package/dist/types/resources/admin/jobs.d.ts +97 -0
- package/dist/types/resources/admin/myOauthApplications.d.ts +282 -0
- package/dist/types/resources/admin/notepads.d.ts +49 -0
- package/dist/types/resources/admin/oauthApplications.d.ts +309 -0
- package/dist/types/resources/admin/quotas.d.ts +114 -0
- package/dist/types/resources/admin/shortLinks.d.ts +201 -0
- package/dist/types/resources/admin/types.d.ts +222 -0
- package/dist/types/resources/admin/vocalSeparations.d.ts +98 -0
- package/dist/types/resources/admin.d.ts +1841 -0
- package/dist/types/resources/auth/index.d.ts +39 -0
- package/dist/types/resources/auth/passkeys.d.ts +652 -0
- package/dist/types/resources/auth/sessions.d.ts +844 -0
- package/dist/types/resources/chests.d.ts +54 -3
- package/dist/types/resources/content/analysis.d.ts +62 -0
- package/dist/types/resources/content/blogs.d.ts +471 -0
- package/dist/types/resources/content/feedbacks.d.ts +263 -0
- package/dist/types/resources/content/index.d.ts +52 -0
- package/dist/types/resources/content/intel/articles.d.ts +230 -0
- package/dist/types/resources/content/intel/config.d.ts +135 -0
- package/dist/types/resources/content/intel/index.d.ts +53 -0
- package/dist/types/resources/content/intel/items.d.ts +91 -0
- package/dist/types/resources/content/intel/reports.d.ts +108 -0
- package/dist/types/resources/content/intel/scripts.d.ts +152 -0
- package/dist/types/resources/content/intel/sources.d.ts +236 -0
- package/dist/types/resources/content/intel/stats.d.ts +105 -0
- package/dist/types/resources/content/intel/types.d.ts +86 -0
- package/dist/types/resources/content/jokes.d.ts +93 -0
- package/dist/types/resources/content/notifications.d.ts +147 -0
- package/dist/types/resources/content/serviceUsages.d.ts +73 -0
- package/dist/types/resources/content/servicesStatus.d.ts +194 -0
- package/dist/types/resources/content/siteConfig.d.ts +43 -0
- package/dist/types/resources/content/spaceInvaders.d.ts +154 -0
- package/dist/types/resources/content.d.ts +2775 -0
- package/dist/types/resources/dynamicQrs.d.ts +39 -3
- package/dist/types/resources/forms.d.ts +176 -35
- package/dist/types/resources/index.d.ts +19 -8
- package/dist/types/resources/ipLookup.d.ts +20 -4
- package/dist/types/resources/jobs.d.ts +69 -25
- package/dist/types/resources/library/annotations.d.ts +162 -0
- package/dist/types/resources/library/books.d.ts +534 -0
- package/dist/types/resources/library/chat.d.ts +259 -0
- package/dist/types/resources/library/index.d.ts +39 -0
- package/dist/types/resources/library/shelves.d.ts +237 -0
- package/dist/types/resources/library/types.d.ts +120 -0
- package/dist/types/resources/library.d.ts +1433 -0
- package/dist/types/resources/linkTrees.d.ts +142 -30
- package/dist/types/resources/media.d.ts +351 -0
- package/dist/types/resources/movies/addons.d.ts +388 -0
- package/dist/types/resources/movies/collections.d.ts +352 -0
- package/dist/types/resources/movies/index.d.ts +29 -0
- package/dist/types/resources/movies/types.d.ts +50 -0
- package/dist/types/resources/movies/watchProgress.d.ts +277 -0
- package/dist/types/resources/movies.d.ts +1160 -0
- package/dist/types/resources/music/artists.d.ts +1068 -0
- package/dist/types/resources/music/imports.d.ts +941 -0
- package/dist/types/resources/music/index.d.ts +61 -0
- package/dist/types/resources/music/playlists.d.ts +1027 -0
- package/dist/types/resources/music/social.d.ts +1132 -0
- package/dist/types/resources/music/songs.d.ts +1167 -0
- package/dist/types/resources/notepads.d.ts +4 -1
- package/dist/types/resources/quotas.d.ts +7 -1
- package/dist/types/resources/realtime.d.ts +855 -0
- package/dist/types/resources/shortLinks.d.ts +49 -5
- package/dist/types/resources/social/groupChats.d.ts +458 -0
- package/dist/types/resources/social/index.d.ts +19 -0
- package/dist/types/resources/social/messages.d.ts +424 -0
- package/dist/types/resources/social/relationships.d.ts +322 -0
- package/dist/types/resources/social/types.d.ts +37 -0
- package/dist/types/resources/social.d.ts +1348 -0
- package/dist/types/resources/storage/upload.d.ts +158 -11
- package/dist/types/resources/storage.d.ts +111 -27
- package/dist/types/resources/tickets.d.ts +101 -13
- package/dist/types/resources/tools/backgroundRemoval.d.ts +18 -3
- package/dist/types/resources/tools/captions.d.ts +448 -21
- package/dist/types/resources/tools/downloader.d.ts +21 -0
- package/dist/types/resources/tools/index.d.ts +57 -15
- package/dist/types/resources/tools/jumpstyle.d.ts +50 -17
- package/dist/types/resources/tools/transcription.d.ts +35 -13
- package/dist/types/resources/tools/upscale.d.ts +23 -3
- package/dist/types/resources/tools/vocalSeparation.d.ts +30 -13
- package/dist/types/types.d.ts +249 -17
- package/package.json +2 -1
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/** The `movies.watchProgress` area: how far into each title the user got, and what "Continuar a ver" is built from. */
|
|
2
|
+
import { Resource } from "../../http";
|
|
3
|
+
import type { BaseRecord, Id, RequestOptions, Timestamp } from "../../types";
|
|
4
|
+
import type { MovieType } from "./types";
|
|
5
|
+
/**
|
|
6
|
+
* Fraction of the runtime that counts as watched when the server derives
|
|
7
|
+
* `finished` itself.
|
|
8
|
+
*/
|
|
9
|
+
export declare const MOVIE_WATCH_FINISHED_THRESHOLD = 0.95;
|
|
10
|
+
/**
|
|
11
|
+
* Rows `POST /movie_watch_progresses/bulk` will accept in one call.
|
|
12
|
+
*
|
|
13
|
+
* The server keeps the first 200 entries: the rest are dropped in SILENCE and
|
|
14
|
+
* the call still answers `200` with the 200 rows it did save, so a client
|
|
15
|
+
* marking a 300-episode series watched would believe it succeeded.
|
|
16
|
+
* {@link MovieWatchProgressesNamespace.saveMany} raises instead.
|
|
17
|
+
*/
|
|
18
|
+
export declare const MOVIE_WATCH_BULK_LIMIT = 200;
|
|
19
|
+
/**
|
|
20
|
+
* Rows `GET /movie_watch_progresses` returns, at most. Hard-coded, with no
|
|
21
|
+
* paging and no way to reach row 501 - see
|
|
22
|
+
* {@link MovieWatchProgressesNamespace.list}.
|
|
23
|
+
*/
|
|
24
|
+
export declare const MOVIE_WATCH_LIST_LIMIT = 500;
|
|
25
|
+
/** One title-or-episode the user has started, and how far in they got. */
|
|
26
|
+
export interface MovieWatchProgress extends BaseRecord {
|
|
27
|
+
readonly user_id: Id;
|
|
28
|
+
readonly movie_type: MovieType;
|
|
29
|
+
/** The addon's id for the TITLE. A series shares it across every episode. */
|
|
30
|
+
readonly movie_id: string;
|
|
31
|
+
/**
|
|
32
|
+
* The addon's id for the specific playable. For a film this is usually the
|
|
33
|
+
* same string as `movie_id`; for a series it is the episode.
|
|
34
|
+
*
|
|
35
|
+
* `(user_id, movie_id, video_id)` is the row's identity and carries a unique
|
|
36
|
+
* index.
|
|
37
|
+
*/
|
|
38
|
+
readonly video_id: string;
|
|
39
|
+
readonly season: number | null;
|
|
40
|
+
readonly episode: number | null;
|
|
41
|
+
readonly name: string | null;
|
|
42
|
+
readonly episode_title: string | null;
|
|
43
|
+
readonly poster: string | null;
|
|
44
|
+
/** Seconds into the playable. A float, `NOT NULL DEFAULT 0.0`. */
|
|
45
|
+
readonly position: number;
|
|
46
|
+
/** Runtime in seconds, as the player measured it. `0` when unknown. */
|
|
47
|
+
readonly duration: number;
|
|
48
|
+
/** See {@link MovieWatchProgressInput.finished} for how this gets its value. */
|
|
49
|
+
readonly finished: boolean;
|
|
50
|
+
/** What "Continuar a ver" sorts on, descending. */
|
|
51
|
+
readonly last_watched_at: Timestamp;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* One row to upsert, through {@link MovieWatchProgressesNamespace.save} or
|
|
55
|
+
* {@link MovieWatchProgressesNamespace.saveMany}.
|
|
56
|
+
*
|
|
57
|
+
* `movie_id`, `video_id` and `movie_type` are checked up front and a missing
|
|
58
|
+
* one is `400 "movie_id, video_id, movie_type are required"`. Everything else
|
|
59
|
+
* is optional, but read the notes on `finished` and `last_watched_at` before
|
|
60
|
+
* leaving them out: both are cases where omitting the field does something
|
|
61
|
+
* other than "leave it as it was".
|
|
62
|
+
*/
|
|
63
|
+
export interface MovieWatchProgressInput {
|
|
64
|
+
readonly movie_type: MovieType;
|
|
65
|
+
/** Identity, with `video_id`. Changing `movie_type` does NOT make a new row. */
|
|
66
|
+
readonly movie_id: string;
|
|
67
|
+
/** Identity, with `movie_id`. */
|
|
68
|
+
readonly video_id: string;
|
|
69
|
+
readonly season?: number | null;
|
|
70
|
+
readonly episode?: number | null;
|
|
71
|
+
readonly name?: string | null;
|
|
72
|
+
readonly episode_title?: string | null;
|
|
73
|
+
readonly poster?: string | null;
|
|
74
|
+
/** Seconds into the playable. Negative is `400 "Position must be greater than or equal to 0"`. */
|
|
75
|
+
readonly position?: number;
|
|
76
|
+
/** Runtime in seconds. Negative is a `400` the same way. */
|
|
77
|
+
readonly duration?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Three states, not two.
|
|
80
|
+
*
|
|
81
|
+
* - **omitted** - the server derives it:
|
|
82
|
+
* `finished = position >= duration * 0.95`, and if `duration <= 0` it
|
|
83
|
+
* leaves the stored flag ALONE. This is what a playback tick should send.
|
|
84
|
+
* - **`true` / `false`** - the user said so. The value is written as given,
|
|
85
|
+
* even when `duration <= 0`.
|
|
86
|
+
* - **`null`** - dropped before it is read, and therefore identical to
|
|
87
|
+
* omitting it.
|
|
88
|
+
*
|
|
89
|
+
* That last branch is the bug that was fixed here. "Marcar como nao visto"
|
|
90
|
+
* sends `position: 0, duration: 0`, which used to hit the `duration <= 0`
|
|
91
|
+
* guard and leave `finished` true forever. Sending an explicit `false` is
|
|
92
|
+
* what makes it stick - sending `null`, or leaving the key out, still does
|
|
93
|
+
* nothing at all. {@link MovieWatchProgressesNamespace.setWatched} spells it
|
|
94
|
+
* out so you cannot get this wrong by accident.
|
|
95
|
+
*
|
|
96
|
+
* An explicit value only pins the flag for THAT save. The next tick that
|
|
97
|
+
* omits `finished` goes back to deriving it from the position.
|
|
98
|
+
*/
|
|
99
|
+
readonly finished?: boolean;
|
|
100
|
+
/**
|
|
101
|
+
* When the user last watched, ISO-8601.
|
|
102
|
+
*
|
|
103
|
+
* SEND IT ON EVERY CALL. It is only defaulted when the row is first
|
|
104
|
+
* created - so an upsert that omits it keeps whatever timestamp was there
|
|
105
|
+
* when the row was first created. The Continue Watching list is ordered by
|
|
106
|
+
* it, descending, so omitting this pins the title where it first appeared
|
|
107
|
+
* and it never moves back to the front. `new Date().toISOString()` at the
|
|
108
|
+
* call site is the whole fix.
|
|
109
|
+
*/
|
|
110
|
+
readonly last_watched_at?: Timestamp;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The same arithmetic the server uses, for a client that wants to render a
|
|
114
|
+
* "watched" tick before the round trip lands.
|
|
115
|
+
*
|
|
116
|
+
* Returns `null` - not `false` - when `duration` is zero or negative, because
|
|
117
|
+
* that is precisely the case where the server declines to decide and leaves the
|
|
118
|
+
* stored flag untouched. Treating that as `false` is how an optimistic UI ends
|
|
119
|
+
* up un-ticking something the server still considers watched.
|
|
120
|
+
*/
|
|
121
|
+
export declare function movieWatchFinished(position: number, duration: number): boolean | null;
|
|
122
|
+
/**
|
|
123
|
+
* The `movies.watchProgress` namespace: how far into each title the user got,
|
|
124
|
+
* and what "Continuar a ver" is built from.
|
|
125
|
+
*
|
|
126
|
+
* This family is the odd one out: none of the list DSL applies, `create`
|
|
127
|
+
* answers `200`, and there is no `show` and no `update`. Everything is an
|
|
128
|
+
* upsert keyed on `(user, movie_id, video_id)`.
|
|
129
|
+
*/
|
|
130
|
+
export declare class MovieWatchProgressesNamespace extends Resource {
|
|
131
|
+
/**
|
|
132
|
+
* `GET /movie_watch_progresses` - the caller's rows, newest first.
|
|
133
|
+
*
|
|
134
|
+
* Not paginated, and not filterable. The query string is ignored entirely
|
|
135
|
+
* and the answer is always the 500 most recently watched rows, so:
|
|
136
|
+
*
|
|
137
|
+
* - there is NO way to reach row 501. A user with more history than that
|
|
138
|
+
* simply cannot read the tail through this API;
|
|
139
|
+
* - `search[...]` / `exact_search[...]` / `modifiers[...]` are not rejected,
|
|
140
|
+
* they are silently ignored - this index never raises
|
|
141
|
+
* `400 "Unknown search filter"`. A client that thinks it asked for one
|
|
142
|
+
* title gets all 500 rows and, if it trusts the filter, the wrong answer.
|
|
143
|
+
* Filter client-side; that is why this method takes no params;
|
|
144
|
+
* - there is no `ETag` either.
|
|
145
|
+
*
|
|
146
|
+
* Finished rows are included. Build "Continuar a ver" by dropping
|
|
147
|
+
* `finished === true` yourself, and remember an episode can be finished while
|
|
148
|
+
* its series is not.
|
|
149
|
+
*
|
|
150
|
+
* @throws {OmsApiError} 401 for an anonymous caller, 403 for an OAuth token.
|
|
151
|
+
*/
|
|
152
|
+
list(options?: RequestOptions): Promise<MovieWatchProgress[]>;
|
|
153
|
+
/**
|
|
154
|
+
* `POST /movie_watch_progresses` - upserts ONE row. **`200`, not `201`.**
|
|
155
|
+
*
|
|
156
|
+
* This is the playback tick: call it while something is playing, throttled
|
|
157
|
+
* by the player to whatever interval you like. The server finds by
|
|
158
|
+
* `(user, movie_id, video_id)` and updates in place, so it is idempotent and
|
|
159
|
+
* safe to repeat - the only create in this file where opting into
|
|
160
|
+
* `options.retry` is a good idea rather than a way to make duplicates.
|
|
161
|
+
* (The transport does not replay non-safe methods unless you ask.)
|
|
162
|
+
*
|
|
163
|
+
* `movie_type` is NOT part of the key. Posting the same `(movie_id,
|
|
164
|
+
* video_id)` with a different type rewrites the existing row's type rather
|
|
165
|
+
* than creating a second one.
|
|
166
|
+
*
|
|
167
|
+
* Read {@link MovieWatchProgressInput.finished} and
|
|
168
|
+
* {@link MovieWatchProgressInput.last_watched_at} before using this: a tick
|
|
169
|
+
* that omits `last_watched_at` does not move the title up the list, and
|
|
170
|
+
* `finished` has three states rather than two. A `finished` of `null` is
|
|
171
|
+
* dropped by this method rather than sent, matching what the server would do
|
|
172
|
+
* with it, so `finished` reaches the wire only as a real boolean.
|
|
173
|
+
*
|
|
174
|
+
* Use {@link saveMany} instead when you have more than a couple of rows -
|
|
175
|
+
* see its docs for why one request is not just faster but differently shaped.
|
|
176
|
+
*
|
|
177
|
+
* @throws {OmsError} `invalid_request` when `movie_id`, `video_id` or
|
|
178
|
+
* `movie_type` is blank.
|
|
179
|
+
* @throws {OmsApiError} 400 `"movie_id, video_id, movie_type are required"`
|
|
180
|
+
* from the server's own check, or a validation sentence such as
|
|
181
|
+
* `"Position must be greater than or equal to 0"`.
|
|
182
|
+
*/
|
|
183
|
+
save(input: MovieWatchProgressInput, options?: RequestOptions): Promise<MovieWatchProgress>;
|
|
184
|
+
/**
|
|
185
|
+
* `POST /movie_watch_progresses/bulk` - upserts up to
|
|
186
|
+
* {@link MOVIE_WATCH_BULK_LIMIT} rows in ONE request and ONE transaction.
|
|
187
|
+
* `200`, with the saved rows in the order sent.
|
|
188
|
+
*
|
|
189
|
+
* ## Why this endpoint exists, and when to reach for it
|
|
190
|
+
*
|
|
191
|
+
* "Marcar temporada como vista" is one row per episode. A 24-episode season
|
|
192
|
+
* through {@link save} is 24 POSTs: 24 round trips, 24 chances for one to
|
|
193
|
+
* fail and leave the season half-marked, and 24 requests against the
|
|
194
|
+
* caller's 600/min ceiling. This collapses it into one.
|
|
195
|
+
*
|
|
196
|
+
* The transaction is the other half of the point, and it cuts both ways:
|
|
197
|
+
*
|
|
198
|
+
* - **use {@link saveMany}** for a statement about several rows at once that
|
|
199
|
+
* must be all-or-nothing - mark a season watched or unwatched, restore a
|
|
200
|
+
* device's offline queue, seed history on first sync. If any entry is
|
|
201
|
+
* invalid the whole batch rolls back and answers `400`, so you never end up
|
|
202
|
+
* with episodes 1-9 marked and 10-24 not.
|
|
203
|
+
* - **use {@link save}** for the continuous playback tick. One row, and a
|
|
204
|
+
* failure costs one tick that the next one will overwrite anyway. Batching
|
|
205
|
+
* ticks would trade a lost second for a lost minute.
|
|
206
|
+
*
|
|
207
|
+
* ## The silent-truncation trap this method closes
|
|
208
|
+
*
|
|
209
|
+
* The server keeps the first 200 entries: the rest are dropped without a
|
|
210
|
+
* word and the response is a cheerful `200` listing the 200 that were saved.
|
|
211
|
+
* A 300-episode batch would look like it worked. This method raises before
|
|
212
|
+
* sending instead, so split the work yourself - and note that separate
|
|
213
|
+
* batches are separate transactions, so a split is no longer atomic end to
|
|
214
|
+
* end.
|
|
215
|
+
*
|
|
216
|
+
* Retrying the whole batch is safe: every entry is the same upsert
|
|
217
|
+
* {@link save} performs.
|
|
218
|
+
*
|
|
219
|
+
* @throws {OmsError} `invalid_request` for an empty list, for more than
|
|
220
|
+
* {@link MOVIE_WATCH_BULK_LIMIT} entries, or for an entry missing
|
|
221
|
+
* `movie_id`, `video_id` or `movie_type`.
|
|
222
|
+
* @throws {OmsApiError} 400 `"items is required"` for an empty list that got
|
|
223
|
+
* through, or the first failing entry's validation sentence - and in that
|
|
224
|
+
* case NOTHING was saved.
|
|
225
|
+
*/
|
|
226
|
+
saveMany(inputs: readonly MovieWatchProgressInput[], options?: RequestOptions): Promise<MovieWatchProgress[]>;
|
|
227
|
+
/**
|
|
228
|
+
* Marks one playable watched or unwatched, explicitly.
|
|
229
|
+
*
|
|
230
|
+
* Sugar over {@link save} that exists because this is the exact call the
|
|
231
|
+
* fixed bug was about. Marking something UNWATCHED sends `position: 0,
|
|
232
|
+
* duration: 0`, and with `finished` absent the server's `duration <= 0`
|
|
233
|
+
* guard leaves the stored flag as it was - so the tick never came off.
|
|
234
|
+
* Passing the boolean outright skips that guard and writes what you said.
|
|
235
|
+
*
|
|
236
|
+
* Defaults `position` and `duration` to `0` when you do not supply them,
|
|
237
|
+
* which is right for "mark unwatched" and harmless for "mark watched"
|
|
238
|
+
* precisely because the explicit flag stops the server deriving anything from
|
|
239
|
+
* them. Supply the real numbers when you have them; the progress bar reads
|
|
240
|
+
* them.
|
|
241
|
+
*
|
|
242
|
+
* `last_watched_at` is still yours to send, and still matters: marking an
|
|
243
|
+
* episode watched without one leaves the series where it was in the list.
|
|
244
|
+
*/
|
|
245
|
+
setWatched(input: Omit<MovieWatchProgressInput, "finished">, watched: boolean, options?: RequestOptions): Promise<MovieWatchProgress>;
|
|
246
|
+
/**
|
|
247
|
+
* `DELETE /movie_watch_progresses/:id` - forgets ONE playable. `204`, empty
|
|
248
|
+
* body.
|
|
249
|
+
*
|
|
250
|
+
* The id is the progress row's primary key, which for a series is one
|
|
251
|
+
* episode. To forget a whole title use {@link forgetMovie}.
|
|
252
|
+
*
|
|
253
|
+
* @throws {OmsApiError} 404 `"Resource not found"` for a row that is not
|
|
254
|
+
* yours.
|
|
255
|
+
*/
|
|
256
|
+
delete(id: Id, options?: RequestOptions): Promise<void>;
|
|
257
|
+
/**
|
|
258
|
+
* `DELETE /movie_watch_progresses/for_movie?movie_id=...` - forgets EVERY row
|
|
259
|
+
* for a title. `204`, empty body.
|
|
260
|
+
*
|
|
261
|
+
* This is the "remover de Continuar a ver" button. For a series it destroys
|
|
262
|
+
* the progress of every episode, not just the one on screen; there is no
|
|
263
|
+
* per-season form and no undo.
|
|
264
|
+
*
|
|
265
|
+
* Note the verb: despite the `?movie_id=` query string this is a **DELETE**,
|
|
266
|
+
* not a read. It is a collection route, so the `movie_id` is the addon's id
|
|
267
|
+
* for the title (an IMDb id, say), never a `movie_watch_progresses` primary
|
|
268
|
+
* key.
|
|
269
|
+
*
|
|
270
|
+
* `204` even when nothing matched, so the answer does not tell you whether
|
|
271
|
+
* anything was there.
|
|
272
|
+
*
|
|
273
|
+
* @throws {OmsError} `invalid_request` for a blank id.
|
|
274
|
+
* @throws {OmsApiError} 400 `"movie_id is required"`.
|
|
275
|
+
*/
|
|
276
|
+
forgetMovie(movieId: string, options?: RequestOptions): Promise<void>;
|
|
277
|
+
}
|