public int CharactersReservedPerMedia { get; set; }
[DataMember(Name = "photo_size_limit")]
- public int PhotoSizeLimit { get; set; }
+ public long PhotoSizeLimit { get; set; }
[DataMember(Name = "photo_sizes")]
public TwitterMediaSizes PhotoSizes { get; set; }
--- /dev/null
+// OpenTween - Client of Twitter
+// Copyright (c) 2014 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using OpenTween.Api;
+
+namespace OpenTween.Connection
+{
+ /// <summary>
+ /// Twitterでの画像の共有に使用できるサービスを表すインタフェース
+ /// </summary>
+ public interface IMediaUploadService
+ {
+ /// <summary>
+ /// アップロード可能なメディアの最大枚数
+ /// </summary>
+ int MaxMediaCount { get; }
+
+ /// <summary>
+ /// アップロード可能なファイルの種類を表す文字列 (OpenFileDialog.Filter に使用)
+ /// </summary>
+ string SupportedFormatsStrForDialog { get; }
+
+ /// <summary>
+ /// ファイルの拡張子からアップロード可能なフォーマットであるかを判定します
+ /// </summary>
+ /// <param name="fileExtension">アップロードするファイルの拡張子 (ピリオドを含む)</param>
+ bool CheckFileExtension(string fileExtension);
+
+ /// <summary>
+ /// ファイルサイズがアップロード可能な範囲内であるかを判定します
+ /// </summary>
+ /// <param name="fileExtension">アップロードするファイルの拡張子 (ピリオドを含む)</param>
+ /// <param name="fileSize">アップロードするファイルのサイズ (バイト単位)</param>
+ bool CheckFileSize(string fileExtension, long fileSize);
+
+ /// <summary>
+ /// アップロード可能なファイルサイズの上限を返します
+ /// </summary>
+ /// <param name="fileExtension">アップロードするファイルの拡張子 (ピリオドを含む)</param>
+ /// <returns>ファイルサイズの上限 (バイト単位, nullの場合は上限なし)</returns>
+ long? GetMaxFileSize(string fileExtension);
+
+ /// <summary>
+ /// メディアのアップロードとツイートの投稿を行います
+ /// </summary>
+ /// <exception cref="WebApiException"/>
+ Task PostStatusAsync(string text, long? inReplyToStatusId, string[] filePaths);
+
+ /// <summary>
+ /// 画像URLのために確保する必要のある文字数を返します
+ /// </summary>
+ /// <param name="mediaCount">アップロードするメディアの個数</param>
+ int GetReservedTextLength(int mediaCount);
+
+ /// <summary>
+ /// IMediaUploadService で使用する /help/configuration.json の値を更新します
+ /// </summary>
+ void UpdateTwitterConfiguration(TwitterConfiguration config);
+ }
+}
+++ /dev/null
-// OpenTween - Client of Twitter
-// Copyright (c) 2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
-// (c) 2011 Egtra (@egtra) <http://dev.activebasic.com/egtra/>
-// All rights reserved.
-//
-// This file is part of OpenTween.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-// for more details.
-//
-// You should have received a copy of the GNU General Public License along
-// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
-// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
-// Boston, MA 02110-1301, USA.
-
-namespace OpenTween
-{
- public interface IMultimediaShareService
- {
- string Upload(ref string filePath,
- ref string message,
- long? reply_to);
- bool CheckValidExtension(string ext) ;
- string GetFileOpenDialogFilter();
- MyCommon.UploadFileType GetFileType(string ext);
- bool IsSupportedFileType(MyCommon.UploadFileType type);
- bool CheckValidFilesize(string ext, long fileSize);
- bool Configuration(string key, object value);
- }
-}
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
+using System.Net.Http;
+using System.Net.Http.Headers;
using System.Text;
-using System.Net;
-using System.IO;
+using System.Threading.Tasks;
using System.Xml.Linq;
-using System.Xml;
+using OpenTween.Api;
namespace OpenTween.Connection
{
- public class Imgur : HttpConnectionOAuth, IMultimediaShareService
+ public class Imgur : IMediaUploadService
{
private readonly static long MaxFileSize = 10L * 1024 * 1024;
private readonly static Uri UploadEndpoint = new Uri("https://api.imgur.com/3/image.xml");
".xcf",
};
- private readonly Twitter _twitter;
+ private readonly Twitter twitter;
+ private TwitterConfiguration twitterConfig;
- public Imgur(Twitter tw)
+ public Imgur(Twitter tw, TwitterConfiguration twitterConfig)
{
- this._twitter = tw;
-
- Initialize(ApplicationSettings.TwitterConsumerKey, ApplicationSettings.TwitterConsumerSecret,
- tw.AccessToken, tw.AccessTokenSecret,
- "", "");
+ this.twitter = tw;
+ this.twitterConfig = twitterConfig;
}
- protected override void AppendOAuthInfo(HttpWebRequest webRequest, Dictionary<string, string> query, string token, string tokenSecret)
+ public int MaxMediaCount
{
- webRequest.Headers[HttpRequestHeader.Authorization] =
- string.Format("Client-ID {0}", ApplicationSettings.ImgurClientID);
+ get { return 1; }
}
- public string Upload(ref string filePath, ref string message, long? reply_to)
+ public string SupportedFormatsStrForDialog
{
- if (!File.Exists(filePath))
- return "Err:File isn't exists.";
-
- var mediaFile = new FileInfo(filePath);
- var content = "";
- HttpStatusCode result;
- try
- {
- result = this.UploadFile(mediaFile, message, ref content);
- }
- catch (Exception ex)
+ get
{
- return "Err:" + ex.Message;
- }
+ var formats = new StringBuilder();
- if (result != HttpStatusCode.OK)
- {
- return "Err:" + result;
- }
+ foreach (var extension in SupportedExtensions)
+ formats.AppendFormat("*{0};", extension);
- var imageUrl = "";
- try
- {
- var xdoc = XDocument.Parse(content);
- var image = xdoc.Element("data");
- if (image.Attribute("success").Value != "1")
- {
- return "APIErr:" + image.Attribute("status").Value;
- }
- imageUrl = image.Element("link").Value;
- }
- catch (XmlException ex)
- {
- return "XmlErr:" + ex.Message;
+ return "Image Files(" + formats + ")|" + formats;
}
+ }
- filePath = "";
- if (message == null)
- message = "";
+ public bool CheckFileExtension(string fileExtension)
+ {
+ return SupportedExtensions.Contains(fileExtension, StringComparer.OrdinalIgnoreCase);
+ }
- // Post to twitter
- if (message.Length + AppendSettingDialog.Instance.TwitterConfiguration.CharactersReservedPerMedia + 1 > 140)
- {
- message = message.Substring(0, 140 - AppendSettingDialog.Instance.TwitterConfiguration.CharactersReservedPerMedia - 1) + " " + imageUrl;
- }
- else
- {
- message += " " + imageUrl;
- }
- return _twitter.PostStatus(message, reply_to);
+ public bool CheckFileSize(string fileExtension, long fileSize)
+ {
+ var maxFileSize = this.GetMaxFileSize(fileExtension);
+ return maxFileSize == null || fileSize <= maxFileSize.Value;
+ }
+
+ public long? GetMaxFileSize(string fileExtension)
+ {
+ return MaxFileSize;
}
- private HttpStatusCode UploadFile(FileInfo mediaFile, string message, ref string content)
+ public async Task PostStatusAsync(string text, long? inReplyToStatusId, string[] filePaths)
{
- if (!CheckValidExtension(mediaFile.Extension))
- throw new ArgumentException("Service don't support this filetype", "mediaFile");
- if (!CheckValidFilesize(mediaFile.Extension, mediaFile.Length))
- throw new ArgumentException("File is too large", "mediaFile");
+ if (filePaths.Length != 1)
+ throw new ArgumentOutOfRangeException("filePaths");
+
+ var file = new FileInfo(filePaths[0]);
+
+ if (!file.Exists)
+ throw new ArgumentException("File isn't exists.", "filePaths[0]");
- var param = new Dictionary<string, string>
+ XDocument xml;
+ try
{
- {"title", message},
- };
- var binary = new List<KeyValuePair<string, FileInfo>>
+ xml = await this.UploadFileAsync(file, text)
+ .ConfigureAwait(false);
+ }
+ catch (HttpRequestException ex)
{
- new KeyValuePair<string, FileInfo>("image", mediaFile)
- };
- this.InstanceTimeout = 60000;
+ throw new WebApiException("Err:" + ex.Message, ex);
+ }
- return this.GetContent(PostMethod, UploadEndpoint, param, binary, ref content, null, null);
- }
+ var imageElm = xml.Element("data");
- public bool CheckValidExtension(string ext)
- {
- return SupportedExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
- }
+ if (imageElm.Attribute("success").Value != "1")
+ throw new WebApiException("Err:" + imageElm.Attribute("status").Value);
- public string GetFileOpenDialogFilter()
- {
- var formats = new StringBuilder();
+ var imageUrl = imageElm.Element("link").Value;
- foreach (var extension in SupportedExtensions)
- formats.AppendFormat("*{0};", extension);
+ var textWithImageUrl = text + " " + imageUrl.Trim();
- return "Image Files(" + formats + ")|" + formats;
+ await Task.Run(() => this.twitter.PostStatus(textWithImageUrl, inReplyToStatusId))
+ .ConfigureAwait(false);
}
- public MyCommon.UploadFileType GetFileType(string ext)
+ public int GetReservedTextLength(int mediaCount)
{
- return this.CheckValidExtension(ext)
- ? MyCommon.UploadFileType.Picture
- : MyCommon.UploadFileType.Invalid;
+ return this.twitterConfig.ShortUrlLength;
}
- public bool IsSupportedFileType(MyCommon.UploadFileType type)
+ public void UpdateTwitterConfiguration(TwitterConfiguration config)
{
- return type == MyCommon.UploadFileType.Picture;
+ this.twitterConfig = config;
}
- public bool CheckValidFilesize(string ext, long fileSize)
+ public async Task<XDocument> UploadFileAsync(FileInfo file, string title)
{
- return CheckValidExtension(ext) && fileSize <= MaxFileSize;
- }
+ using (var content = new MultipartFormDataContent())
+ using (var fileStream = file.OpenRead())
+ using (var fileContent = new StreamContent(fileStream))
+ using (var titleContent = new StringContent(title))
+ {
+ content.Add(fileContent, "image", file.Name);
+ content.Add(titleContent, "title");
- public bool Configuration(string key, object value)
- {
- throw new NotImplementedException();
+ using (var http = MyCommon.CreateHttpClient())
+ using (var request = new HttpRequestMessage(HttpMethod.Post, UploadEndpoint))
+ {
+ http.Timeout = TimeSpan.FromMinutes(1);
+
+ request.Headers.Authorization =
+ new AuthenticationHeaderValue("Client-ID", ApplicationSettings.ImgurClientID);
+ request.Content = content;
+
+ using (var response = await http.SendAsync(request).ConfigureAwait(false))
+ {
+ response.EnsureSuccessStatusCode();
+
+ using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
+ {
+ return XDocument.Load(stream);
+ }
+ }
+ }
+ }
}
}
}
// OpenTween - Client of Twitter
// Copyright (c) 2013 ANIKITI (@anikiti07) <https://twitter.com/anikiti07>
+// (c) 2014 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
// All rights reserved.
//
// This file is part of OpenTween.
using System.IO;
using System.Linq;
using System.Net;
+using System.Threading.Tasks;
+using System.Windows.Forms;
using System.Xml;
+using System.Xml.Linq;
+using System.Xml.XPath;
+using OpenTween.Api;
namespace OpenTween.Connection
{
- public sealed class TwipplePhoto : HttpConnectionOAuthEcho,
- IMultimediaShareService
+ public sealed class TwipplePhoto : IMediaUploadService
{
- private const long MaxFileSize = 4 * 1024 * 1024;
-
- private readonly Twitter _twitter;
- private readonly Uri _twipplePhotoUploadUri = new Uri("http://p.twipple.jp/api/upload2");
- private readonly IEnumerable<string> _supportedPictureExtensions = new[]
+ private static readonly long MaxFileSize = 4L * 1024 * 1024;
+ private static readonly IEnumerable<string> SupportedPictureExtensions = new[]
{
".gif",
".jpg",
- ".png"
+ ".png",
};
+ private readonly Twitter twitter;
+ private readonly TwippleApi twippleApi;
+
+ private TwitterConfiguration twitterConfig;
+
#region Constructors
- public TwipplePhoto(Twitter twitter)
- : base(new Uri("http://api.twitter.com/"), new Uri("https://api.twitter.com/1.1/account/verify_credentials.json"))
+ public TwipplePhoto(Twitter twitter, TwitterConfiguration twitterConfig)
{
if (twitter == null)
throw new ArgumentNullException("twitter");
+ if (twitterConfig == null)
+ throw new ArgumentNullException("config");
+
+ this.twitter = twitter;
+ this.twitterConfig = twitterConfig;
- _twitter = twitter;
- Initialize(ApplicationSettings.TwitterConsumerKey, ApplicationSettings.TwitterConsumerSecret,
- _twitter.AccessToken, _twitter.AccessTokenSecret,
- "", "");
+ this.twippleApi = new TwippleApi(twitter.AccessToken, twitter.AccessTokenSecret);
}
#endregion
- #region IMultimediaShareService Members
-
- #region Upload Methods
-
- public string Upload(ref string filePath, ref string message, long? reply_to)
+ public int MaxMediaCount
{
- if (!File.Exists(filePath))
- return "Err:File isn't exists.";
-
- var mediaFile = new FileInfo(filePath);
- var content = "";
- HttpStatusCode result;
- try
- {
- result = UploadFile(mediaFile, ref content);
- }
- catch (Exception ex)
- {
- return "Err:" + ex.Message;
- }
+ get { return 1; }
+ }
- var imageUrl = "";
- if (result == HttpStatusCode.OK)
+ public string SupportedFormatsStrForDialog
+ {
+ get
{
- try
+ var filterFormatExtensions = "";
+ foreach (var pictureExtension in SupportedPictureExtensions)
{
- var xdoc = new XmlDocument();
- xdoc.LoadXml(content);
- var urlNode = xdoc.SelectSingleNode("/rsp/mediaurl");
- if (urlNode != null)
- {
- imageUrl = urlNode.InnerText;
- }
+ filterFormatExtensions += '*';
+ filterFormatExtensions += pictureExtension;
+ filterFormatExtensions += ';';
}
- catch (XmlException ex)
- {
- return "XmlErr:" + ex.Message;
- }
- }
- else
- {
- return "Err:" + result;
+ return "Image Files(" + filterFormatExtensions + ")|" + filterFormatExtensions;
}
-
- filePath = "";
- if (message == null)
- message = "";
-
- // Post to twitter
- if (message.Length + AppendSettingDialog.Instance.TwitterConfiguration.CharactersReservedPerMedia + 1 > 140)
- {
- message = message.Substring(0, 140 - AppendSettingDialog.Instance.TwitterConfiguration.CharactersReservedPerMedia - 1) + " " + imageUrl;
- }
- else
- {
- message += " " + imageUrl;
- }
- return _twitter.PostStatus(message, reply_to);
}
- private HttpStatusCode UploadFile(FileInfo mediaFile, ref string content)
+ public bool CheckFileExtension(string fileExtension)
{
- if (!CheckValidExtension(mediaFile.Extension))
- throw new ArgumentException("Service don't support this filetype", "mediaFile");
- if (!CheckValidFilesize(mediaFile.Extension, mediaFile.Length))
- throw new ArgumentException("File is too large", "mediaFile");
-
- var binaly = new List<KeyValuePair<string, FileInfo>>
- {
- new KeyValuePair<string, FileInfo>("media", mediaFile)
- };
- InstanceTimeout = 60000;
-
- return GetContent(PostMethod, _twipplePhotoUploadUri, null, binaly, ref content, null, null);
+ return SupportedPictureExtensions.Contains(fileExtension, StringComparer.OrdinalIgnoreCase);
}
- #endregion
-
- public bool CheckValidExtension(string ext)
+ public bool CheckFileSize(string fileExtension, long fileSize)
{
- return _supportedPictureExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
+ var maxFileSize = this.GetMaxFileSize(fileExtension);
+ return maxFileSize == null || fileSize <= maxFileSize.Value;
}
- public string GetFileOpenDialogFilter()
+ public long? GetMaxFileSize(string fileExtension)
{
- string filterFormatExtensions = "";
- foreach (var pictureExtension in _supportedPictureExtensions)
- {
- filterFormatExtensions += '*';
- filterFormatExtensions += pictureExtension;
- filterFormatExtensions += ';';
- }
- return "Image Files(" + filterFormatExtensions + ")|" + filterFormatExtensions;
+ return MaxFileSize;
}
- public MyCommon.UploadFileType GetFileType(string extension)
+ public async Task PostStatusAsync(string text, long? inReplyToStatusId, string[] filePaths)
{
- return CheckValidExtension(extension)
- ? MyCommon.UploadFileType.Picture
- : MyCommon.UploadFileType.Invalid;
+ if (filePaths.Length != 1)
+ throw new ArgumentOutOfRangeException("filePaths");
+
+ var file = new FileInfo(filePaths[0]);
+
+ if (!file.Exists)
+ throw new ArgumentException("Err:File isn't exists.", "filePaths[0]");
+
+ var xml = await this.twippleApi.UploadFileAsync(file)
+ .ConfigureAwait(false);
+
+ var imageUrlElm = xml.XPathSelectElement("/rsp/mediaurl");
+ if (imageUrlElm == null)
+ throw new WebApiException("Invalid API response", xml.ToString());
+
+ var textWithImageUrl = text + " " + imageUrlElm.Value.Trim();
+
+ await Task.Run(() => this.twitter.PostStatus(textWithImageUrl, inReplyToStatusId))
+ .ConfigureAwait(false);
}
- public bool IsSupportedFileType(MyCommon.UploadFileType type)
+ public int GetReservedTextLength(int mediaCount)
{
- return type == MyCommon.UploadFileType.Picture;
+ return this.twitterConfig.ShortUrlLength;
}
- public bool CheckValidFilesize(string extension, long fileSize)
+ public void UpdateTwitterConfiguration(TwitterConfiguration config)
{
- return CheckValidExtension(extension) && fileSize <= MaxFileSize;
+ this.twitterConfig = config;
}
- public bool Configuration(string key, object value)
+ public class TwippleApi : HttpConnectionOAuthEcho
{
- throw new NotImplementedException();
- }
+ private static readonly Uri UploadEndpoint = new Uri("http://p.twipple.jp/api/upload2");
- #endregion
+ public TwippleApi(string twitterAccessToken, string twitterAccessTokenSecret)
+ : base(new Uri("http://api.twitter.com/"), new Uri("https://api.twitter.com/1.1/account/verify_credentials.json"))
+ {
+ this.Initialize(ApplicationSettings.TwitterConsumerKey, ApplicationSettings.TwitterConsumerSecret,
+ twitterAccessToken, twitterAccessTokenSecret, "", "");
+
+ this.InstanceTimeout = 60000;
+ }
+
+ /// <summary>
+ /// 画像のアップロードを行います
+ /// </summary>
+ /// <exception cref="WebApiException"/>
+ /// <exception cref="XmlException"/>
+ public async Task<XDocument> UploadFileAsync(FileInfo file)
+ {
+ // 参照: http://p.twipple.jp/wiki/API_Upload2/ja
+
+ var param = new Dictionary<string, string>
+ {
+ {"upload_from", Application.ProductName},
+ };
+ var paramFiles = new List<KeyValuePair<string, FileInfo>>
+ {
+ new KeyValuePair<string, FileInfo>("media", file),
+ };
+ var response = "";
+
+ var uploadTask = Task.Run(() => this.GetContent(HttpConnection.PostMethod,
+ UploadEndpoint, param, paramFiles, ref response, null, null));
+
+ var ret = await uploadTask.ConfigureAwait(false);
+
+ if (ret != HttpStatusCode.OK)
+ throw new WebApiException("Err:" + ret, response);
+
+ return XDocument.Parse(response);
+ }
+ }
}
}
\ No newline at end of file
// (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
// (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
// (c) 2011 spinor (@tplantd) <http://d.hatena.ne.jp/spinor/>
+// (c) 2014 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
// All rights reserved.
-//
+//
// This file is part of OpenTween.
-//
+//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option)
// any later version.
-//
+//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-// for more details.
-//
+// for more details.
+//
// You should have received a copy of the GNU General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
// Boston, MA 02110-1301, USA.
-using HttpConnectionOAuthEcho = OpenTween.HttpConnectionOAuthEcho;
-using IMultimediaShareService = OpenTween.IMultimediaShareService;
-using FileInfo = System.IO.FileInfo;
-using NotSupportedException = System.NotSupportedException;
-using HttpStatusCode = System.Net.HttpStatusCode;
-using Exception = System.Exception;
-using XmlDocument = System.Xml.XmlDocument;
-using XmlException = System.Xml.XmlException;
-using ArgumentException = System.ArgumentException;
-using System.Collections.Generic; // for Dictionary<TKey, TValue>, List<T>, KeyValuePair<TKey, TValue>
-using UploadFileType = OpenTween.MyCommon.UploadFileType;
-using Uri = System.Uri;
-using Array = System.Array;
-
-namespace OpenTween
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Threading.Tasks;
+using System.Xml;
+using System.Xml.Linq;
+using System.Xml.XPath;
+using OpenTween.Api;
+
+namespace OpenTween.Connection
{
- public class TwitPic : HttpConnectionOAuthEcho, IMultimediaShareService
- {
- private string[] pictureExt = new string[] { ".jpg", ".jpeg", ".gif", ".png" };
-
- private string[] multimediaExt = new string[] { ".avi", ".wmv", ".flv", ".m4v", ".mov", ".mp4", ".rm", ".mpeg", ".mpg", ".3gp", ".3g2" };
-
- private const long MaxFileSize = 10 * 1024 * 1024; // Image only
- // Multimedia filesize limit unknown. But length limit is 1:30.
-
- private Twitter tw;
-
- public string Upload( ref string filePath, ref string message, long? reply_to )
- {
- if ( string.IsNullOrEmpty( filePath ) )
- return "Err:File isn't specified.";
- if ( string.IsNullOrEmpty( message ) )
- message = "";
-
- FileInfo mediaFile;
- try
- {
- mediaFile = new FileInfo( filePath );
- }
- catch ( NotSupportedException ex )
- {
- return "Err:" + ex.Message;
- }
- if ( mediaFile == null || !mediaFile.Exists )
- return "Err:File isn't exists.";
-
- string content = "";
- HttpStatusCode ret;
- // TwitPicへの投稿
- try
- {
- ret = this.UploadFile( mediaFile, message, ref content );
- }
- catch ( Exception ex )
- {
- return "Err:" + ex.Message;
- }
- string url = "";
- if ( ret == HttpStatusCode.OK )
- {
- XmlDocument xd = new XmlDocument();
- try
- {
- xd.LoadXml( content );
- // URLの取得
- url = xd.SelectSingleNode( "/image/url" ).InnerText;
- }
- catch ( XmlException ex )
- {
- return "Err:" + ex.Message;
- }
- catch ( Exception ex )
- {
- return "Err:" + ex.Message;
- }
- }
- else
- return "Err:" + ret.ToString();
-
- // アップロードまでは成功
- filePath = "";
- if ( string.IsNullOrEmpty( message ) )
- message = "";
- if ( string.IsNullOrEmpty( url ) )
- url = "";
- // Twitterへの投稿
- // 投稿メッセージの再構成
- if ( message.Length + AppendSettingDialog.Instance.TwitterConfiguration.CharactersReservedPerMedia + 1 > 140 )
- message = message.Substring( 0, 140 - AppendSettingDialog.Instance.TwitterConfiguration.CharactersReservedPerMedia - 1 ) + " " + url;
- else
- message += " " + url;
-
- return tw.PostStatus( message, reply_to );
- }
-
- private HttpStatusCode UploadFile( FileInfo mediaFile, string message, ref string content )
- {
- // Message必須
- if ( string.IsNullOrEmpty( message ) )
- message = "";
- // Check filetype and size(Max 5MB)
- if ( !this.CheckValidExtension( mediaFile.Extension ) )
- throw new ArgumentException( "Service don't support this filetype." );
- if ( !this.CheckValidFilesize( mediaFile.Extension, mediaFile.Length ) )
- throw new ArgumentException( "File is too large." );
-
- Dictionary< string, string > param = new Dictionary< string, string >();
- param.Add( "key", ApplicationSettings.TwitpicApiKey );
- param.Add( "message", message );
- List< KeyValuePair< string, FileInfo > > binary = new List< KeyValuePair< string, FileInfo > >();
- binary.Add( new KeyValuePair< string, FileInfo >( "media", mediaFile ) );
- if ( this.GetFileType( mediaFile.Extension ) == UploadFileType.Picture )
- this.InstanceTimeout = 60000; // タイムアウト60秒
- else
- this.InstanceTimeout = 120000;
-
- return this.GetContent( HttpConnection.PostMethod, new Uri( "http://api.twitpic.com/2/upload.xml" ), param, binary, ref content, null, null );
- }
-
- public bool CheckValidExtension( string ext )
- {
- if ( Array.IndexOf( this.pictureExt, ext.ToLower() ) > -1 )
- return true;
- if ( Array.IndexOf( this.multimediaExt, ext.ToLower() ) > -1 )
- return true;
-
- return false;
- }
-
- public string GetFileOpenDialogFilter()
- {
- return "Image Files(*" + string.Join( ";*", this.pictureExt ) + ")|*" + string.Join( ";*", this.pictureExt )
- + "|Videos(*" + string.Join( ";*", this.multimediaExt ) + ")|*" + string.Join( ";*", this.multimediaExt );
- }
-
- public UploadFileType GetFileType( string ext )
- {
- if ( Array.IndexOf( this.pictureExt, ext.ToLower() ) > -1 )
- return UploadFileType.Picture;
- if ( Array.IndexOf( this.multimediaExt, ext.ToLower() ) > -1 )
- return UploadFileType.MultiMedia;
-
- return UploadFileType.Invalid;
- }
-
- public bool IsSupportedFileType( UploadFileType type )
- {
- return !type.Equals( UploadFileType.Invalid );
- }
-
- public bool CheckValidFilesize( string ext, long fileSize )
- {
- if ( Array.IndexOf( this.pictureExt, ext.ToLower() ) > -1 )
- return fileSize <= TwitPic.MaxFileSize;
- if ( Array.IndexOf( this.multimediaExt, ext.ToLower() ) > -1 )
- return true; // Multimedia : no check
-
- return false;
- }
-
- public bool Configuration( string key, object value )
- {
- return true;
- }
-
- public TwitPic( Twitter twitter )
- : base( new Uri( "http://api.twitter.com/" ), new Uri( "https://api.twitter.com/1.1/account/verify_credentials.json" ) )
- {
- this.tw = twitter;
- this.Initialize( ApplicationSettings.TwitterConsumerKey, ApplicationSettings.TwitterConsumerSecret, tw.AccessToken, tw.AccessTokenSecret, "", "" );
- }
- }
+ public class TwitPic : IMediaUploadService
+ {
+ private readonly string[] pictureExt = new[] { ".jpg", ".jpeg", ".gif", ".png" };
+
+ private readonly string[] multimediaExt = new[] { ".avi", ".wmv", ".flv", ".m4v", ".mov", ".mp4", ".rm", ".mpeg", ".mpg", ".3gp", ".3g2" };
+
+ private readonly long MaxFileSize = 10L * 1024 * 1024; // Image only
+ // Multimedia filesize limit unknown. But length limit is 1:30.
+
+ private readonly Twitter tw;
+ private readonly TwitpicApi twitpicApi;
+
+ private TwitterConfiguration twitterConfig;
+
+ public TwitPic(Twitter twitter, TwitterConfiguration twitterConfig)
+ {
+ this.tw = twitter;
+ this.twitterConfig = twitterConfig;
+
+ this.twitpicApi = new TwitpicApi(twitter.AccessToken, twitter.AccessTokenSecret);
+ }
+
+ public int MaxMediaCount
+ {
+ get { return 1; }
+ }
+
+ public string SupportedFormatsStrForDialog
+ {
+ get
+ {
+ return "Image Files(*" + string.Join(";*", this.pictureExt) + ")|*" + string.Join(";*", this.pictureExt)
+ + "|Videos(*" + string.Join(";*", this.multimediaExt) + ")|*" + string.Join(";*", this.multimediaExt);
+ }
+ }
+
+ public bool CheckFileExtension(string fileExtension)
+ {
+ fileExtension = fileExtension.ToLower();
+
+ return this.pictureExt.Contains(fileExtension) ||
+ this.multimediaExt.Contains(fileExtension);
+ }
+
+ public bool CheckFileSize(string fileExtension, long fileSize)
+ {
+ var maxFileSize = this.GetMaxFileSize(fileExtension);
+ return maxFileSize == null || fileSize <= maxFileSize.Value;
+ }
+
+ public long? GetMaxFileSize(string fileExtension)
+ {
+ if (this.multimediaExt.Contains(fileExtension))
+ return null; // Multimedia : no check
+
+ return MaxFileSize;
+ }
+
+ public async Task PostStatusAsync(string text, long? inReplyToStatusId, string[] filePaths)
+ {
+ if (filePaths.Length != 1)
+ throw new ArgumentOutOfRangeException("filePaths");
+
+ var file = new FileInfo(filePaths[0]);
+
+ if (!file.Exists)
+ throw new ArgumentException("Err:File isn't exists.", "filePaths[0]");
+
+ var xml = await this.twitpicApi.UploadFileAsync(file, text)
+ .ConfigureAwait(false);
+
+ var imageUrlElm = xml.XPathSelectElement("/image/url");
+ if (imageUrlElm == null)
+ throw new WebApiException("Invalid API response", xml.ToString());
+
+ var textWithImageUrl = text + " " + imageUrlElm.Value.Trim();
+
+ await Task.Run(() => this.tw.PostStatus(textWithImageUrl, inReplyToStatusId))
+ .ConfigureAwait(false);
+ }
+
+ public int GetReservedTextLength(int mediaCount)
+ {
+ return this.twitterConfig.ShortUrlLength;
+ }
+
+ public void UpdateTwitterConfiguration(TwitterConfiguration config)
+ {
+ this.twitterConfig = config;
+ }
+
+ public class TwitpicApi : HttpConnectionOAuthEcho
+ {
+ private static readonly Uri UploadEndpoint = new Uri("http://api.twitpic.com/2/upload.xml");
+
+ public TwitpicApi(string twitterAccessToken, string twitterAccessTokenSecret)
+ : base(new Uri("http://api.twitter.com/"), new Uri("https://api.twitter.com/1.1/account/verify_credentials.json"))
+ {
+ this.Initialize(ApplicationSettings.TwitterConsumerKey, ApplicationSettings.TwitterConsumerSecret,
+ twitterAccessToken, twitterAccessTokenSecret, "", "");
+
+ this.InstanceTimeout = 120000;
+ }
+
+ /// <summary>
+ /// 画像のアップロードを行います
+ /// </summary>
+ /// <exception cref="WebApiException"/>
+ /// <exception cref="XmlException"/>
+ public async Task<XDocument> UploadFileAsync(FileInfo file, string message)
+ {
+ // 参照: http://dev.twitpic.com/docs/2/upload/
+
+ var param = new Dictionary<string, string>
+ {
+ {"key", ApplicationSettings.TwitpicApiKey},
+ {"message", message},
+ };
+ var paramFiles = new List<KeyValuePair<string, FileInfo>>
+ {
+ new KeyValuePair<string, FileInfo>("media", file),
+ };
+ var response = "";
+
+ var uploadTask = Task.Run(() => this.GetContent(HttpConnection.PostMethod,
+ UploadEndpoint, param, paramFiles, ref response, null, null));
+
+ var ret = await uploadTask.ConfigureAwait(false);
+
+ if (ret != HttpStatusCode.OK)
+ throw new WebApiException("Err:" + ret, response);
+
+ return XDocument.Parse(response);
+ }
+ }
+ }
}
// (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
// (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
// (c) 2011 spinor (@tplantd) <http://d.hatena.ne.jp/spinor/>
+// (c) 2014 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
// All rights reserved.
-//
+//
// This file is part of OpenTween.
-//
+//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option)
// any later version.
-//
+//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-// for more details.
-//
+// for more details.
+//
// You should have received a copy of the GNU General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
// Boston, MA 02110-1301, USA.
-using IMultimediaShareService = OpenTween.IMultimediaShareService;
-using Array = System.Array;
-using Convert = System.Convert;
-using Exception = System.Exception;
-using UploadFileType = OpenTween.MyCommon.UploadFileType;
-using MyCommon = OpenTween.MyCommon;
-using FileInfo = System.IO.FileInfo;
-using NotSupportedException = System.NotSupportedException;
+using System;
using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using OpenTween.Api;
-namespace OpenTween
+namespace OpenTween.Connection
{
- public class TwitterPhoto : IMultimediaShareService
- {
- private string[] pictureExt = new string[] { ".jpg", ".jpeg", ".gif", ".png" };
-
- private const long MaxfilesizeDefault = 3145728;
-
- // help/configurationにより取得されコンストラクタへ渡される
- private long _MaxFileSize = 3145728;
-
- private Twitter tw;
-
- public bool CheckValidExtension( string ext )
- {
- if ( Array.IndexOf( this.pictureExt, ext.ToLower() ) > -1 )
- return true;
-
- return false;
- }
-
- public bool CheckValidFilesize( string ext, long fileSize )
- {
- if ( this.CheckValidExtension( ext ) )
- return fileSize <= this._MaxFileSize;
-
- return false;
- }
-
- public bool Configuration( string key, object value )
- {
- if ( key == "MaxUploadFilesize" )
- {
- long val;
- try
- {
- val = Convert.ToInt64( value );
- if ( val > 0 )
- this._MaxFileSize = val;
- else
- this._MaxFileSize = TwitterPhoto.MaxfilesizeDefault;
- }
- catch ( Exception )
- {
- this._MaxFileSize = TwitterPhoto.MaxfilesizeDefault;
- return false; // error
- }
- return true; // 正常に設定終了
- }
- return true; // 設定項目がない場合はとりあえずエラー扱いにしない
- }
-
- public string GetFileOpenDialogFilter()
- {
- return "Image Files(*.gif;*.jpg;*.jpeg;*.png)|*.gif;*.jpg;*.jpeg;*.png";
- }
-
- public UploadFileType GetFileType( string ext )
- {
- if ( this.CheckValidExtension( ext ) )
- return UploadFileType.Picture;
-
- return UploadFileType.Invalid;
- }
-
- public bool IsSupportedFileType( UploadFileType type )
- {
- return type.Equals( UploadFileType.Picture );
- }
-
- public string Upload( ref string filePath, ref string message, long? reply_to )
- {
- if ( string.IsNullOrEmpty( filePath ) )
- return "Err:File isn't specified.";
-
- if ( string.IsNullOrEmpty( message ) )
- message = "";
-
- FileInfo mediaFile;
- try
- {
- mediaFile = new FileInfo( filePath );
- }
- catch ( NotSupportedException ex )
- {
- return "Err:" + ex.Message;
- }
-
- if ( !mediaFile.Exists )
- return "Err:File isn't exists.";
-
- if ( MyCommon.IsAnimatedGif( filePath ) )
- return "Err:Don't support animatedGIF.";
-
- return tw.PostStatusWithMedia( message, reply_to, mediaFile );
- }
-
- public string Upload(ref string[] filePaths, ref string message, long? reply_to)
+ public class TwitterPhoto : IMediaUploadService
+ {
+ private readonly string[] pictureExt = new[] { ".jpg", ".jpeg", ".gif", ".png" };
+
+ private readonly Twitter tw;
+ private TwitterConfiguration twitterConfig;
+
+ public TwitterPhoto(Twitter twitter, TwitterConfiguration twitterConfig)
{
- if (filePaths == null || filePaths.Length == 0 || string.IsNullOrEmpty(filePaths[0]))
- return "Err:File isn't specified.";
+ this.tw = twitter;
+ this.twitterConfig = twitterConfig;
+ }
- if (string.IsNullOrEmpty(message))
- message = "";
+ public int MaxMediaCount
+ {
+ get { return 4; }
+ }
+
+ public string SupportedFormatsStrForDialog
+ {
+ get
+ {
+ return "Image Files(*.gif;*.jpg;*.jpeg;*.png)|*.gif;*.jpg;*.jpeg;*.png";
+ }
+ }
+
+ public bool CheckFileExtension(string fileExtension)
+ {
+ return this.pictureExt.Contains(fileExtension.ToLower());
+ }
+
+ public bool CheckFileSize(string fileExtension, long fileSize)
+ {
+ var maxFileSize = this.GetMaxFileSize(fileExtension);
+ return maxFileSize == null || fileSize <= maxFileSize.Value;
+ }
+
+ public long? GetMaxFileSize(string fileExtension)
+ {
+ return this.twitterConfig.PhotoSizeLimit;
+ }
+
+ public async Task PostStatusAsync(string text, long? inReplyToStatusId, string[] filePaths)
+ {
+ if (filePaths == null || filePaths.Length == 0 || string.IsNullOrEmpty(filePaths[0]))
+ throw new ArgumentException("Err:File isn't specified.", "filePaths");
var mediaFiles = new List<FileInfo>();
{
if (string.IsNullOrEmpty(filePath)) continue;
- FileInfo mediaFile;
- try
- {
- mediaFile = new FileInfo(filePath);
- }
- catch (NotSupportedException ex)
- {
- return "Err:" + ex.Message;
- }
+ var mediaFile = new FileInfo(filePath);
if (!mediaFile.Exists)
- return "Err:File isn't exists.";
+ throw new ArgumentException("Err:File isn't exists.", "filePaths");
if (MyCommon.IsAnimatedGif(filePath))
- return "Err:Don't support animatedGIF.";
+ throw new ArgumentException("Err:Don't support animatedGIF.", "filePaths");
mediaFiles.Add(mediaFile);
}
- return tw.PostStatusWithMultipleMedia(message, reply_to, mediaFiles);
+ await Task.Run(() => this.tw.PostStatusWithMultipleMedia(text, inReplyToStatusId, mediaFiles))
+ .ConfigureAwait(false);
+ }
+
+ public int GetReservedTextLength(int mediaCount)
+ {
+ // 枚数に関わらず文字数は一定
+ return this.twitterConfig.ShortUrlLength;
+ }
+
+ public void UpdateTwitterConfiguration(TwitterConfiguration config)
+ {
+ this.twitterConfig = config;
}
-
- public TwitterPhoto(Twitter twitter)
- {
- this.tw = twitter;
- }
- }
+ }
}
// (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
// (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
// (c) 2011 spinor (@tplantd) <http://d.hatena.ne.jp/spinor/>
+// (c) 2014 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
// All rights reserved.
-//
+//
// This file is part of OpenTween.
-//
+//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option)
// any later version.
-//
+//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-// for more details.
-//
+// for more details.
+//
// You should have received a copy of the GNU General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
// Boston, MA 02110-1301, USA.
-using HttpConnectionOAuthEcho = OpenTween.HttpConnectionOAuthEcho;
-using IMultimediaShareService = OpenTween.IMultimediaShareService;
-using FileInfo = System.IO.FileInfo;
-using NotSupportedException = System.NotSupportedException;
-using HttpStatusCode = System.Net.HttpStatusCode;
-using Exception = System.Exception;
-using XmlDocument = System.Xml.XmlDocument;
-using XmlException = System.Xml.XmlException;
-using ArgumentException = System.ArgumentException;
-using System.Collections.Generic; // for Dictionary<TKey, TValue>, List<T>, KeyValuePair<TKey, TValue>
-using Uri = System.Uri;
-using Array = System.Array;
-using UploadFileType = OpenTween.MyCommon.UploadFileType;
-
-namespace OpenTween
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Threading.Tasks;
+using System.Xml;
+using System.Xml.Linq;
+using System.Xml.XPath;
+using OpenTween.Api;
+
+namespace OpenTween.Connection
{
- public class imgly : HttpConnectionOAuthEcho, IMultimediaShareService
- {
- private string[] pictureExt = new string[] { ".jpg", ".jpeg", ".gif", ".png" };
-
- private const long MaxFileSize = 4 * 1024 * 1024;
-
- private Twitter tw;
-
- public string Upload( ref string filePath, ref string message, long? reply_to )
- {
- if ( string.IsNullOrEmpty( filePath ) )
- return "Err:File isn't specified.";
- if ( string.IsNullOrEmpty( message ) )
- message = "";
-
- FileInfo mediaFile;
- try
- {
- mediaFile = new FileInfo( filePath );
- }
- catch ( NotSupportedException ex )
- {
- return "Err:" + ex.Message;
- }
- if ( mediaFile == null || !mediaFile.Exists )
- return "Err:File isn't exists.";
-
- string content = "";
- HttpStatusCode ret;
- // img.lyへの投稿
- try
- {
- ret = this.UploadFile( mediaFile, message, ref content );
- }
- catch ( Exception ex )
- {
- return "Err:" + ex.Message;
- }
-
- string url = "";
- if ( ret == HttpStatusCode.OK )
- {
- XmlDocument xd = new XmlDocument();
- try
- {
- xd.LoadXml( content );
- // URLの取得
- url = xd.SelectSingleNode( "/image/url" ).InnerText;
- }
- catch ( XmlException ex )
- {
- return "Err:" + ex.Message;
- }
- catch ( Exception ex )
- {
- return "Err:" + ex.Message;
- }
- }
- else
- {
- return "Err:" + ret.ToString();
- }
- // アップロードまでは成功
- filePath = "";
- if ( string.IsNullOrEmpty( url ) )
- url = "";
- // Twitterへの投稿
- // 投稿メッセージの再構成
- if ( string.IsNullOrEmpty( message ) )
- message = "";
- if ( message.Length + AppendSettingDialog.Instance.TwitterConfiguration.CharactersReservedPerMedia + 1 > 140 )
- message = message.Substring( 0, 140 - AppendSettingDialog.Instance.TwitterConfiguration.CharactersReservedPerMedia - 1 ) + " " + url;
- else
- message += " " + url;
-
- return tw.PostStatus( message, reply_to );
- }
-
- private HttpStatusCode UploadFile( FileInfo mediaFile, string message, ref string content )
- {
- // Message必須
- if ( string.IsNullOrEmpty( message ) )
- message = "";
- // Check filetype and size(Max 4MB)
- if ( !this.CheckValidExtension( mediaFile.Extension ) )
- throw new ArgumentException( "Service don't support this filetype." );
- if ( !this.CheckValidFilesize( mediaFile.Extension, mediaFile.Length ) )
- throw new ArgumentException( "File is too large." );
-
- Dictionary< string, string > param = new Dictionary< string, string >();
- param.Add( "message", message );
- List< KeyValuePair< string, FileInfo > > binary = new List< KeyValuePair< string, FileInfo > >();
- binary.Add( new KeyValuePair< string, FileInfo >( "media", mediaFile ) );
- this.InstanceTimeout = 60000; // タイムアウト60秒
-
- return this.GetContent( HttpConnection.PostMethod, new Uri( "http://img.ly/api/2/upload.xml" ), param, binary, ref content, null, null );
- }
-
- public bool CheckValidExtension( string ext )
- {
- if ( Array.IndexOf( this.pictureExt, ext.ToLower() ) > -1 )
- return true;
-
- return false;
- }
-
- public string GetFileOpenDialogFilter()
- {
- return "Image Files(*.gif;*.jpg;*.jpeg;*.png)|*.gif;*.jpg;*.jpeg;*.png";
- }
-
- public UploadFileType GetFileType( string ext )
- {
- if ( this.CheckValidExtension( ext ) )
- return UploadFileType.Picture;
-
- return UploadFileType.Invalid;
- }
-
- public bool IsSupportedFileType( UploadFileType type )
- {
- return type.Equals( UploadFileType.Picture );
- }
-
- public bool CheckValidFilesize( string ext, long fileSize )
- {
- if ( this.CheckValidExtension( ext ) )
- return fileSize <= imgly.MaxFileSize;
-
- return false;
- }
-
- public bool Configuration( string key, object value )
- {
- return true;
- }
-
- public imgly( Twitter twitter )
- : base( new Uri( "http://api.twitter.com/" ), new Uri( "https://api.twitter.com/1.1/account/verify_credentials.json" ) )
- {
- this.tw = twitter;
- this.Initialize( ApplicationSettings.TwitterConsumerKey, ApplicationSettings.TwitterConsumerSecret, tw.AccessToken, tw.AccessTokenSecret, "", "" );
- }
- }
+ public class imgly : IMediaUploadService
+ {
+ private readonly string[] pictureExt = new[] { ".jpg", ".jpeg", ".gif", ".png" };
+ private readonly long MaxFileSize = 4L * 1024 * 1024;
+
+ private readonly Twitter tw;
+ private readonly ImglyApi imglyApi;
+
+ private TwitterConfiguration twitterConfig;
+
+ public imgly(Twitter twitter, TwitterConfiguration twitterConfig)
+ {
+ this.tw = twitter;
+ this.twitterConfig = twitterConfig;
+
+ this.imglyApi = new ImglyApi(twitter.AccessToken, twitter.AccessTokenSecret);
+ }
+
+ public int MaxMediaCount
+ {
+ get { return 1; }
+ }
+
+ public string SupportedFormatsStrForDialog
+ {
+ get
+ {
+ return "Image Files(*.gif;*.jpg;*.jpeg;*.png)|*.gif;*.jpg;*.jpeg;*.png";
+ }
+ }
+
+ public bool CheckFileExtension(string fileExtension)
+ {
+ return this.pictureExt.Contains(fileExtension.ToLower());
+ }
+
+ public bool CheckFileSize(string fileExtension, long fileSize)
+ {
+ var maxFileSize = this.GetMaxFileSize(fileExtension);
+ return maxFileSize == null || fileSize <= maxFileSize.Value;
+ }
+
+ public long? GetMaxFileSize(string fileExtension)
+ {
+ return MaxFileSize;
+ }
+
+ public async Task PostStatusAsync(string text, long? inReplyToStatusId, string[] filePaths)
+ {
+ if (filePaths.Length != 1)
+ throw new ArgumentOutOfRangeException("filePaths");
+
+ var file = new FileInfo(filePaths[0]);
+
+ if (!file.Exists)
+ throw new ArgumentException("Err:File isn't exists.", "filePaths[0]");
+
+ var xml = await this.imglyApi.UploadFileAsync(file, text)
+ .ConfigureAwait(false);
+
+ var imageUrlElm = xml.XPathSelectElement("/image/url");
+ if (imageUrlElm == null)
+ throw new WebApiException("Invalid API response", xml.ToString());
+
+ var textWithImageUrl = text + " " + imageUrlElm.Value.Trim();
+
+ await Task.Run(() => this.tw.PostStatus(textWithImageUrl, inReplyToStatusId))
+ .ConfigureAwait(false);
+ }
+
+ public int GetReservedTextLength(int mediaCount)
+ {
+ return this.twitterConfig.ShortUrlLength;
+ }
+
+ public void UpdateTwitterConfiguration(TwitterConfiguration config)
+ {
+ this.twitterConfig = config;
+ }
+
+ public class ImglyApi : HttpConnectionOAuthEcho
+ {
+ private static readonly Uri UploadEndpoint = new Uri("http://img.ly/api/2/upload.xml");
+
+ public ImglyApi(string twitterAccessToken, string twitterAccessTokenSecret)
+ : base(new Uri("http://api.twitter.com/"), new Uri("https://api.twitter.com/1.1/account/verify_credentials.json"))
+ {
+ this.Initialize(ApplicationSettings.TwitterConsumerKey, ApplicationSettings.TwitterConsumerSecret,
+ twitterAccessToken, twitterAccessTokenSecret, "", "");
+
+ this.InstanceTimeout = 60000;
+ }
+
+ /// <summary>
+ /// 画像のアップロードを行います
+ /// </summary>
+ /// <exception cref="WebApiException"/>
+ /// <exception cref="XmlException"/>
+ public async Task<XDocument> UploadFileAsync(FileInfo file, string message)
+ {
+ // 参照: http://img.ly/api
+
+ var param = new Dictionary<string, string>
+ {
+ {"message", message},
+ };
+ var paramFiles = new List<KeyValuePair<string, FileInfo>>
+ {
+ new KeyValuePair<string, FileInfo>("media", file),
+ };
+ var response = "";
+
+ var uploadTask = Task.Run(() => this.GetContent(HttpConnection.PostMethod,
+ UploadEndpoint, param, paramFiles, ref response, null, null));
+
+ var ret = await uploadTask.ConfigureAwait(false);
+
+ if (ret != HttpStatusCode.OK)
+ throw new WebApiException("Err:" + ret, response);
+
+ return XDocument.Parse(response);
+ }
+ }
+ }
}
// (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
// (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
// (c) 2011 spinor (@tplantd) <http://d.hatena.ne.jp/spinor/>
+// (c) 2014 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
// All rights reserved.
-//
+//
// This file is part of OpenTween.
-//
+//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option)
// any later version.
-//
+//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-// for more details.
-//
+// for more details.
+//
// You should have received a copy of the GNU General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
// Boston, MA 02110-1301, USA.
-using HttpConnectionOAuthEcho = OpenTween.HttpConnectionOAuthEcho;
-using IMultimediaShareService = OpenTween.IMultimediaShareService;
-using FileInfo = System.IO.FileInfo;
-using NotSupportedException = System.NotSupportedException;
-using HttpStatusCode = System.Net.HttpStatusCode;
-using Exception = System.Exception;
-using XmlDocument = System.Xml.XmlDocument;
-using XmlException = System.Xml.XmlException;
-using ArgumentException = System.ArgumentException;
-using System.Collections.Generic; // for Dictionary<TKey, TValue>, List<T>, KeyValuePair<TKey, TValue>
-using Uri = System.Uri;
-using Array = System.Array;
-using UploadFileType = OpenTween.MyCommon.UploadFileType;
-
-namespace OpenTween
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Threading.Tasks;
+using System.Xml;
+using System.Xml.Linq;
+using System.Xml.XPath;
+using OpenTween.Api;
+
+namespace OpenTween.Connection
{
- public class yfrog : HttpConnectionOAuthEcho, IMultimediaShareService
- {
- private string[] pictureExt = new string[] { ".jpg", ".jpeg", ".gif", ".png" };
-
- private const long MaxFileSize = 5 * 1024 * 1024;
-
- private Twitter tw;
-
- public string Upload( ref string filePath, ref string message, long? reply_to )
- {
- if ( string.IsNullOrEmpty( filePath ) )
- return "Err:File isn't exists.";
- if ( string.IsNullOrEmpty( message ) )
- message = "";
-
- // FileInfo作成
- FileInfo mediaFile;
- try
- {
- mediaFile = new FileInfo( filePath );
- }
- catch ( NotSupportedException ex )
- {
- return "Err:" + ex.Message;
- }
- if ( mediaFile == null || !mediaFile.Exists )
- return "Err:File isn't exists.";
-
- string content = "";
- HttpStatusCode ret;
- // yfrogへの投稿
- try
- {
- ret = this.UploadFile( mediaFile, message, ref content );
- }
- catch ( Exception ex )
- {
- return "Err:" + ex.Message;
- }
- string url = "";
- if ( ret == HttpStatusCode.OK )
- {
- XmlDocument xd = new XmlDocument();
- try
- {
- xd.LoadXml( content );
- // URLの取得
- url = xd.SelectSingleNode( "/rsp/mediaurl" ).InnerText;
- }
- catch ( XmlException ex )
- {
- return "Err:" + ex.Message;
- }
- catch ( Exception ex )
- {
- return "Err:" + ex.Message;
- }
- }
- else
- return "Err:" + ret.ToString();
-
- if ( string.IsNullOrEmpty( url ) )
- url = "";
- // アップロードまでは成功
- filePath = "";
- // Twitterへの投稿
- // 投稿メッセージの再構成
- if ( string.IsNullOrEmpty( message ) )
- message = "";
- if ( message.Length + AppendSettingDialog.Instance.TwitterConfiguration.CharactersReservedPerMedia + 1 > 140 )
- message = message.Substring(0, 140 - AppendSettingDialog.Instance.TwitterConfiguration.CharactersReservedPerMedia - 1) + " " + url;
- else
- message += " " + url;
-
- return this.tw.PostStatus( message, reply_to );
- }
-
- private HttpStatusCode UploadFile( FileInfo mediaFile, string message, ref string content )
- {
- // Message必須
- if ( string.IsNullOrEmpty( message ) )
- message = "";
- // Check filetype and size(Max 5MB)
- if ( !this.CheckValidExtension( mediaFile.Extension ) )
- throw new ArgumentException( "Service don't support this filetype." );
- if ( !this.CheckValidFilesize( mediaFile.Extension, mediaFile.Length ) )
- throw new ArgumentException( "File is too large." );
-
- Dictionary< string, string > param = new Dictionary< string, string >();
- param.Add( "key", ApplicationSettings.YfrogApiKey );
- param.Add( "message", message );
- List< KeyValuePair< string, FileInfo > > binary = new List< KeyValuePair< string, FileInfo > >();
- binary.Add( new KeyValuePair< string, FileInfo >( "media", mediaFile ) );
- this.InstanceTimeout = 60000; // タイムアウト60秒
-
- return this.GetContent( HttpConnection.PostMethod, new Uri( "http://yfrog.com/api/xauth_upload" ), param, binary, ref content, null, null );
- }
-
- public bool CheckValidExtension( string ext )
- {
- if ( Array.IndexOf( this.pictureExt, ext.ToLower() ) > -1 )
- return true;
-
- return false;
- }
-
- public string GetFileOpenDialogFilter()
- {
- return "Image Files(*.gif;*.jpg;*.jpeg;*.png)|*.gif;*.jpg;*.jpeg;*.png";
- }
-
- public UploadFileType GetFileType( string ext )
- {
- if ( this.CheckValidExtension( ext ) )
- return UploadFileType.Picture;
-
- return UploadFileType.Invalid;
- }
-
- public bool IsSupportedFileType( UploadFileType type )
- {
- return type.Equals( UploadFileType.Picture );
- }
-
- public bool CheckValidFilesize( string ext, long fileSize )
- {
- if ( this.CheckValidExtension( ext ) )
- return fileSize <= yfrog.MaxFileSize;
-
- return false;
- }
-
- public yfrog( Twitter twitter )
- : base( new Uri( "http://api.twitter.com/" ), new Uri( "https://api.twitter.com/1.1/account/verify_credentials.xml" ) )
- {
- this.tw = twitter;
- this.Initialize( ApplicationSettings.TwitterConsumerKey, ApplicationSettings.TwitterConsumerSecret, this.tw.AccessToken, this.tw.AccessTokenSecret, "", "" );
- }
-
- public bool Configuration( string key, object value )
- {
- return true;
- }
- }
+ public class yfrog : IMediaUploadService
+ {
+ private readonly string[] pictureExt = new[] { ".jpg", ".jpeg", ".gif", ".png" };
+ private readonly long MaxFileSize = 5L * 1024 * 1024;
+
+ private readonly Twitter tw;
+ private readonly YfrogApi yfrogApi;
+
+ private TwitterConfiguration twitterConfig;
+
+ public yfrog(Twitter twitter, TwitterConfiguration twitterConfig)
+ {
+ this.tw = twitter;
+ this.twitterConfig = twitterConfig;
+
+ this.yfrogApi = new YfrogApi(twitter.AccessToken, twitter.AccessTokenSecret);
+ }
+
+ public int MaxMediaCount
+ {
+ get { return 1; }
+ }
+
+ public string SupportedFormatsStrForDialog
+ {
+ get
+ {
+ return "Image Files(*.gif;*.jpg;*.jpeg;*.png)|*.gif;*.jpg;*.jpeg;*.png";
+ }
+ }
+
+ public bool CheckFileExtension(string fileExtension)
+ {
+ return this.pictureExt.Contains(fileExtension.ToLower());
+ }
+
+ public bool CheckFileSize(string fileExtension, long fileSize)
+ {
+ var maxFileSize = this.GetMaxFileSize(fileExtension);
+ return maxFileSize == null || fileSize <= maxFileSize.Value;
+ }
+
+ public long? GetMaxFileSize(string fileExtension)
+ {
+ return MaxFileSize;
+ }
+
+ public async Task PostStatusAsync(string text, long? inReplyToStatusId, string[] filePaths)
+ {
+ if (filePaths.Length != 1)
+ throw new ArgumentOutOfRangeException("filePaths");
+
+ var file = new FileInfo(filePaths[0]);
+
+ if (!file.Exists)
+ throw new ArgumentException("Err:File isn't exists.", "filePaths[0]");
+
+ var xml = await this.yfrogApi.UploadFileAsync(file, text)
+ .ConfigureAwait(false);
+
+ var imageUrlElm = xml.XPathSelectElement("/rsp/mediaurl");
+ if (imageUrlElm == null)
+ throw new WebApiException("Invalid API response", xml.ToString());
+
+ var textWithImageUrl = text + " " + imageUrlElm.Value.Trim();
+
+ await Task.Run(() => this.tw.PostStatus(textWithImageUrl, inReplyToStatusId))
+ .ConfigureAwait(false);
+ }
+
+ public int GetReservedTextLength(int mediaCount)
+ {
+ return this.twitterConfig.ShortUrlLength;
+ }
+
+ public void UpdateTwitterConfiguration(TwitterConfiguration config)
+ {
+ this.twitterConfig = config;
+ }
+
+ public class YfrogApi : HttpConnectionOAuthEcho
+ {
+ private static readonly Uri UploadEndpoint = new Uri("https://yfrog.com/api/xauth_upload");
+
+ public YfrogApi(string twitterAccessToken, string twitterAccessTokenSecret)
+ : base(new Uri("http://api.twitter.com/"), new Uri("https://api.twitter.com/1.1/account/verify_credentials.xml"))
+ {
+ this.Initialize(ApplicationSettings.TwitterConsumerKey, ApplicationSettings.TwitterConsumerSecret,
+ twitterAccessToken, twitterAccessTokenSecret, "", "");
+
+ this.InstanceTimeout = 60000;
+ }
+
+ /// <summary>
+ /// 画像のアップロードを行います
+ /// </summary>
+ /// <exception cref="WebApiException"/>
+ /// <exception cref="XmlException"/>
+ public async Task<XDocument> UploadFileAsync(FileInfo file, string message)
+ {
+ // 参照: http://twitter.yfrog.com/page/api#a1
+
+ var param = new Dictionary<string, string>
+ {
+ {"key", ApplicationSettings.YfrogApiKey},
+ };
+ var paramFiles = new List<KeyValuePair<string, FileInfo>>
+ {
+ new KeyValuePair<string, FileInfo>("media", file),
+ };
+ var response = "";
+
+ var uploadTask = Task.Run(() => this.GetContent(HttpConnection.PostMethod,
+ UploadEndpoint, param, paramFiles, ref response, null, null));
+
+ var ret = await uploadTask.ConfigureAwait(false);
+
+ if (ret != HttpStatusCode.OK)
+ throw new WebApiException("Err:" + ret, response);
+
+ return XDocument.Parse(response);
+ }
+ }
+ }
}
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
+using OpenTween.Api;
using OpenTween.Connection;
namespace OpenTween
}
/// <summary>
- /// 指定された投稿先名から、作成済みの IMultimediaShareService インスタンスを取得する。
+ /// 指定された投稿先名から、作成済みの IMediaUploadService インスタンスを取得する。
/// </summary>
- public IMultimediaShareService GetService(string serviceName)
+ public IMediaUploadService GetService(string serviceName)
{
- IMultimediaShareService service;
+ IMediaUploadService service;
this.pictureService.TryGetValue(serviceName, out service);
return service;
}
+ /// <summary>
+ /// 利用可能な全ての IMediaUploadService インスタンスを取得する。
+ /// </summary>
+ public ICollection<IMediaUploadService> GetServices()
+ {
+ return this.pictureService.Values;
+ }
+
private class SelectedMedia
{
public string Path { get; set; }
}
}
- private Dictionary<string, IMultimediaShareService> pictureService;
+ private Dictionary<string, IMediaUploadService> pictureService;
- private void CreateServices(Twitter tw)
+ private void CreateServices(Twitter tw, TwitterConfiguration twitterConfig)
{
if (this.pictureService != null) this.pictureService.Clear();
this.pictureService = null;
- this.pictureService = new Dictionary<string, IMultimediaShareService> {
- {"TwitPic", new TwitPic(tw)},
- {"img.ly", new imgly(tw)},
- {"yfrog", new yfrog(tw)},
- {"Twitter", new TwitterPhoto(tw)},
- {"ついっぷるフォト", new TwipplePhoto(tw)},
- {"Imgur", new Imgur(tw)},
+ this.pictureService = new Dictionary<string, IMediaUploadService> {
+ {"TwitPic", new TwitPic(tw, twitterConfig)},
+ {"img.ly", new imgly(tw, twitterConfig)},
+ {"yfrog", new yfrog(tw, twitterConfig)},
+ {"Twitter", new TwitterPhoto(tw, twitterConfig)},
+ {"ついっぷるフォト", new TwipplePhoto(tw, twitterConfig)},
+ {"Imgur", new Imgur(tw, twitterConfig)},
};
}
/// <summary>
/// 投稿先サービスなどを初期化する。
/// </summary>
- public void Initialize(Twitter tw, string svc, int? index = null)
+ public void Initialize(Twitter tw, TwitterConfiguration twitterConfig, string svc, int? index = null)
{
- CreateServices(tw);
+ CreateServices(tw, twitterConfig);
SetImageServiceCombo();
SetImagePageCombo();
/// <summary>
/// 投稿先サービスを再作成する。
/// </summary>
- public void Reset(Twitter tw)
+ public void Reset(Twitter tw, TwitterConfiguration twitterConfig)
{
- CreateServices(tw);
+ CreateServices(tw, twitterConfig);
SetImageServiceCombo();
}
var serviceName = this.ServiceName;
if (!string.IsNullOrEmpty(serviceName) &&
- this.pictureService[serviceName].CheckValidFilesize(ext, fl.Length))
+ this.pictureService[serviceName].CheckFileSize(ext, fl.Length))
{
return true;
}
foreach (string svc in ImageServiceCombo.Items)
{
if (!string.IsNullOrEmpty(svc) &&
- this.pictureService[svc].CheckValidFilesize(ext, fl.Length))
+ this.pictureService[svc].CheckFileSize(ext, fl.Length))
{
return true;
}
private void FilePickButton_Click(object sender, EventArgs e)
{
if (FilePickDialog == null || string.IsNullOrEmpty(this.ServiceName)) return;
- FilePickDialog.Filter = this.pictureService[this.ServiceName].GetFileOpenDialogFilter();
+ FilePickDialog.Filter = this.pictureService[this.ServiceName].SupportedFormatsStrForDialog;
FilePickDialog.Title = Properties.Resources.PickPictureDialog1;
FilePickDialog.FileName = "";
string ext = fl.Extension;
var imageService = this.pictureService[serviceName];
- if (!imageService.CheckValidExtension(ext))
+ if (!imageService.CheckFileExtension(ext))
{
//画像以外の形式
ClearSelectedImagePage();
return;
}
- if (!imageService.CheckValidFilesize(ext, fl.Length))
+ if (!imageService.CheckFileSize(ext, fl.Length))
{
// ファイルサイズが大きすぎる
ClearSelectedImagePage();
return;
}
- switch (imageService.GetFileType(ext))
+ try
{
- case MyCommon.UploadFileType.Picture:
- using (var fs = File.OpenRead(fileName))
- {
- ImageSelectedPicture.Image = MemoryImage.CopyFromStream(fs);
- }
- SetSelectedImagePage(fileName, MyCommon.UploadFileType.Picture);
- break;
- case MyCommon.UploadFileType.MultiMedia:
- SetSelectedImagePage(fileName, MyCommon.UploadFileType.MultiMedia);
- break;
- default:
- ClearSelectedImagePage();
- break;
+ using (var fs = File.OpenRead(fileName))
+ {
+ ImageSelectedPicture.Image = MemoryImage.CopyFromStream(fs);
+ }
+ SetSelectedImagePage(fileName, MyCommon.UploadFileType.Picture);
+ }
+ catch (InvalidImageException)
+ {
+ SetSelectedImagePage(fileName, MyCommon.UploadFileType.MultiMedia);
}
}
catch (FileNotFoundException)
{
var text = string.Join(", ",
ImageServiceCombo.Items.Cast<string>()
- .Where(x => !string.IsNullOrEmpty(x) && this.pictureService[x].CheckValidFilesize(ext, fileSize)));
+ .Where(x => !string.IsNullOrEmpty(x) && this.pictureService[x].CheckFileSize(ext, fileSize)));
if (string.IsNullOrEmpty(text))
return Properties.Resources.PostPictureWarn6;
FileInfo fi = new FileInfo(ImagefilePathText.Text.Trim());
string ext = fi.Extension;
var imageService = this.pictureService[serviceName];
- if (!imageService.CheckValidFilesize(ext, fi.Length))
+ if (!imageService.CheckFileSize(ext, fi.Length))
{
ClearImageSelectedPicture();
ClearSelectedImagePage();
<Compile Include="Connection\HttpConnectionOAuth.cs" />
<Compile Include="Connection\HttpConnectionOAuthEcho.cs" />
<Compile Include="Connection\IHttpConnection.cs" />
+ <Compile Include="Connection\IMediaUploadService.cs" />
<Compile Include="Connection\imgly.cs" />
<Compile Include="Connection\Imgur.cs" />
- <Compile Include="Connection\IMultimediaShareService.cs" />
<Compile Include="Connection\TwipplePhoto.cs" />
<Compile Include="EventViewerDialog.cs">
<SubType>Form</SubType>
AllrepliesToolStripMenuItem.Checked = tw.AllAtReply;
//画像投稿サービス
- ImageSelector.Initialize(tw, _cfgCommon.UseImageServiceName, _cfgCommon.UseImageService);
+ ImageSelector.Initialize(tw, SettingDialog.TwitterConfiguration, _cfgCommon.UseImageServiceName, _cfgCommon.UseImageService);
//ウィンドウ設定
this.ClientSize = _cfgLocal.FormSize;
else
{
var service = ImageSelector.GetService(args.status.imageService);
- if (args.status.imagePath.Length > 1 &&
- args.status.imageService.Equals("Twitter"))
+ try
{
- //複数画像投稿
- ret = ((TwitterPhoto)service).Upload(ref args.status.imagePath,
- ref args.status.status,
- args.status.inReplyToId);
+ service.PostStatusAsync(args.status.status, args.status.inReplyToId, args.status.imagePath)
+ .Wait();
}
- else
+ catch (AggregateException ex)
{
- ret = service.Upload(ref args.status.imagePath[0],
- ref args.status.status,
- args.status.inReplyToId);
+ ret = ex.InnerException.Message;
}
}
bw.ReportProgress(300);
//_waitFollower = false
if (SettingDialog.TwitterConfiguration.PhotoSizeLimit != 0)
{
- var service = ImageSelector.GetService("Twitter");
- if (service != null)
- service.Configuration("MaxUploadFilesize", SettingDialog.TwitterConfiguration.PhotoSizeLimit);
+ foreach (var service in this.ImageSelector.GetServices())
+ {
+ service.UpdateTwitterConfiguration(this.SettingDialog.TwitterConfiguration);
+ }
}
this.PurgeListViewItemCache();
if (_curList != null) _curList.Refresh();
SettingDialog.ProxyUser,
SettingDialog.ProxyPassword);
- ImageSelector.Reset(tw);
+ ImageSelector.Reset(tw, SettingDialog.TwitterConfiguration);
try
{