Merge pull request #5039 from CEikermann/patch-1
[vuplus_xbmc] / xbmc / cdrip / CDDARipper.cpp
1 /*
2  *      Copyright (C) 2005-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 "threads/SystemClock.h"
22 #include "system.h"
23
24 #ifdef HAS_CDDA_RIPPER
25
26 #include "CDDARipper.h"
27 #include "CDDARipJob.h"
28 #include "utils/StringUtils.h"
29 #include "Util.h"
30 #include "filesystem/CDDADirectory.h"
31 #include "music/tags/MusicInfoTagLoaderFactory.h"
32 #include "utils/LabelFormatter.h"
33 #include "music/tags/MusicInfoTag.h"
34 #include "guilib/GUIWindowManager.h"
35 #include "dialogs/GUIDialogOK.h"
36 #include "settings/AdvancedSettings.h"
37 #include "settings/SettingPath.h"
38 #include "settings/Settings.h"
39 #include "settings/windows/GUIControlSettings.h"
40 #include "FileItem.h"
41 #include "filesystem/SpecialProtocol.h"
42 #include "storage/MediaManager.h"
43 #include "guilib/LocalizeStrings.h"
44 #include "utils/log.h"
45 #include "utils/TimeUtils.h"
46 #include "utils/URIUtils.h"
47 #include "settings/MediaSourceSettings.h"
48 #include "Application.h"
49 #include "music/MusicDatabase.h"
50
51 using namespace std;
52 using namespace XFILE;
53 using namespace MUSIC_INFO;
54
55 CCDDARipper& CCDDARipper::GetInstance()
56 {
57   static CCDDARipper sRipper;
58   return sRipper;
59 }
60
61 CCDDARipper::CCDDARipper()
62   : CJobQueue(false, 1) //enforce fifo and non-parallel processing
63 {
64 }
65
66 CCDDARipper::~CCDDARipper()
67 {
68 }
69
70 // rip a single track from cd
71 bool CCDDARipper::RipTrack(CFileItem* pItem)
72 {
73   // don't rip non cdda items
74   if (!URIUtils::HasExtension(pItem->GetPath(), ".cdda"))
75   {
76     CLog::Log(LOGDEBUG, "cddaripper: file is not a cdda track");
77     return false;
78   }
79
80   // construct directory where the track is stored
81   CStdString strDirectory;
82   int legalType;
83   if (!CreateAlbumDir(*pItem->GetMusicInfoTag(), strDirectory, legalType))
84     return false;
85
86   CStdString strFile = URIUtils::AddFileToFolder(strDirectory, 
87                       CUtil::MakeLegalFileName(GetTrackName(pItem), legalType));
88
89   AddJob(new CCDDARipJob(pItem->GetPath(),strFile,
90                          *pItem->GetMusicInfoTag(),
91                          CSettings::Get().GetInt("audiocds.encoder")));
92
93   return true;
94 }
95
96 bool CCDDARipper::RipCD()
97 {
98   // return here if cd is not a CDDA disc
99   MEDIA_DETECT::CCdInfo* pInfo = g_mediaManager.GetCdInfo();
100   if (pInfo == NULL || !pInfo->IsAudio(1))
101   {
102     CLog::Log(LOGDEBUG, "cddaripper: CD is not an audio cd");
103     return false;
104   }
105
106   // get cd cdda contents
107   CFileItemList vecItems;
108   XFILE::CCDDADirectory directory;
109   directory.GetDirectory("cdda://local/", vecItems);
110
111   // get cddb info
112   for (int i = 0; i < vecItems.Size(); ++i)
113   {
114     CFileItemPtr pItem = vecItems[i];
115     CMusicInfoTagLoaderFactory factory;
116     auto_ptr<IMusicInfoTagLoader> pLoader (factory.CreateLoader(pItem->GetPath()));
117     if (NULL != pLoader.get())
118     {
119       pLoader->Load(pItem->GetPath(), *pItem->GetMusicInfoTag()); // get tag from file
120       if (!pItem->GetMusicInfoTag()->Loaded())
121         break;  //  No CDDB info available
122     }
123   }
124
125   // construct directory where the tracks are stored
126   CStdString strDirectory;
127   int legalType;
128   if (!CreateAlbumDir(*vecItems[0]->GetMusicInfoTag(), strDirectory, legalType))
129     return false;
130
131   // rip all tracks one by one
132   for (int i = 0; i < vecItems.Size(); i++)
133   {
134     CFileItemPtr item = vecItems[i];
135
136     // construct filename
137     CStdString strFile = URIUtils::AddFileToFolder(strDirectory, CUtil::MakeLegalFileName(GetTrackName(item.get()), legalType));
138
139     // don't rip non cdda items
140     if (item->GetPath().find(".cdda") == std::string::npos)
141       continue;
142
143     bool eject = CSettings::Get().GetBool("audiocds.ejectonrip") && 
144                  i == vecItems.Size()-1;
145     AddJob(new CCDDARipJob(item->GetPath(),strFile,
146                            *item->GetMusicInfoTag(),
147                            CSettings::Get().GetInt("audiocds.encoder"), eject));
148   }
149
150   return true;
151 }
152
153 const char* CCDDARipper::GetExtension(int iEncoder)
154 {
155   if (iEncoder == CDDARIP_ENCODER_WAV) return ".wav";
156   if (iEncoder == CDDARIP_ENCODER_VORBIS) return ".ogg";
157   if (iEncoder == CDDARIP_ENCODER_FLAC) return ".flac";
158   if (iEncoder == CDDARIP_ENCODER_FFMPEG_M4A) return ".m4a";
159   if (iEncoder == CDDARIP_ENCODER_FFMPEG_WMA) return ".wma";
160   return ".mp3";
161 }
162
163 bool CCDDARipper::CreateAlbumDir(const MUSIC_INFO::CMusicInfoTag& infoTag, CStdString& strDirectory, int& legalType)
164 {
165   CSettingPath *recordingpathSetting = (CSettingPath*)CSettings::Get().GetSetting("audiocds.recordingpath");
166   if (recordingpathSetting != NULL)
167   {
168     strDirectory = recordingpathSetting->GetValue();
169     if (strDirectory.empty())
170     {
171       if (CGUIControlButtonSetting::GetPath(recordingpathSetting))
172         strDirectory = recordingpathSetting->GetValue();
173     }
174   }
175   URIUtils::AddSlashAtEnd(strDirectory);
176
177   if (strDirectory.size() < 3)
178   {
179     // no rip path has been set, show error
180     CLog::Log(LOGERROR, "Error: CDDARipPath has not been set");
181     g_graphicsContext.Lock();
182     CGUIDialogOK::ShowAndGetInput(257, 608, 609, 0);
183     g_graphicsContext.Unlock();
184     return false;
185   }
186
187   legalType = LEGAL_NONE;
188   CFileItem ripPath(strDirectory, true);
189   if (ripPath.IsSmb())
190     legalType = LEGAL_WIN32_COMPAT;
191 #ifdef TARGET_WINDOWS
192   if (ripPath.IsHD())
193     legalType = LEGAL_WIN32_COMPAT;
194 #endif
195
196   CStdString strAlbumDir = GetAlbumDirName(infoTag);
197
198   if (!strAlbumDir.empty())
199   {
200     strDirectory = URIUtils::AddFileToFolder(strDirectory, strAlbumDir);
201     URIUtils::AddSlashAtEnd(strDirectory);
202   }
203
204   strDirectory = CUtil::MakeLegalPath(strDirectory, legalType);
205
206   // Create directory if it doesn't exist
207   if (!CUtil::CreateDirectoryEx(strDirectory))
208   {
209     CLog::Log(LOGERROR, "Unable to create directory '%s'", strDirectory.c_str());
210     return false;
211   }
212
213   return true;
214 }
215
216 CStdString CCDDARipper::GetAlbumDirName(const MUSIC_INFO::CMusicInfoTag& infoTag)
217 {
218   CStdString strAlbumDir;
219
220   // use audiocds.trackpathformat setting to format
221   // directory name where CD tracks will be stored,
222   // use only format part ending at the last '/'
223   strAlbumDir = CSettings::Get().GetString("audiocds.trackpathformat");
224   size_t pos = strAlbumDir.find_last_of("/\\");
225   if (pos == std::string::npos)
226     return ""; // no directory
227   
228   strAlbumDir = strAlbumDir.substr(0, pos);
229
230   // replace %A with album artist name
231   if (strAlbumDir.find("%A") != std::string::npos)
232   {
233     CStdString strAlbumArtist = StringUtils::Join(infoTag.GetAlbumArtist(), g_advancedSettings.m_musicItemSeparator);
234     if (strAlbumArtist.empty())
235       strAlbumArtist = StringUtils::Join(infoTag.GetArtist(), g_advancedSettings.m_musicItemSeparator);
236     if (strAlbumArtist.empty())
237       strAlbumArtist = "Unknown Artist";
238     else
239       StringUtils::Replace(strAlbumArtist, '/', '_');
240     StringUtils::Replace(strAlbumDir, "%A", strAlbumArtist);
241   }
242
243   // replace %B with album title
244   if (strAlbumDir.find("%B") != std::string::npos)
245   {
246     CStdString strAlbum = infoTag.GetAlbum();
247     if (strAlbum.empty())
248       strAlbum = StringUtils::Format("Unknown Album %s", CDateTime::GetCurrentDateTime().GetAsLocalizedDateTime().c_str());
249     else
250       StringUtils::Replace(strAlbum, '/', '_');
251     StringUtils::Replace(strAlbumDir, "%B", strAlbum);
252   }
253
254   // replace %G with genre
255   if (strAlbumDir.find("%G") != std::string::npos)
256   {
257     CStdString strGenre = StringUtils::Join(infoTag.GetGenre(), g_advancedSettings.m_musicItemSeparator);
258     if (strGenre.empty())
259       strGenre = "Unknown Genre";
260     else
261       StringUtils::Replace(strGenre, '/', '_');
262     StringUtils::Replace(strAlbumDir, "%G", strGenre);
263   }
264
265   // replace %Y with year
266   if (strAlbumDir.find("%Y") != std::string::npos)
267   {
268     CStdString strYear = infoTag.GetYearString();
269     if (strYear.empty())
270       strYear = "Unknown Year";
271     else
272       StringUtils::Replace(strYear, '/', '_');
273     StringUtils::Replace(strAlbumDir, "%Y", strYear);
274   }
275
276   return strAlbumDir;
277 }
278
279 CStdString CCDDARipper::GetTrackName(CFileItem *item)
280 {
281   // get track number from "cdda://local/01.cdda"
282   int trackNumber = atoi(item->GetPath().substr(13, item->GetPath().size() - 13 - 5).c_str());
283
284   // Format up our ripped file label
285   CFileItem destItem(*item);
286   destItem.SetLabel("");
287
288   // get track file name format from audiocds.trackpathformat setting,
289   // use only format part starting from the last '/'
290   CStdString strFormat = CSettings::Get().GetString("audiocds.trackpathformat");
291   size_t pos = strFormat.find_last_of("/\\");
292   if (pos != std::string::npos)
293     strFormat.erase(0, pos+1);
294
295   CLabelFormatter formatter(strFormat, "");
296   formatter.FormatLabel(&destItem);
297
298   // grab the label to use it as our ripped filename
299   CStdString track = destItem.GetLabel();
300   if (track.empty())
301     track = StringUtils::Format("%s%02i", "Track-", trackNumber);
302   track += GetExtension(CSettings::Get().GetInt("audiocds.encoder"));
303
304   return track;
305 }
306
307 void CCDDARipper::OnJobComplete(unsigned int jobID, bool success, CJob* job)
308 {
309   if (success)
310   {
311     if(CJobQueue::QueueEmpty())
312     {
313       CStdString dir = URIUtils::GetDirectory(((CCDDARipJob*)job)->GetOutput());
314       bool unimportant;
315       int source = CUtil::GetMatchingSource(dir, *CMediaSourceSettings::Get().CMediaSourceSettings::GetSources("music"), unimportant);
316
317       CMusicDatabase database;
318       database.Open();
319       if (source>=0 && database.InsideScannedPath(dir))
320         g_application.StartMusicScan(dir);
321       database.Close();
322     }
323     return CJobQueue::OnJobComplete(jobID, success, job);
324   }
325
326   CancelJobs();
327 }
328
329 #endif