4bda0005c3072d3a546f07e750d4c7a088709271
[vuplus_xbmc] / xbmc / cdrip / CDDARipJob.cpp
1 /*
2  *      Copyright (C) 2012-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "CDDARipJob.h"
22 #ifdef HAVE_LIBMP3LAME
23 #include "EncoderLame.h"
24 #endif
25 #ifdef HAVE_LIBVORBISENC
26 #include "EncoderVorbis.h"
27 #endif
28 #include "EncoderWav.h"
29 #include "EncoderFFmpeg.h"
30 #include "EncoderFlac.h"
31 #include "FileItem.h"
32 #include "utils/log.h"
33 #include "Util.h"
34 #include "dialogs/GUIDialogExtendedProgressBar.h"
35 #include "filesystem/File.h"
36 #include "filesystem/SpecialProtocol.h"
37 #include "guilib/GUIWindowManager.h"
38 #include "guilib/LocalizeStrings.h"
39 #include "settings/AdvancedSettings.h"
40 #include "utils/StringUtils.h"
41 #include "storage/MediaManager.h"
42
43 using namespace MUSIC_INFO;
44 using namespace XFILE;
45
46 CCDDARipJob::CCDDARipJob(const CStdString& input,
47                          const CStdString& output,
48                          const CMusicInfoTag& tag, 
49                          int encoder,
50                          bool eject,
51                          unsigned int rate,
52                          unsigned int channels, unsigned int bps) : 
53   m_rate(rate), m_channels(channels), m_bps(bps), m_tag(tag),
54   m_input(input), m_output(CUtil::MakeLegalPath(output)), m_eject(eject),
55   m_encoder(encoder)
56 {
57 }
58
59 CCDDARipJob::~CCDDARipJob()
60 {
61 }
62
63 bool CCDDARipJob::DoWork()
64 {
65   CLog::Log(LOGINFO, "Start ripping track %s to %s", m_input.c_str(),
66                                                      m_output.c_str());
67
68   // if we are ripping to a samba share, rip it to hd first and then copy it it the share
69   CFileItem file(m_output, false);
70   if (file.IsRemote())
71     m_output = SetupTempFile();
72   
73   if (m_output.IsEmpty())
74   {
75     CLog::Log(LOGERROR, "CCDDARipper: Error opening file");
76     return false;
77   }
78
79   // init ripper
80   CFile reader;
81   CEncoder* encoder;
82   if (!reader.Open(m_input,READ_CACHED) || !(encoder=SetupEncoder(reader)))
83   {
84     CLog::Log(LOGERROR, "Error: CCDDARipper::Init failed");
85     return false;
86   }
87
88   // setup the progress dialog
89   CGUIDialogExtendedProgressBar* pDlgProgress = 
90       (CGUIDialogExtendedProgressBar*)g_windowManager.GetWindow(WINDOW_DIALOG_EXT_PROGRESS);
91   CGUIDialogProgressBarHandle* handle = pDlgProgress->GetHandle(g_localizeStrings.Get(605));
92   CStdString strLine0;
93   int iTrack = atoi(m_input.substr(13, m_input.size() - 13 - 5).c_str());
94   strLine0.Format("%02i. %s - %s", iTrack,
95                   StringUtils::Join(m_tag.GetArtist(),
96                               g_advancedSettings.m_musicItemSeparator).c_str(),
97                   m_tag.GetTitle().c_str());
98   handle->SetText(strLine0);
99
100   // start ripping
101   int percent=0;
102   int oldpercent=0;
103   bool cancelled(false);
104   int result;
105   while (!cancelled && (result=RipChunk(reader, encoder, percent)) == 0)
106   {
107     cancelled = ShouldCancel(percent,100);
108     if (percent > oldpercent)
109     {
110       oldpercent = percent;
111       handle->SetPercentage(percent);
112     }
113   }
114
115   // close encoder ripper
116   encoder->Close();
117   delete encoder;
118   reader.Close();
119
120   if (file.IsRemote() && !cancelled && result == 2)
121   {
122     // copy the ripped track to the share
123     if (!CFile::Cache(m_output, file.GetPath()))
124     {
125       CLog::Log(LOGERROR, "CDDARipper: Error copying file from %s to %s", 
126                 m_output.c_str(), file.GetPath().c_str());
127       CFile::Delete(m_output);
128       return false;
129     }
130     // delete cached file
131     CFile::Delete(m_output);
132   }
133
134   if (cancelled)
135   {
136     CLog::Log(LOGWARNING, "User Cancelled CDDA Rip");
137     CFile::Delete(m_output);
138   }
139   else if (result == 1)
140     CLog::Log(LOGERROR, "CDDARipper: Error ripping %s", m_input.c_str());
141   else if (result < 0)
142     CLog::Log(LOGERROR, "CDDARipper: Error encoding %s", m_input.c_str());
143   else
144   {
145     CLog::Log(LOGINFO, "Finished ripping %s", m_input.c_str());
146     if (m_eject)
147     {
148       CLog::Log(LOGINFO, "Ejecting CD");
149       g_mediaManager.EjectTray();
150     }
151   }
152
153   handle->MarkFinished();
154
155   return !cancelled && result == 2;
156 }
157
158 int CCDDARipJob::RipChunk(CFile& reader, CEncoder* encoder, int& percent)
159 {
160   percent = 0;
161
162   uint8_t stream[1024];
163
164   // get data
165   int result = reader.Read(stream, 1024);
166
167   // return if rip is done or on some kind of error
168   if (!result)
169     return 1;
170
171   // encode data
172   int encres=encoder->Encode(result, stream);
173
174   // Get progress indication
175   percent = reader.GetPosition()*100/reader.GetLength();
176
177   if (reader.GetPosition() == reader.GetLength())
178     return 2;
179
180   return -(1-encres);
181 }
182
183 CEncoder* CCDDARipJob::SetupEncoder(CFile& reader)
184 {
185   CEncoder* encoder;
186   switch (m_encoder)
187   {
188 #ifdef HAVE_LIBVORBISENC
189   case CDDARIP_ENCODER_VORBIS:
190     encoder = new CEncoderVorbis();
191     break;
192 #endif
193 #ifdef HAVE_LIBMP3LAME
194   case CDDARIP_ENCODER_LAME:
195     encoder = new CEncoderLame();
196     break;
197 #endif
198   case CDDARIP_ENCODER_FLAC:
199     encoder = new CEncoderFlac();
200     break;
201   case CDDARIP_ENCODER_FFMPEG_M4A:
202   case CDDARIP_ENCODER_FFMPEG_WMA:
203     encoder = new CEncoderFFmpeg();
204     break;
205   case CDDARIP_ENCODER_WAV:
206   default:
207     encoder = new CEncoderWav();
208     break;
209   }
210
211   if (!encoder)
212     return NULL;
213
214   // we have to set the tags before we init the Encoder
215   CStdString strTrack;
216   strTrack.Format("%i", strtol(m_input.substr(13, m_input.size() - 13 - 5).c_str(),NULL,10));
217
218   encoder->SetComment("Ripped with XBMC");
219   encoder->SetArtist(StringUtils::Join(m_tag.GetArtist(),
220                                       g_advancedSettings.m_musicItemSeparator));
221   encoder->SetTitle(m_tag.GetTitle());
222   encoder->SetAlbum(m_tag.GetAlbum());
223   encoder->SetAlbumArtist(StringUtils::Join(m_tag.GetAlbumArtist(),
224                                       g_advancedSettings.m_musicItemSeparator));
225   encoder->SetGenre(StringUtils::Join(m_tag.GetGenre(),
226                                       g_advancedSettings.m_musicItemSeparator));
227   encoder->SetTrack(strTrack);
228   encoder->SetTrackLength(reader.GetLength());
229   encoder->SetYear(m_tag.GetYearString());
230
231   // init encoder
232   if (!encoder->Init(m_output.c_str(), m_channels, m_rate, m_bps))
233     delete encoder, encoder = NULL;
234
235   return encoder;
236 }
237
238 CStdString CCDDARipJob::SetupTempFile()
239 {
240   char tmp[MAX_PATH];
241 #ifndef TARGET_POSIX
242   GetTempFileName(CSpecialProtocol::TranslatePath("special://temp/"), "riptrack", 0, tmp);
243 #else
244   int fd;
245   strncpy(tmp, CSpecialProtocol::TranslatePath("special://temp/riptrackXXXXXX").c_str(), MAX_PATH);
246   if ((fd = mkstemp(tmp)) == -1)
247    tmp[0] = '\0'; 
248   if (fd != -1)
249     close(fd);
250 #endif
251   return tmp;
252 }
253
254 bool CCDDARipJob::operator==(const CJob* job) const
255 {
256   if (strcmp(job->GetType(),GetType()) == 0)
257   {
258     const CCDDARipJob* rjob = dynamic_cast<const CCDDARipJob*>(job);
259     if (rjob)
260     {
261       return m_input  == rjob->m_input &&
262              m_output == rjob->m_output;
263     }
264   }
265   return false;
266 }