OSDN Git Service

Twitterへの複数画像の投稿に対応した
authorspx <spx268@gmail.com>
Sun, 1 Jun 2014 13:28:06 +0000 (22:28 +0900)
committerspx <spx268@gmail.com>
Sun, 1 Jun 2014 13:28:06 +0000 (22:28 +0900)
OpenTween/Api/TwitterUploadMediaResult.cs [new file with mode: 0644]
OpenTween/Connection/HttpTwitter.cs
OpenTween/Connection/TwitterPhoto.cs
OpenTween/OpenTween.csproj
OpenTween/Tween.cs
OpenTween/Twitter.cs

diff --git a/OpenTween/Api/TwitterUploadMediaResult.cs b/OpenTween/Api/TwitterUploadMediaResult.cs
new file mode 100644 (file)
index 0000000..6e7e05d
--- /dev/null
@@ -0,0 +1,68 @@
+// 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);
+        }
+    }
+}
index 3299c6d..e4c54f6 100644 (file)
@@ -164,7 +164,7 @@ namespace OpenTween
             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);
@@ -172,6 +172,19 @@ namespace OpenTween
             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,
@@ -201,6 +214,21 @@ namespace OpenTween
                 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;
@@ -840,6 +868,7 @@ namespace OpenTween
         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)
         {
@@ -856,6 +885,11 @@ namespace OpenTween
             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
index 27fb255..67e4bcf 100644 (file)
@@ -32,6 +32,7 @@ using UploadFileType = OpenTween.MyCommon.UploadFileType;
 using MyCommon = OpenTween.MyCommon;
 using FileInfo = System.IO.FileInfo;
 using NotSupportedException = System.NotSupportedException;
+using System.Collections.Generic;
 
 namespace OpenTween
 {
@@ -130,7 +131,43 @@ 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;
                }
index 448eed4..fa8ff0c 100644 (file)
@@ -76,6 +76,7 @@
     <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" />
index 1ea2672..554eb3f 100644 (file)
@@ -296,7 +296,7 @@ namespace OpenTween
             public long? inReplyToId = null;
             public string inReplyToName = null;
             public string imageService = "";      //画像投稿サービス名
-            public string imagePath = "";
+            public string[] imagePath = null;
             public PostingStatus()
             {
             }
@@ -2296,12 +2296,8 @@ namespace OpenTween
             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);
@@ -2520,15 +2516,27 @@ namespace OpenTween
 
                 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;
index 4021a46..1f5830a 100644 (file)
@@ -457,13 +457,14 @@ namespace OpenTween
             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);
             }
@@ -472,7 +473,7 @@ namespace OpenTween
             var content = "";
             try
             {
-                res = twCon.UpdateStatus(postStr, reply_to, ref content);
+                res = twCon.UpdateStatus(postStr, reply_to, mediaIds, ref content);
             }
             catch(Exception ex)
             {
@@ -564,6 +565,68 @@ namespace OpenTween
             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 "";