OSDN Git Service

メソッドに式本体を使用する (IDE0021, IDE0022, IDE0025, IDE0027)
[opentween/open-tween.git] / OpenTween / Connection / Mobypicture.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2014 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
4 //
5 // This file is part of OpenTween.
6 //
7 // This program is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General Public License as published by the Free
9 // Software Foundation; either version 3 of the License, or (at your option)
10 // any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 // for more details.
16 //
17 // You should have received a copy of the GNU General Public License along
18 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
19 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 // Boston, MA 02110-1301, USA.
21
22 using System;
23 using System.Collections.Generic;
24 using System.IO;
25 using System.Linq;
26 using System.Net;
27 using System.Net.Http;
28 using System.Text;
29 using System.Threading.Tasks;
30 using System.Windows.Forms;
31 using System.Xml;
32 using System.Xml.Linq;
33 using System.Xml.XPath;
34 using OpenTween.Api;
35 using OpenTween.Api.DataModel;
36
37 namespace OpenTween.Connection
38 {
39     public class Mobypicture : IMediaUploadService
40     {
41         private static readonly long MaxFileSize = 5L * 1024 * 1024; // 上限不明
42
43         // 参照: http://developers.mobypicture.com/documentation/1-0/postmedia/
44         private static readonly IEnumerable<string> AllowedExtensions = new[]
45         {
46             // Photo
47             ".jpg",
48             ".gif",
49             ".png",
50             ".bmp",
51
52             // Video
53             ".flv",
54             ".mpg",
55             ".mpeg",
56             ".mkv",
57             ".wmv",
58             ".mov",
59             ".3gp",
60             ".mp4",
61             ".avi",
62
63             // Audio
64             ".mp3",
65             ".wma",
66             ".aac",
67             ".aif",
68             ".au",
69             ".flac",
70             ".ra",
71             ".wav",
72             ".ogg",
73             ".3gp",
74         };
75
76         private readonly Twitter twitter;
77         private readonly MobypictureApi mobypictureApi;
78
79         private TwitterConfiguration twitterConfig;
80
81         public Mobypicture(Twitter twitter, TwitterConfiguration twitterConfig)
82         {
83             this.twitter = twitter ?? throw new ArgumentNullException(nameof(twitter));
84             this.twitterConfig = twitterConfig ?? throw new ArgumentNullException(nameof(twitterConfig));
85
86             this.mobypictureApi = new MobypictureApi(twitter.Api);
87         }
88
89         public int MaxMediaCount => 1;
90
91         public string SupportedFormatsStrForDialog
92         {
93             get
94             {
95                 var filterFormatExtensions = "";
96                 foreach (var pictureExtension in AllowedExtensions)
97                 {
98                     filterFormatExtensions += '*' + pictureExtension + ';';
99                 }
100                 return "Media Files(" + filterFormatExtensions + ")|" + filterFormatExtensions;
101             }
102         }
103
104         public bool CanUseAltText => false;
105
106         public bool CheckFileExtension(string fileExtension)
107             => AllowedExtensions.Contains(fileExtension, StringComparer.OrdinalIgnoreCase);
108
109         public bool CheckFileSize(string fileExtension, long fileSize)
110         {
111             var maxFileSize = this.GetMaxFileSize(fileExtension);
112             return maxFileSize == null || fileSize <= maxFileSize.Value;
113         }
114
115         public long? GetMaxFileSize(string fileExtension)
116             => MaxFileSize;
117
118         public async Task<PostStatusParams> UploadAsync(IMediaItem[] mediaItems, PostStatusParams postParams)
119         {
120             if (mediaItems == null)
121                 throw new ArgumentNullException(nameof(mediaItems));
122
123             if (mediaItems.Length != 1)
124                 throw new ArgumentOutOfRangeException(nameof(mediaItems));
125
126             var item = mediaItems[0];
127
128             if (item == null)
129                 throw new ArgumentException("Err:Media not specified.");
130
131             if (!item.Exists)
132                 throw new ArgumentException("Err:Media not found.");
133
134             var xml = await this.mobypictureApi.UploadFileAsync(item, postParams.Text)
135                 .ConfigureAwait(false);
136
137             var imageUrlElm = xml.XPathSelectElement("/rsp/media/mediaurl");
138             if (imageUrlElm == null)
139                 throw new WebApiException("Invalid API response", xml.ToString());
140
141             postParams.Text += " " + imageUrlElm.Value.Trim();
142
143             return postParams;
144         }
145
146         public int GetReservedTextLength(int mediaCount)
147             => this.twitterConfig.ShortUrlLength + 1;
148
149         public void UpdateTwitterConfiguration(TwitterConfiguration config)
150             => this.twitterConfig = config;
151
152         public class MobypictureApi
153         {
154             private readonly HttpClient http;
155
156             private static readonly Uri UploadEndpoint = new Uri("https://api.mobypicture.com/2.0/upload.xml");
157
158             private static readonly Uri OAuthRealm = new Uri("http://api.twitter.com/");
159             private static readonly Uri AuthServiceProvider = new Uri("https://api.twitter.com/1.1/account/verify_credentials.json");
160
161             public MobypictureApi(TwitterApi twitterApi)
162             {
163                 var handler = twitterApi.CreateOAuthEchoHandler(AuthServiceProvider, OAuthRealm);
164
165                 this.http = Networking.CreateHttpClient(handler);
166                 this.http.Timeout = Networking.UploadImageTimeout;
167             }
168
169             /// <summary>
170             /// 画像のアップロードを行います
171             /// </summary>
172             /// <exception cref="WebApiException"/>
173             /// <exception cref="XmlException"/>
174             public async Task<XDocument> UploadFileAsync(IMediaItem item, string message)
175             {
176                 // 参照: http://developers.mobypicture.com/documentation/2-0/upload/
177
178                 using (var request = new HttpRequestMessage(HttpMethod.Post, UploadEndpoint))
179                 using (var multipart = new MultipartFormDataContent())
180                 {
181                     request.Content = multipart;
182
183                     using (var apiKeyContent = new StringContent(ApplicationSettings.MobypictureKey))
184                     using (var messageContent = new StringContent(message))
185                     using (var mediaStream = item.OpenRead())
186                     using (var mediaContent = new StreamContent(mediaStream))
187                     {
188                         multipart.Add(apiKeyContent, "key");
189                         multipart.Add(messageContent, "message");
190                         multipart.Add(mediaContent, "media", item.Name);
191
192                         using (var response = await this.http.SendAsync(request).ConfigureAwait(false))
193                         {
194                             var responseText = await response.Content.ReadAsStringAsync()
195                                 .ConfigureAwait(false);
196
197                             if (!response.IsSuccessStatusCode)
198                                 throw new WebApiException(response.StatusCode.ToString(), responseText);
199
200                             return XDocument.Parse(responseText);
201                         }
202                     }
203                 }
204             }
205         }
206     }
207 }