OSDN Git Service

bit.ly のアクセストークン取得に使用する認証ダイアログを追加
[opentween/open-tween.git] / OpenTween / MediaItem.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2015 spx (@5px)
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.Drawing;
24 using System.IO;
25 using System.Linq;
26 using System.Threading;
27
28 namespace OpenTween
29 {
30     public interface IMediaItem
31     {
32         /// <summary>
33         /// メディアへの絶対パス
34         /// </summary>
35         string Path { get; }
36
37         /// <summary>
38         /// メディア名
39         /// </summary>
40         string Name { get; }
41
42         /// <summary>
43         /// メディアの拡張子
44         /// </summary>
45         string Extension { get; }
46
47         /// <summary>
48         /// メディアが存在するかどうかを示す真偽値
49         /// </summary>
50         bool Exists { get; }
51
52         /// <summary>
53         /// メディアのサイズ(バイト単位)
54         /// </summary>
55         long Size { get; }
56
57         /// <summary>
58         /// メディアが画像であるかどうかを示す真偽値
59         /// </summary>
60         bool IsImage { get; }
61
62         /// <summary>
63         /// 代替テキスト (アップロード先が対応している必要がある)
64         /// </summary>
65         string AltText { get; set; }
66
67         /// <summary>
68         /// 表示用の MemoryImage を作成する
69         /// </summary>
70         /// <remarks>
71         /// 呼び出し側にて破棄すること
72         /// </remarks>
73         MemoryImage CreateImage();
74
75         /// <summary>
76         /// メディアの内容を読み込むための Stream を開く
77         /// </summary>
78         /// <remarks>
79         /// 呼び出し側にて閉じること
80         /// </remarks>
81         Stream OpenRead();
82
83         /// <summary>
84         /// メディアの内容を Stream へ書き込む
85         /// </summary>
86         void CopyTo(Stream stream);
87     }
88
89     /// <summary>
90     /// ファイル用の MediaItem クラス
91     /// </summary>
92     public class FileMediaItem : IMediaItem
93     {
94         public FileInfo FileInfo { get; }
95         public string AltText { get; set; }
96
97         public FileMediaItem(string path)
98         {
99             this.FileInfo = new FileInfo(path);
100         }
101
102         public FileMediaItem(FileInfo fileInfo)
103             : this(fileInfo.FullName)
104         {
105         }
106
107         public string Path
108         {
109             get { return this.FileInfo.FullName; }
110         }
111
112         public string Name
113         {
114             get { return this.FileInfo.Name; }
115         }
116
117         public string Extension
118         {
119             get { return this.FileInfo.Extension; }
120         }
121
122         public bool Exists
123         {
124             get { return this.FileInfo.Exists; }
125         }
126
127         public long Size
128         {
129             get { return this.FileInfo.Length; }
130         }
131
132         public bool IsImage
133         {
134             get
135             {
136                 if (this.isImage == null)
137                 {
138                     try
139                     {
140                         // MemoryImage が生成できるかを検証する
141                         using (var image = this.CreateImage()) { }
142
143                         this.isImage = true;
144                     }
145                     catch (InvalidImageException)
146                     {
147                         this.isImage = false;
148                     }
149                 }
150
151                 return this.isImage.Value;
152             }
153         }
154
155         /// <summary>IsImage の検証結果をキャッシュする。未検証なら null</summary>
156         private bool? isImage = null;
157
158         public MemoryImage CreateImage()
159         {
160             using (var fs = this.FileInfo.OpenRead())
161             {
162                 return MemoryImage.CopyFromStream(fs);
163             }
164         }
165
166         public Stream OpenRead()
167         {
168             return this.FileInfo.OpenRead();
169         }
170
171         public void CopyTo(Stream stream)
172         {
173             using (var fs = this.FileInfo.OpenRead())
174             {
175                 fs.CopyTo(stream);
176             }
177         }
178     }
179
180     /// <summary>
181     /// MemoryImage 用の MediaItem クラス
182     /// </summary>
183     /// <remarks>
184     /// 用途の関係上、メモリ使用量が大きくなるため、不要になればできるだけ破棄すること
185     /// </remarks>
186     public class MemoryImageMediaItem : IMediaItem, IDisposable
187     {
188         public const string PathPrefix = "<>MemoryImage://";
189         private static int _fileNumber = 0;
190
191         private bool _disposed = false;
192
193         private readonly MemoryImage _image;
194
195         public MemoryImageMediaItem(MemoryImage image)
196         {
197             this._image = image ?? throw new ArgumentNullException(nameof(image));
198
199             var num = Interlocked.Increment(ref _fileNumber);
200             this.Path = PathPrefix + num + this._image.ImageFormatExt;
201         }
202
203         public string Path { get; }
204         public string AltText { get; set; }
205
206         public string Name
207         {
208             get { return this.Path.Substring(PathPrefix.Length); }
209         }
210
211         public string Extension
212         {
213             get { return this._image.ImageFormatExt; }
214         }
215
216         public bool Exists
217         {
218             get { return this._image != null; }
219         }
220
221         public long Size
222         {
223             get { return this._image.Stream.Length; }
224         }
225
226         public bool IsImage
227         {
228             get { return true; }
229         }
230
231         public bool IsDisposed
232         {
233             get { return this._disposed; }
234         }
235
236         public MemoryImage CreateImage()
237         {
238             return this._image.Clone();
239         }
240
241         public Stream OpenRead()
242         {
243             MemoryStream memstream = null;
244             try
245             {
246                 // コピーを作成する
247                 memstream = new MemoryStream();
248
249                 this._image.Stream.WriteTo(memstream);
250                 memstream.Seek(0, SeekOrigin.Begin);
251
252                 return memstream;
253             }
254             catch
255             {
256                 memstream?.Dispose();
257                 throw;
258             }
259         }
260
261         public void CopyTo(Stream stream)
262         {
263             this._image.Stream.WriteTo(stream);
264         }
265
266         protected virtual void Dispose(bool disposing)
267         {
268             if (this._disposed) return;
269
270             if (disposing)
271             {
272                 this._image.Dispose();
273             }
274
275             this._disposed = true;
276         }
277
278         public void Dispose()
279         {
280             this.Dispose(true);
281
282             // 明示的にDisposeが呼ばれた場合はファイナライザを使用しない
283             GC.SuppressFinalize(this);
284         }
285
286         ~MemoryImageMediaItem()
287             => this.Dispose(false);
288     }
289 }