001/*
002 *   Copyright 2024 Vonage
003 *
004 *   Licensed under the Apache License, Version 2.0 (the "License");
005 *   you may not use this file except in compliance with the License.
006 *   You may obtain a copy of the License at
007 *
008 *        http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *   Unless required by applicable law or agreed to in writing, software
011 *   distributed under the License is distributed on an "AS IS" BASIS,
012 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *   See the License for the specific language governing permissions and
014 *   limitations under the License.
015 */
016package com.vonage.client.video;
017
018import com.fasterxml.jackson.annotation.JsonIgnore;
019import com.fasterxml.jackson.annotation.JsonProperty;
020import com.vonage.client.Jsonable;
021import java.net.URI;
022import java.time.Duration;
023
024/**
025* Represents an archive of a video session.
026*/
027public class Archive extends StreamComposition {
028    private Long size;
029    @JsonProperty("duration") private Integer duration;
030    private String name, reason, multiArchiveTag;
031    private URI url;
032    private ArchiveStatus status;
033    private OutputMode outputMode;
034
035    protected Archive() {
036    }
037
038    protected Archive(Builder builder) {
039        if ((sessionId = builder.sessionId) == null || sessionId.isEmpty()) {
040            throw new IllegalArgumentException("Session ID is required.");
041        }
042        layout = builder.layout;
043        if ((outputMode = builder.outputMode) != OutputMode.COMPOSED && layout != null) {
044            throw new IllegalStateException("Layout can only be applied to composed archives.");
045        }
046        name = builder.name;
047        multiArchiveTag = builder.multiArchiveTag;
048        hasAudio = builder.hasAudio;
049        hasVideo = builder.hasVideo;
050        resolution = builder.resolution;
051        streamMode = builder.streamMode;
052        if ((maxBitrate = builder.maxBitrate) != null && (maxBitrate < 100000 || maxBitrate > 6000000)) {
053            throw new IllegalArgumentException("Maximum bitrate must be between 100000 and 6000000.");
054        }
055    }
056
057    /**
058     * The duration of the recording, in seconds.
059     *
060     * @return The duration as an integer.
061     */
062    @JsonProperty("duration")
063    public Integer getDurationSeconds() {
064        return duration;
065    }
066
067    /**
068     * The duration of the recording (precision in seconds).
069     *
070     * @return The duration.
071     */
072    @JsonIgnore
073    public Duration getDuration() {
074        return duration != null ? Duration.ofSeconds(duration) : null;
075    }
076
077    /**
078     * The name of the archive.
079     *
080     * @return The archive name.
081     */
082    @JsonProperty("name")
083    public String getName() {
084        return name;
085    }
086
087    /**
088     * For archives with {@link ArchiveStatus#STOPPED} or {@link ArchiveStatus#FAILED}, this string
089     * describes the reason the archive stopped (such as "maximum duration exceeded") or failed.
090     *
091     * @return The failure reason text, or {@code null} if not applicable.
092     */
093    @JsonProperty("reason")
094    public String getReason() {
095        return reason;
096    }
097
098    /**
099     * The size of the MP4 file. For archives that have not been generated, this value is set to 0.
100     *
101     * @return The size as a long, or {@code null} if unset.
102     */
103    @JsonProperty("size")
104    public Long getSize() {
105        return size;
106    }
107
108    /**
109     * The status of the archive, as defined by the {@link ArchiveStatus} enum.
110     *
111     * @return The status.
112     */
113    @JsonProperty("status")
114    public ArchiveStatus getStatus() {
115        return status;
116    }
117
118    /**
119     * The download URL of the available MP4 file. This is only set for an archive with the status
120     * set to {@link ArchiveStatus#AVAILABLE}; for other archives (including those with the status of
121     * {@link ArchiveStatus#UPLOADED}) this method returns null. The download URL is obfuscated, and the file
122     * is only available from the URL for 10 minutes. To generate a new URL, call the
123     * {@link VideoClient#listArchives()} or {@link VideoClient#getArchive(String)} method.
124     *
125     * @return The download URL.
126     */
127    @JsonProperty("url")
128    public URI getUrl() {
129        return url;
130    }
131
132    /**
133     * The output mode to be generated for this archive: {@code composed} or {@code individual}.
134     *
135     * @return The {@linkplain OutputMode}.
136     */
137    @JsonProperty("outputMode")
138    public OutputMode getOutputMode() {
139        return outputMode;
140    }
141
142    /**
143     * Returns the multiArchiveTag if set for the Archive.
144     *
145     * @return The multiArchiveTag, or {@code null} if not applicable.
146     */
147    @JsonProperty("multiArchiveTag")
148    public String getMultiArchiveTag() {
149        return multiArchiveTag;
150    }
151
152    /**
153     * Creates an instance of this class from a JSON payload.
154     *
155     * @param json The JSON string to parse.
156     * @return An instance of this class with the fields populated, if present.
157     */
158    public static Archive fromJson(String json) {
159        return Jsonable.fromJson(json);
160    }
161
162
163    /**
164     * Instantiates a Builder, used to construct this object.
165     *
166     * @param sessionId The ID of the Vonage Video session you are working with.
167     *
168     * @return A new {@linkplain Archive.Builder}.
169     */
170    public static Builder builder(String sessionId) {
171        return new Builder(sessionId);
172    }
173
174    /**
175     * Used to construct an Archive object.
176     *
177     * @see Archive
178     */
179    public static class Builder extends StreamComposition.Builder {
180        private String name, multiArchiveTag;
181        private OutputMode outputMode;
182
183        Builder(String sessionId) {
184            this.sessionId = sessionId;
185        }
186
187        /**
188         * Sets a name for the archive.
189         *
190         * @param name The name of the archive. You can use this name to identify the archive. It is a property
191         * of the Archive object, and it is a property of archive-related events in the SDK.
192         *
193         * @return This Builder with the name setting.
194         */
195        public Builder name(String name) {
196            this.name = name;
197            return this;
198        }
199
200        /**
201         * Sets the resolution of the archive.
202         *
203         * @param resolution The resolution of the archive, either "640x480" (SD, the default) or
204         * "1280x720" (HD). This property only applies to composed archives. If you set this
205         * and set the outputMode property to "individual", the call in the API method results in
206         * an error.
207         *
208         * @return This Builder with the resolution setting.
209         */
210        public Builder resolution(Resolution resolution) {
211            this.resolution = resolution;
212            return this;
213        }
214
215        /**
216         * Call this method to include an audio track ({@code true}, the default)
217         * or not {@code false}).
218         *
219         * @param hasAudio Whether the archive will include an audio track.
220         *
221         * @return This Builder with the hasAudio setting.
222         */
223        public Builder hasAudio(boolean hasAudio) {
224            this.hasAudio = hasAudio;
225            return this;
226        }
227
228        /**
229         * Call this method to include a video track ({@code true}, the default)
230         * or not {@code false}).
231         *
232         * @param hasVideo Whether the archive will include a video track.
233         *
234         * @return This Builder with the hasVideo setting.
235         */
236        public Builder hasVideo(boolean hasVideo) {
237            this.hasVideo = hasVideo;
238            return this;
239        }
240
241        /**
242         * Sets the output mode for this archive.
243         *
244         * @param outputMode Set to a value defined in the {@link OutputMode} enum.
245         *
246         * @return This Builder with the output mode setting.
247         */
248        public Builder outputMode(OutputMode outputMode) {
249            this.outputMode = outputMode;
250            return this;
251        }
252
253        /**
254         * Sets the stream mode for this archive.
255         * <p>
256         * When streams are selected automatically ({@code StreamMode.AUTO}, the default), all
257         * streams in the session can be included in the archive. When streams are selected manually
258         * ({@code StreamMode.MANUAL}), you specify streams to be included based on calls
259         * to the {@link VideoClient#addArchiveStream(String, String, Boolean, Boolean)} and
260         * {@link VideoClient#removeArchiveStream(String, String)} methods. With
261         * {@code StreamMode.MANUAL}, you can specify whether a stream's audio, video, or both
262         * are included in the archive. In both automatic and manual modes, the archive composer
263         * includes streams based on
264         * <a href="https://tokbox.com/developer/guides/archive-broadcast-layout/#stream-prioritization-rules">stream
265         * prioritization rules</a>.
266         *
267         * @param streamMode Set to a value defined in the {@link StreamMode} enum.
268         *
269         * @return This Builder with the stream mode setting.
270         */
271        public Builder streamMode(StreamMode streamMode) {
272            this.streamMode = streamMode;
273            return this;
274        }
275
276        /**
277         * Sets the layout for a composed archive. If this option is specified,
278         * {@linkplain Builder#outputMode(OutputMode)} must be {@linkplain OutputMode#COMPOSED}.
279         *
280         * @param layout The layout type to use.
281         *
282         * @return This Builder with the layout setting.
283         */
284        public Builder layout(StreamCompositionLayout layout) {
285            this.layout = layout;
286            return this;
287        }
288
289        /**
290         * Set this to support recording multiple archives for the same session simultaneously.
291         * Set this to a unique string for each simultaneous archive of an ongoing session. You must also set this
292         * option when manually starting an archive that is automatically archived. If you do
293         * not specify a unique multiArchiveTag, you can only record one archive at a time for a given session. See
294         * <a href="https://tokbox.com/developer/guides/archiving/#simultaneous-archives">
295         * Simultaneous Archives documentation</a>.
296         *
297         * @param multiArchiveTag A unique archive tag.
298         *
299         * @return This Builder with the MultiArchiveTag setting.
300         */
301        public Builder multiArchiveTag(String multiArchiveTag) {
302            this.multiArchiveTag = multiArchiveTag;
303            return this;
304        }
305
306        /**
307         * (OPTIONAL) The maximum bitrate for the archive, in bits per second.
308         * This must be between 100000 and 6000000.
309         *
310         * @param maxBitrate The maximum bitrate as an int.
311         *
312         * @return This builder.
313         * @since 8.14.0
314         */
315        public Builder maxBitrate(int maxBitrate) {
316            this.maxBitrate = maxBitrate;
317            return this;
318        }
319
320        /**
321         * Builds the {@linkplain Archive} object with this builder's settings.
322         *
323         * @return A new {@link Archive} instance.
324         */
325        public Archive build() {
326            return new Archive(this);
327        }
328    }
329}