1<?php
2/**
3 * Copyright 2017 Facebook, Inc.
4 *
5 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
6 * use, copy, modify, and distribute this software in source code or binary
7 * form for use in connection with the web services and APIs provided by
8 * Facebook.
9 *
10 * As with any software that integrates with the Facebook platform, your use
11 * of this software is subject to the Facebook Developer Principles and
12 * Policies [http://developers.facebook.com/policy/]. This copyright notice
13 * shall be included in all copies or substantial portions of the software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 */
24namespace Facebook\FileUpload;
25
26use Facebook\Authentication\AccessToken;
27use Facebook\Exceptions\FacebookResponseException;
28use Facebook\Exceptions\FacebookResumableUploadException;
29use Facebook\Exceptions\FacebookSDKException;
30use Facebook\FacebookApp;
31use Facebook\FacebookClient;
32use Facebook\FacebookRequest;
33
34/**
35 * Class FacebookResumableUploader
36 *
37 * @package Facebook
38 */
39class FacebookResumableUploader
40{
41    /**
42     * @var FacebookApp
43     */
44    protected $app;
45
46    /**
47     * @var string
48     */
49    protected $accessToken;
50
51    /**
52     * @var FacebookClient The Facebook client service.
53     */
54    protected $client;
55
56    /**
57     * @var string Graph version to use for this request.
58     */
59    protected $graphVersion;
60
61    /**
62     * @param FacebookApp             $app
63     * @param FacebookClient          $client
64     * @param AccessToken|string|null $accessToken
65     * @param string                  $graphVersion
66     */
67    public function __construct(FacebookApp $app, FacebookClient $client, $accessToken, $graphVersion)
68    {
69        $this->app = $app;
70        $this->client = $client;
71        $this->accessToken = $accessToken;
72        $this->graphVersion = $graphVersion;
73    }
74
75    /**
76     * Upload by chunks - start phase
77     *
78     * @param string $endpoint
79     * @param FacebookFile $file
80     *
81     * @return FacebookTransferChunk
82     *
83     * @throws FacebookSDKException
84     */
85    public function start($endpoint, FacebookFile $file)
86    {
87        $params = [
88            'upload_phase' => 'start',
89            'file_size' => $file->getSize(),
90        ];
91        $response = $this->sendUploadRequest($endpoint, $params);
92
93        return new FacebookTransferChunk($file, $response['upload_session_id'], $response['video_id'], $response['start_offset'], $response['end_offset']);
94    }
95
96    /**
97     * Upload by chunks - transfer phase
98     *
99     * @param string $endpoint
100     * @param FacebookTransferChunk $chunk
101     * @param boolean $allowToThrow
102     *
103     * @return FacebookTransferChunk
104     *
105     * @throws FacebookResponseException
106     */
107    public function transfer($endpoint, FacebookTransferChunk $chunk, $allowToThrow = false)
108    {
109        $params = [
110            'upload_phase' => 'transfer',
111            'upload_session_id' => $chunk->getUploadSessionId(),
112            'start_offset' => $chunk->getStartOffset(),
113            'video_file_chunk' => $chunk->getPartialFile(),
114        ];
115
116        try {
117            $response = $this->sendUploadRequest($endpoint, $params);
118        } catch (FacebookResponseException $e) {
119            $preException = $e->getPrevious();
120            if ($allowToThrow || !$preException instanceof FacebookResumableUploadException) {
121                throw $e;
122            }
123
124            // Return the same chunk entity so it can be retried.
125            return $chunk;
126        }
127
128        return new FacebookTransferChunk($chunk->getFile(), $chunk->getUploadSessionId(), $chunk->getVideoId(), $response['start_offset'], $response['end_offset']);
129    }
130
131    /**
132     * Upload by chunks - finish phase
133     *
134     * @param string $endpoint
135     * @param string $uploadSessionId
136     * @param array $metadata The metadata associated with the file.
137     *
138     * @return boolean
139     *
140     * @throws FacebookSDKException
141     */
142    public function finish($endpoint, $uploadSessionId, $metadata = [])
143    {
144        $params = array_merge($metadata, [
145            'upload_phase' => 'finish',
146            'upload_session_id' => $uploadSessionId,
147        ]);
148        $response = $this->sendUploadRequest($endpoint, $params);
149
150        return $response['success'];
151    }
152
153    /**
154     * Helper to make a FacebookRequest and send it.
155     *
156     * @param string $endpoint The endpoint to POST to.
157     * @param array $params The params to send with the request.
158     *
159     * @return array
160     */
161    private function sendUploadRequest($endpoint, $params = [])
162    {
163        $request = new FacebookRequest($this->app, $this->accessToken, 'POST', $endpoint, $params, null, $this->graphVersion);
164
165        return $this->client->sendRequest($request)->getDecodedBody();
166    }
167}
168