Merge pull request #5039 from CEikermann/patch-1
[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 #include "system.h"
23 #ifdef HAVE_LIBMP3LAME
24 #include "EncoderLame.h"
25 #endif
26 #ifdef HAVE_LIBVORBISENC
27 #include "EncoderVorbis.h"
28 #endif
29 #include "EncoderWav.h"
30 #include "EncoderFFmpeg.h"
31 #include "EncoderFlac.h"
32 #include "FileItem.h"
33 #include "utils/log.h"
34 #include "Util.h"
35 #include "dialogs/GUIDialogExtendedProgressBar.h"
36 #include "filesystem/File.h"
37 #include "filesystem/SpecialProtocol.h"
38 #include "guilib/GUIWindowManager.h"
39 #include "guilib/LocalizeStrings.h"
40 #include "settings/AdvancedSettings.h"
41 #include "utils/StringUtils.h"
42 #include "storage/MediaManager.h"
43
44 using namespace MUSIC_INFO;
45 using namespace XFILE;
46
47 CCDDARipJob::CCDDARipJob(const CStdString& input,
48                          const CStdString& output,
49                          const CMusicInfoTag& tag, 
50                          int encoder,
51                          bool eject,
52                          unsigned int rate,
53                          unsigned int channels, unsigned int bps) : 
54   m_rate(rate), m_channels(channels), m_bps(bps), m_tag(tag),
55   m_input(input), m_output(CUtil::MakeLegalPath(output)), m_eject(eject),
56   m_encoder(encoder)
57 {
58 }
59
60 CCDDARipJob::~CCDDARipJob()
61 {
62 }
63
64 bool CCDDARipJob::DoWork()
65 {
66   CLog::Log(LOGINFO, "Start ripping track %s to %s", m_input.c_str(),
67                                                      m_output.c_str());
68
69   // if we are ripping to a samba share, rip it to hd first and then copy it it the share
70   CFileItem file(m_output, false);
71   if (file.IsRemote())
72     m_output = SetupTempFile();
73   
74   if (m_output.empty())
75   {
76     CLog::Log(LOGERROR, "CCDDARipper: Error opening file");
77     return false;
78   }
79
80   // init ripper
81   CFile reader;
82   CEncoder* encoder;
83   if (!reader.Open(m_input,READ_CACHED) || !(encoder=SetupEncoder(reader)))
84   {
85     CLog::Log(LOGERROR, "Error: CCDDARipper::Init failed");
86     return false;
87   }
88
89   // setup the progress dialog
90   CGUIDialogExtendedProgressBar* pDlgProgress = 
91       (CGUIDialogExtendedProgressBar*)g_windowManager.GetWindow(WINDOW_DIALOG_EXT_PROGRESS);
92   CGUIDialogProgressBarHandle* handle = pDlgProgress->GetHandle(g_localizeStrings.Get(605));
93
94   int iTrack = atoi(m_input.substr(13, m_input.size() - 13 - 5).c_str());
95   CStdString strLine0 = StringUtils::Format("%02i. %s - %s", iTrack,
96                                             StringUtils::Join(m_tag.GetArtist(), 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 = StringUtils::Format("%i", strtol(m_input.substr(13, m_input.size() - 13 - 5).c_str(),NULL,10));
216
217   encoder->SetComment("Ripped with XBMC");
218   encoder->SetArtist(StringUtils::Join(m_tag.GetArtist(),
219                                       g_advancedSettings.m_musicItemSeparator));
220   encoder->SetTitle(m_tag.GetTitle());
221   encoder->SetAlbum(m_tag.GetAlbum());
222   encoder->SetAlbumArtist(StringUtils::Join(m_tag.GetAlbumArtist(),
223                                       g_advancedSettings.m_musicItemSeparator));
224   encoder->SetGenre(StringUtils::Join(m_tag.GetGenre(),
225                                       g_advancedSettings.m_musicItemSeparator));
226   encoder->SetTrack(strTrack);
227   encoder->SetTrackLength(reader.GetLength());
228   encoder->SetYear(m_tag.GetYearString());
229
230   // init encoder
231   if (!encoder->Init(m_output.c_str(), m_channels, m_rate, m_bps))
232     delete encoder, encoder = NULL;
233
234   return encoder;
235 }
236
237 CStdString CCDDARipJob::SetupTempFile()
238 {
239   char tmp[MAX_PATH];
240 #ifndef TARGET_POSIX
241   GetTempFileName(CSpecialProtocol::TranslatePath("special://temp/"), "riptrack", 0, tmp);
242 #else
243   int fd;
244   strncpy(tmp, CSpecialProtocol::TranslatePath("special://temp/riptrackXXXXXX").c_str(), MAX_PATH);
245   if ((fd = mkstemp(tmp)) == -1)
246    tmp[0] = '\0'; 
247   if (fd != -1)
248     close(fd);
249 #endif
250   return tmp;
251 }
252
253 bool CCDDARipJob::operator==(const CJob* job) const
254 {
255   if (strcmp(job->GetType(),GetType()) == 0)
256   {
257     const CCDDARipJob* rjob = dynamic_cast<const CCDDARipJob*>(job);
258     if (rjob)
259     {
260       return m_input  == rjob->m_input &&
261              m_output == rjob->m_output;
262     }
263   }
264   return false;
265 }