/// <summary>
        /// 트윗을 게시합니다.
        /// </summary>
        /// <param name="songinfo">트윗할 SongInfo 객체</param>
        /// <returns>트윗 성공 여부</returns>
        public bool Publish(SongInfo songinfo)
        {
            if (songinfo == null)
            {
                return(false);
            }

            string mediaId = null;

            var stream = ImageResize.LoadImageResized(songinfo.Cover);

            if (stream != null)
            {
                try
                {
                    var req = OAuth.MakeRequest("POST", "https://upload.twitter.com/1.1/media/upload.json");

                    using (stream)
                    {
                        var buff = new byte[4096];
                        var read = 0;

                        var boundary = Guid.NewGuid().ToString("N") + Guid.NewGuid().ToString("N");
                        var bef      = Encoding.UTF8.GetBytes(string.Format("--{0}\r\nContent-Type: application/octet-stream\r\nContent-Disposition: form-data; name=\"media\";\r\n\r\n", boundary));
                        var aft      = Encoding.UTF8.GetBytes(string.Format("\r\n\r\n--{0}--\r\n", boundary));

                        req.ContentType = "multipart/form-data; boundary=" + boundary;

                        using (var writer = new BinaryWriter(req.GetRequestStream()))
                        {
                            writer.Write(bef);
                            while ((read = stream.Read(buff, 0, 4096)) > 0)
                            {
                                writer.Write(buff, 0, read);
                            }
                            writer.Write(aft);
                        }
                    }

                    using (var res = req.GetResponse())
                        using (var reader = new StreamReader(res.GetResponseStream()))
                            mediaId = m_media.Match(reader.ReadToEnd()).Groups[1].Value;
                }
                catch
                {
                    mediaId = null;
                }
            }

            //////////////////////////////////////////////////

            var text = songinfo.ToString(mediaId != null);

            if (string.IsNullOrWhiteSpace(text))
            {
                return(false);
            }

            if (TwitterCommunicator.m_lastStr == text)
            {
                return(true);
            }
            TwitterCommunicator.m_lastStr = text;

            //////////////////////////////////////////////////

            object obj;

            if (mediaId != null)
            {
                obj = new { status = text, media_ids = mediaId }
            }
            ;
            else
            {
                obj = new { status = text }
            };

            try
            {
                var buff = Encoding.UTF8.GetBytes(OAuth.ToString(obj));

                var req = OAuth.MakeRequest("POST", "https://api.twitter.com/1.1/statuses/update.json", obj);
                req.GetRequestStream().Write(buff, 0, buff.Length);

                using (var res = req.GetResponse())
                    using (var reader = new StreamReader(res.GetResponseStream()))
                        return(reader.ReadToEnd() != null);
            }
            catch (WebException ex)
            {
                if (ex.Response != null)
                {
                    using (var res = ex.Response)
                        using (var reader = new StreamReader(res.GetResponseStream()))
                        {
                            this.m_lastError = reader.ReadToEnd();

                            try
                            {
                                var m = m_error.Match(this.m_lastError);
                                this.m_lastError = m.Groups[1].Value;

                                // 중복 트윗인 경우에는 에러 메시지를 작성하지 않는다
                                if (this.m_lastError.IndexOf("duplica") >= 0)
                                {
                                    return(true);
                                }
                            }
                            catch
                            { }
                        }
                }
                return(false);
            }
            catch
            {
                return(false);
            }
        }