OSDN Git Service

www.instagram.com のサムネイル表示に対応
[opentween/open-tween.git] / OpenTween / Thumbnail / ThumbnailGenerator.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2012 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.Linq;
25 using System.Net.Http;
26 using System.Text;
27 using System.Text.RegularExpressions;
28 using System.Threading;
29 using System.Threading.Tasks;
30 using OpenTween.Thumbnail.Services;
31
32 namespace OpenTween.Thumbnail
33 {
34     class ThumbnailGenerator
35     {
36         public static List<IThumbnailService> Services { get; protected set; }
37
38         internal static ImgAzyobuziNet ImgAzyobuziNetInstance { get; private set; }
39
40         static ThumbnailGenerator()
41         {
42             ThumbnailGenerator.Services = new List<IThumbnailService>();
43         }
44
45         public static void InitializeGenerator()
46         {
47             ImgAzyobuziNetInstance = new ImgAzyobuziNet(autoupdate: true);
48
49             ThumbnailGenerator.Services = new List<IThumbnailService>()
50             {
51                 // ton.twitter.com
52                 new TonTwitterCom(),
53
54                 // twitter.com/tweet_video
55                 new TwitterComVideo(),
56
57                 // pic.twitter.com
58                 new SimpleThumbnailService(
59                     @"^(https?://pbs\.twimg\.com/[^:]+)(?:\:.+)?$",
60                     "${1}",
61                     "${1}:orig"),
62
63                 // youtube
64                 new Youtube(),
65
66                 // ニコニコ動画
67                 new Nicovideo(),
68
69                 // vimeo
70                 new Vimeo(),
71
72                 // DirectLink
73                 new SimpleThumbnailService(@"^https?://.*(\.jpg|\.jpeg|\.gif|\.png|\.bmp)$", "${0}"),
74
75                 // img.azyobuzi.net
76                 ImgAzyobuziNetInstance,
77
78                 // ImgUr
79                 new SimpleThumbnailService(
80                     @"^https?://(?:i\.)?imgur\.com/(\w+)(?:\..{3})?$",
81                     "http://img.imgur.com/${1}l.jpg",
82                     "http://img.imgur.com/${1}.jpg"),
83
84                 // Twitpic
85                 new SimpleThumbnailService(
86                     @"^http://(www\.)?twitpic\.com/(?<photoId>\w+)(/full/?)?$",
87                     "http://twitpic.com/show/thumb/${photoId}",
88                     "http://twitpic.com/show/large/${photoId}"),
89
90                 // yfrog
91                 new SimpleThumbnailService(
92                     @"^https?://yfrog\.com/(\w+)$",
93                     "${0}:small",
94                     "${0}"),
95
96                 // Lockerz
97                 new SimpleThumbnailService(
98                     @"^https?://(tweetphoto\.com/[0-9]+|pic\.gd/[a-z0-9]+|(lockerz|plixi)\.com/[ps]/[0-9]+)$",
99                     "http://api.plixi.com/api/tpapi.svc/imagefromurl?size=thumbnail&url=${0}",
100                     "http://api.plixi.com/api/tpapi.svc/imagefromurl?size=big&url=${0}"),
101
102                 // MobyPicture
103                 new MetaThumbnailService(
104                     @"^https?://(?:www\.)?mobypicture.com/user/\w+/view/\d+$",
105                     new string[] { "og:image" }),  // twitter:imageは403が返って取得できない
106
107                 // 携帯百景
108                 new SimpleThumbnailService(
109                     @"^https?://movapic\.com/pic/(\w+)$",
110                     "http://image.movapic.com/pic/s_${1}.jpeg",
111                     "http://image.movapic.com/pic/m_${1}.jpeg"),
112
113                 // はてなフォトライフ
114                 new SimpleThumbnailService(
115                     @"^https?://f\.hatena\.ne\.jp/(([a-z])[a-z0-9_-]{1,30}[a-z0-9])/((\d{8})\d+)$",
116                     "http://img.f.hatena.ne.jp/images/fotolife/${2}/${1}/${4}/${3}_120.jpg",
117                     "http://img.f.hatena.ne.jp/images/fotolife/${2}/${1}/${4}/${3}.jpg"),
118
119                 // PhotoShare
120                 new SimpleThumbnailService(@"^https?://(?:www\.)?bcphotoshare\.com/photos/\d+/(\d+)$", "http://images.bcphotoshare.com/storages/${1}/thumb180.jpg"),
121
122                 // img.ly
123                 new SimpleThumbnailService(@"^https?://img\.ly/(\w+)$",
124                     "http://img.ly/show/thumb/${1}",
125                     "http://img.ly/show/full/${1}"),
126
127                 // Twitgoo
128                 new SimpleThumbnailService(@"^https?://twitgoo\.com/(\w+)$",
129                     "http://twitgoo.com/${1}/mini",
130                     "http://twitgoo.com/${1}/img"),
131
132                 // ニコニコ静画
133                 new SimpleThumbnailService(
134                     @"^https?://(?:seiga\.nicovideo\.jp/seiga/|nico\.ms/)im(?<id>\d+)",
135                     "http://lohas.nicoseiga.jp/thumb/${id}q?",
136                     "http://lohas.nicoseiga.jp/thumb/${id}l?"),
137
138                 // pixiv
139                 new Pixiv(),
140
141                 // flickr
142                 new MetaThumbnailService(@"^https?://www\.flickr\.com/.+$"),
143
144                 // フォト蔵
145                 new SimpleThumbnailService(
146                     @"^https?://photozou\.jp/photo/show/(?<userId>[0-9]+)/(?<photoId>[0-9]+)",
147                     "http://photozou.jp/p/thumb/${photoId}",
148                     "http://photozou.jp/p/img/${photoId}"),
149
150                 // Piapro
151                 new MetaThumbnailService(@"^https?://piapro\.jp/(?:content/[0-9a-z]+|t/[0-9a-zA-Z_\-]+)$"),
152
153                 // Tumblr
154                 new Tumblr(),
155
156                 // ついっぷるフォト
157                 new SimpleThumbnailService(@"^https?://p\.twipple\.jp/(?<contentId>[0-9a-z]+)", "http://p.twipple.jp/show/large/${contentId}"),
158
159                 // mypix/shamoji
160                 new SimpleThumbnailService(@"^https?://www\.(mypix\.jp|shamoji\.info)/app\.php/picture/(?<contentId>[0-9a-z]+)", "${0}/thumb.jpg"),
161
162                 // ow.ly
163                 new SimpleThumbnailService(@"^https?://ow\.ly/i/(\w+)$", "http://static.ow.ly/photos/thumb/${1}.jpg"),
164
165                 // cloudfiles
166                 new SimpleThumbnailService(@"^https?://c[0-9]+\.cdn[0-9]+\.cloudfiles\.rackspacecloud\.com/[a-z_0-9]+", "${0}"),
167
168                 // Instagram
169                 new SimpleThumbnailService(
170                     @"^https?://(?:instagram.com|instagr\.am|i\.instagram\.com|www\.instagram\.com)/p/.+/",
171                     "${0}media/?size=m",
172                     "${0}media/?size=l"),
173
174                 // pikubo
175                 new SimpleThumbnailService(
176                     @"^https?://pikubo\.me/([a-z0-9-_]+)",
177                     "http://pikubo.me/q/${1}",
178                     "http://pikubo.me/l/${1}"),
179
180                 // Foursquare
181                 new FoursquareCheckin(),
182
183                 // TINAMI
184                 new Tinami(),
185
186                 // TwitrPix
187                 new SimpleThumbnailService(
188                     @"^https?://twitrpix\.com/(\w+)$",
189                     "http://img.twitrpix.com/thumb/${1}",
190                     "http://img.twitrpix.com/${1}"),
191
192                 // Pckles
193                 new SimpleThumbnailService(
194                     @"^https?://pckles\.com/\w+/\w+$",
195                     "${0}.resize.jpg",
196                     "${0}.jpg"),
197
198                 // via.me
199                 new ViaMe(),
200
201                 // tuna.be
202                 new SimpleThumbnailService(@"^https?://tuna\.be/t/(?<entryId>[a-zA-Z0-9\.\-_]+)$", "http://tuna.be/show/thumb/${entryId}"),
203
204                 // Path (path.com)
205                 new MetaThumbnailService(@"^https?://path.com/p/\w+$"),
206
207                 // GIFMAGAZINE
208                 new SimpleThumbnailService(@"^https?://gifmagazine\.net/post_images/(\d+)", "http://img.gifmagazine.net/gifmagazine/images/${1}/original.gif"),
209
210                 // SoundCloud
211                 new MetaThumbnailService(@"^https?://soundcloud.com/[\w-]+/[\w-]+$"),
212
213                 // Gyazo (参考: http://qiita.com/uiureo/items/9ea55b07dff28a322a9e)
214                 new SimpleThumbnailService(@"^https?://gyazo\.com/([a-zA-Z0-9]+)/?$", "http://gyazo.com/${1}/raw"),
215             };
216         }
217
218         public static async Task<IEnumerable<ThumbnailInfo>> GetThumbnailsAsync(PostClass post, CancellationToken token)
219         {
220             var thumbnails = new List<ThumbnailInfo>();
221
222             var expandedUrls = Enumerable.Concat(
223                 post.GetExpandedUrls(), post.Media.Select(x => x.Url));
224
225             foreach (var expandedUrl in expandedUrls)
226             {
227                 var thumbInfo = await ThumbnailGenerator.GetThumbnailInfoAsync(expandedUrl, post, token)
228                     .ConfigureAwait(false);
229
230                 if (thumbInfo != null)
231                     thumbnails.Add(thumbInfo);
232
233                 token.ThrowIfCancellationRequested();
234             }
235
236             if (post.PostGeo != null)
237             {
238                 var map = MapThumb.GetDefaultInstance();
239                 var thumb = await map.GetThumbnailInfoAsync(post.PostGeo.Value)
240                     .ConfigureAwait(false);
241                 thumbnails.Add(thumb);
242             }
243
244             return thumbnails;
245         }
246
247         public static async Task<ThumbnailInfo> GetThumbnailInfoAsync(string url, PostClass post, CancellationToken token)
248         {
249             foreach (var generator in ThumbnailGenerator.Services)
250             {
251                 var result = await generator.GetThumbnailInfoAsync(url, post, token)
252                     .ConfigureAwait(false);
253
254                 if (result != null)
255                     return result;
256
257                 token.ThrowIfCancellationRequested();
258             }
259
260             return null;
261         }
262     }
263 }