2 * Copyright (C) 2005-2013 Team XBMC
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)
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.
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/>.
21 #include "threads/SystemClock.h"
24 #ifdef HAS_CDDA_RIPPER
26 #include "CDDARipper.h"
27 #include "CDDARipJob.h"
28 #include "utils/StringUtils.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"
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"
52 using namespace XFILE;
53 using namespace MUSIC_INFO;
55 CCDDARipper& CCDDARipper::GetInstance()
57 static CCDDARipper sRipper;
61 CCDDARipper::CCDDARipper()
62 : CJobQueue(false, 1) //enforce fifo and non-parallel processing
66 CCDDARipper::~CCDDARipper()
70 // rip a single track from cd
71 bool CCDDARipper::RipTrack(CFileItem* pItem)
73 // don't rip non cdda items
74 if (!URIUtils::HasExtension(pItem->GetPath(), ".cdda"))
76 CLog::Log(LOGDEBUG, "cddaripper: file is not a cdda track");
80 // construct directory where the track is stored
81 CStdString strDirectory;
83 if (!CreateAlbumDir(*pItem->GetMusicInfoTag(), strDirectory, legalType))
86 CStdString strFile = URIUtils::AddFileToFolder(strDirectory,
87 CUtil::MakeLegalFileName(GetTrackName(pItem), legalType));
89 AddJob(new CCDDARipJob(pItem->GetPath(),strFile,
90 *pItem->GetMusicInfoTag(),
91 CSettings::Get().GetInt("audiocds.encoder")));
96 bool CCDDARipper::RipCD()
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))
102 CLog::Log(LOGDEBUG, "cddaripper: CD is not an audio cd");
106 // get cd cdda contents
107 CFileItemList vecItems;
108 XFILE::CCDDADirectory directory;
109 directory.GetDirectory("cdda://local/", vecItems);
112 for (int i = 0; i < vecItems.Size(); ++i)
114 CFileItemPtr pItem = vecItems[i];
115 CMusicInfoTagLoaderFactory factory;
116 auto_ptr<IMusicInfoTagLoader> pLoader (factory.CreateLoader(pItem->GetPath()));
117 if (NULL != pLoader.get())
119 pLoader->Load(pItem->GetPath(), *pItem->GetMusicInfoTag()); // get tag from file
120 if (!pItem->GetMusicInfoTag()->Loaded())
121 break; // No CDDB info available
125 // construct directory where the tracks are stored
126 CStdString strDirectory;
128 if (!CreateAlbumDir(*vecItems[0]->GetMusicInfoTag(), strDirectory, legalType))
131 // rip all tracks one by one
132 for (int i = 0; i < vecItems.Size(); i++)
134 CFileItemPtr item = vecItems[i];
136 // construct filename
137 CStdString strFile = URIUtils::AddFileToFolder(strDirectory, CUtil::MakeLegalFileName(GetTrackName(item.get()), legalType));
139 // don't rip non cdda items
140 if (item->GetPath().find(".cdda") == std::string::npos)
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));
153 const char* CCDDARipper::GetExtension(int iEncoder)
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";
163 bool CCDDARipper::CreateAlbumDir(const MUSIC_INFO::CMusicInfoTag& infoTag, CStdString& strDirectory, int& legalType)
165 CSettingPath *recordingpathSetting = (CSettingPath*)CSettings::Get().GetSetting("audiocds.recordingpath");
166 if (recordingpathSetting != NULL)
168 strDirectory = recordingpathSetting->GetValue();
169 if (strDirectory.empty())
171 if (CGUIControlButtonSetting::GetPath(recordingpathSetting))
172 strDirectory = recordingpathSetting->GetValue();
175 URIUtils::AddSlashAtEnd(strDirectory);
177 if (strDirectory.size() < 3)
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();
187 legalType = LEGAL_NONE;
188 CFileItem ripPath(strDirectory, true);
190 legalType = LEGAL_WIN32_COMPAT;
191 #ifdef TARGET_WINDOWS
193 legalType = LEGAL_WIN32_COMPAT;
196 CStdString strAlbumDir = GetAlbumDirName(infoTag);
198 if (!strAlbumDir.empty())
200 strDirectory = URIUtils::AddFileToFolder(strDirectory, strAlbumDir);
201 URIUtils::AddSlashAtEnd(strDirectory);
204 strDirectory = CUtil::MakeLegalPath(strDirectory, legalType);
206 // Create directory if it doesn't exist
207 if (!CUtil::CreateDirectoryEx(strDirectory))
209 CLog::Log(LOGERROR, "Unable to create directory '%s'", strDirectory.c_str());
216 CStdString CCDDARipper::GetAlbumDirName(const MUSIC_INFO::CMusicInfoTag& infoTag)
218 CStdString strAlbumDir;
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
228 strAlbumDir = strAlbumDir.substr(0, pos);
230 // replace %A with album artist name
231 if (strAlbumDir.find("%A") != std::string::npos)
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";
239 StringUtils::Replace(strAlbumArtist, '/', '_');
240 StringUtils::Replace(strAlbumDir, "%A", strAlbumArtist);
243 // replace %B with album title
244 if (strAlbumDir.find("%B") != std::string::npos)
246 CStdString strAlbum = infoTag.GetAlbum();
247 if (strAlbum.empty())
248 strAlbum = StringUtils::Format("Unknown Album %s", CDateTime::GetCurrentDateTime().GetAsLocalizedDateTime().c_str());
250 StringUtils::Replace(strAlbum, '/', '_');
251 StringUtils::Replace(strAlbumDir, "%B", strAlbum);
254 // replace %G with genre
255 if (strAlbumDir.find("%G") != std::string::npos)
257 CStdString strGenre = StringUtils::Join(infoTag.GetGenre(), g_advancedSettings.m_musicItemSeparator);
258 if (strGenre.empty())
259 strGenre = "Unknown Genre";
261 StringUtils::Replace(strGenre, '/', '_');
262 StringUtils::Replace(strAlbumDir, "%G", strGenre);
265 // replace %Y with year
266 if (strAlbumDir.find("%Y") != std::string::npos)
268 CStdString strYear = infoTag.GetYearString();
270 strYear = "Unknown Year";
272 StringUtils::Replace(strYear, '/', '_');
273 StringUtils::Replace(strAlbumDir, "%Y", strYear);
279 CStdString CCDDARipper::GetTrackName(CFileItem *item)
281 // get track number from "cdda://local/01.cdda"
282 int trackNumber = atoi(item->GetPath().substr(13, item->GetPath().size() - 13 - 5).c_str());
284 // Format up our ripped file label
285 CFileItem destItem(*item);
286 destItem.SetLabel("");
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);
295 CLabelFormatter formatter(strFormat, "");
296 formatter.FormatLabel(&destItem);
298 // grab the label to use it as our ripped filename
299 CStdString track = destItem.GetLabel();
301 track = StringUtils::Format("%s%02i", "Track-", trackNumber);
302 track += GetExtension(CSettings::Get().GetInt("audiocds.encoder"));
307 void CCDDARipper::OnJobComplete(unsigned int jobID, bool success, CJob* job)
311 if(CJobQueue::QueueEmpty())
313 CStdString dir = URIUtils::GetDirectory(((CCDDARipJob*)job)->GetOutput());
315 int source = CUtil::GetMatchingSource(dir, *CMediaSourceSettings::Get().CMediaSourceSettings::GetSources("music"), unimportant);
317 CMusicDatabase database;
319 if (source>=0 && database.InsideScannedPath(dir))
320 g_application.StartMusicScan(dir);
323 return CJobQueue::OnJobComplete(jobID, success, job);