--- /dev/null
+// OpenTween - Client of Twitter
+// Copyright (c) 2014 spx (@5px)
+// 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.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenTween.Api
+{
+ // 参照: https://dev.twitter.com/docs/platform-objects/entities
+ // https://dev.twitter.com/docs/api/multiple-media-extended-entities
+
+ [DataContract]
+ public class TwitterUploadMediaResult
+ {
+ [DataContract]
+ public class ImageInfo
+ {
+ [DataMember(Name = "w")]
+ public int Width { get; set; }
+
+ [DataMember(Name = "h")]
+ public int Height { get; set; }
+
+ [DataMember(Name = "image_type")]
+ public string ImageType { get; set; }
+ }
+
+ [DataMember(Name = "image")]
+ public TwitterUploadMediaResult.ImageInfo MediaInfo { get; set; }
+
+ [DataMember(Name = "media_id")]
+ public long MediaId { get; set; }
+
+ [DataMember(Name = "media_id_string")]
+ public string MediaIdStr { get; set; }
+
+ [DataMember(Name = "size")]
+ public long Size { get; set; }
+
+ /// <exception cref="SerializationException"/>
+ public static TwitterUploadMediaResult ParseJson(string json)
+ {
+ return MyCommon.CreateDataFromJson<TwitterUploadMediaResult>(json);
+ }
+ }
+}
this.Initialize("", "", "", 0);
}
- public HttpStatusCode UpdateStatus(string status, long? replyToId, ref string content)
+ public HttpStatusCode UpdateStatus(string status, long? replyToId, List<long> mediaIds, ref string content)
{
Dictionary<string, string> param = new Dictionary<string, string>();
param.Add("status", status);
param.Add("include_entities", "true");
//if (AppendSettingDialog.Instance.ShortenTco && AppendSettingDialog.Instance.UrlConvertAuto) param.Add("wrap_links", "true")
+ if (mediaIds != null)
+ {
+ StringBuilder sb = new StringBuilder();
+ foreach (var idStr in mediaIds.Select(x => x.ToString()))
+ {
+ if (string.IsNullOrEmpty(idStr)) continue;
+ if (sb.Length > 0) sb.Append(",");
+ sb.Append(idStr);
+ }
+
+ if (sb.Length > 0) param.Add("media_ids", sb.ToString());
+ }
+
return httpCon.GetContent(PostMethod,
this.CreateTwitterUri("/1.1/statuses/update.json"),
param,
this.CreateApiCalllback("/statuses/update_with_media"));
}
+ public HttpStatusCode UploadMedia(FileInfo mediaFile, ref string content)
+ {
+ //画像投稿専用エンドポイント
+ List<KeyValuePair<string, FileInfo>> binary = new List<KeyValuePair<string, FileInfo>>();
+ binary.Add(new KeyValuePair<string, FileInfo>("media", mediaFile));
+
+ return httpCon.GetContent(PostMethod,
+ this.CreateTwitterUploadUri("/1.1/media/upload.json"),
+ null,
+ binary,
+ ref content,
+ null,
+ null);
+ }
+
public HttpStatusCode DestroyStatus(long id)
{
string content = null;
private static string _twitterUrl = "api.twitter.com";
private static string _twitterUserStreamUrl = "userstream.twitter.com";
private static string _twitterStreamUrl = "stream.twitter.com";
+ private static string _twitterUploadUrl = "upload.twitter.com";
private Uri CreateTwitterUri(string path)
{
return new Uri(string.Format("{0}{1}{2}", "http://", _twitterStreamUrl, path));
}
+ private Uri CreateTwitterUploadUri(string path)
+ {
+ return new Uri(string.Format("{0}{1}{2}", "https://", _twitterUploadUrl, path));
+ }
+
public static string TwitterUrl
{
set
using MyCommon = OpenTween.MyCommon;
using FileInfo = System.IO.FileInfo;
using NotSupportedException = System.NotSupportedException;
+using System.Collections.Generic;
namespace OpenTween
{
return tw.PostStatusWithMedia( message, reply_to, mediaFile );
}
- public TwitterPhoto( Twitter twitter )
+ public string Upload(ref string[] filePaths, ref string message, long? reply_to)
+ {
+ if (filePaths == null || filePaths.Length == 0 || string.IsNullOrEmpty(filePaths[0]))
+ return "Err:File isn't specified.";
+
+ if (string.IsNullOrEmpty(message))
+ message = "";
+
+ var mediaFiles = new List<FileInfo>();
+
+ foreach (var filePath in filePaths)
+ {
+ if (string.IsNullOrEmpty(filePath)) continue;
+
+ 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.";
+
+ mediaFiles.Add(mediaFile);
+ }
+
+ return tw.PostStatusWithMultipleMedia(message, reply_to, mediaFiles);
+ }
+
+ public TwitterPhoto(Twitter twitter)
{
this.tw = twitter;
}
<Compile Include="Api\TwitterSearchResult.cs" />
<Compile Include="Api\TwitterStatus.cs" />
<Compile Include="Api\TwitterStreamEvent.cs" />
+ <Compile Include="Api\TwitterUploadMediaResult.cs" />
<Compile Include="Api\TwitterUser.cs" />
<Compile Include="Api\TwitterApiAccessLevel.cs" />
<Compile Include="Api\TwitterApiStatus.cs" />
public long? inReplyToId = null;
public string inReplyToName = null;
public string imageService = ""; //画像投稿サービス名
- public string imagePath = "";
+ public string[] imagePath = null;
public PostingStatus()
{
}
if (ImageSelectionPanel.Visible)
{
//画像投稿
- string[] imagePaths;
- if (!TryGetSelectedMedia(out args.status.imageService, out imagePaths))
+ if (!TryGetSelectedMedia(out args.status.imageService, out args.status.imagePath))
return;
-
- // TODO: 複数画像投稿への対応
- args.status.imagePath = imagePaths[0];
}
RunAsync(args);
case MyCommon.WORKERTYPE.PostMessage:
bw.ReportProgress(200);
- if (string.IsNullOrEmpty(args.status.imagePath))
+ if (args.status.imagePath == null || args.status.imagePath.Length == 0 || string.IsNullOrEmpty(args.status.imagePath[0]))
{
ret = tw.PostStatus(args.status.status, args.status.inReplyToId);
}
else
{
- ret = this.pictureService[args.status.imageService].Upload(ref args.status.imagePath,
- ref args.status.status,
- args.status.inReplyToId);
+ var service = this.pictureService[args.status.imageService];
+ if (args.status.imagePath.Length > 1 &&
+ args.status.imageService.Equals("Twitter"))
+ {
+ //複数画像投稿
+ ret = ((TwitterPhoto)service).Upload(ref args.status.imagePath,
+ ref args.status.status,
+ args.status.inReplyToId);
+ }
+ else
+ {
+ ret = service.Upload(ref args.status.imagePath[0],
+ ref args.status.status,
+ args.status.inReplyToId);
+ }
}
bw.ReportProgress(300);
rslt.status = args.status;
return false;
}
- public string PostStatus(string postStr, long? reply_to)
+ public string PostStatus(string postStr, long? reply_to, List<long> mediaIds = null)
{
if (MyCommon._endingFlag) return "";
if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
- if (Regex.Match(postStr, "^DM? +(?<id>[a-zA-Z0-9_]+) +(?<body>.+)", RegexOptions.IgnoreCase | RegexOptions.Singleline).Success)
+ if (mediaIds == null &&
+ Regex.Match(postStr, "^DM? +(?<id>[a-zA-Z0-9_]+) +(?<body>.+)", RegexOptions.IgnoreCase | RegexOptions.Singleline).Success)
{
return SendDirectMessage(postStr);
}
var content = "";
try
{
- res = twCon.UpdateStatus(postStr, reply_to, ref content);
+ res = twCon.UpdateStatus(postStr, reply_to, mediaIds, ref content);
}
catch(Exception ex)
{
return "";
}
+ public string PostStatusWithMultipleMedia(string postStr, long? reply_to, List<FileInfo> mediaFiles)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ var mediaIds = new List<long>();
+
+ foreach (var mediaFile in mediaFiles)
+ {
+ long? mediaId = null;
+ var err = UploadMedia(mediaFile, ref mediaId);
+ if (!mediaId.HasValue || !string.IsNullOrEmpty(err)) return err;
+ mediaIds.Add(mediaId.Value);
+ }
+
+ if (mediaIds.Count == 0)
+ return "Err:Invalid Files!";
+
+ return PostStatus(postStr, reply_to, mediaIds);
+ }
+
+ public string UploadMedia(FileInfo mediaFile, ref long? mediaId)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res;
+ var content = "";
+ try
+ {
+ res = twCon.UploadMedia(mediaFile, ref content);
+ }
+ catch (Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+
+ var err = this.CheckStatusCode(res, content);
+ if (err != null) return err;
+
+ TwitterUploadMediaResult status;
+ try
+ {
+ status = TwitterUploadMediaResult.ParseJson(content);
+ }
+ catch (SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch (Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+
+ mediaId = status.MediaId;
+ return "";
+ }
+
public string SendDirectMessage(string postStr)
{
if (MyCommon._endingFlag) return "";