summaryrefslogtreecommitdiffstats
path: root/xbmc/cdrip/CDDARipper.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xbmc/cdrip/CDDARipper.cpp326
1 files changed, 326 insertions, 0 deletions
diff --git a/xbmc/cdrip/CDDARipper.cpp b/xbmc/cdrip/CDDARipper.cpp
new file mode 100644
index 0000000..fdea7b1
--- /dev/null
+++ b/xbmc/cdrip/CDDARipper.cpp
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "CDDARipper.h"
+
+#include "CDDARipJob.h"
+#include "FileItem.h"
+#include "ServiceBroker.h"
+#include "Util.h"
+#include "addons/AddonManager.h"
+#include "addons/addoninfo/AddonInfo.h"
+#include "addons/addoninfo/AddonType.h"
+#include "filesystem/CDDADirectory.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "messaging/helpers/DialogOKHelper.h"
+#include "music/MusicDatabase.h"
+#include "music/MusicDbUrl.h"
+#include "music/MusicLibraryQueue.h"
+#include "music/infoscanner/MusicInfoScanner.h"
+#include "music/tags/MusicInfoTag.h"
+#include "music/tags/MusicInfoTagLoaderFactory.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/MediaSourceSettings.h"
+#include "settings/SettingPath.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/windows/GUIControlSettings.h"
+#include "storage/MediaManager.h"
+#include "utils/LabelFormatter.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+
+using namespace ADDON;
+using namespace XFILE;
+using namespace MUSIC_INFO;
+using namespace KODI::MESSAGING;
+using namespace KODI::CDRIP;
+
+CCDDARipper& CCDDARipper::GetInstance()
+{
+ static CCDDARipper sRipper;
+ return sRipper;
+}
+
+CCDDARipper::CCDDARipper() : CJobQueue(false, 1) //enforce fifo and non-parallel processing
+{
+}
+
+CCDDARipper::~CCDDARipper() = default;
+
+// rip a single track from cd
+bool CCDDARipper::RipTrack(CFileItem* pItem)
+{
+ // don't rip non cdda items
+ if (!URIUtils::HasExtension(pItem->GetPath(), ".cdda"))
+ {
+ CLog::Log(LOGDEBUG, "CCDDARipper::{} - File '{}' is not a cdda track", __func__,
+ pItem->GetPath());
+ return false;
+ }
+
+ // construct directory where the track is stored
+ std::string strDirectory;
+ int legalType;
+ if (!CreateAlbumDir(*pItem->GetMusicInfoTag(), strDirectory, legalType))
+ return false;
+
+ std::string strFile = URIUtils::AddFileToFolder(
+ strDirectory, CUtil::MakeLegalFileName(GetTrackName(pItem), legalType));
+
+ AddJob(new CCDDARipJob(pItem->GetPath(), strFile, *pItem->GetMusicInfoTag(),
+ CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
+ CSettings::SETTING_AUDIOCDS_ENCODER)));
+
+ return true;
+}
+
+bool CCDDARipper::RipCD()
+{
+ // return here if cd is not a CDDA disc
+ MEDIA_DETECT::CCdInfo* pInfo = CServiceBroker::GetMediaManager().GetCdInfo();
+ if (pInfo == nullptr || !pInfo->IsAudio(1))
+ {
+ CLog::Log(LOGDEBUG, "CCDDARipper::{} - CD is not an audio cd", __func__);
+ return false;
+ }
+
+ // get cd cdda contents
+ CFileItemList vecItems;
+ XFILE::CCDDADirectory directory;
+ directory.GetDirectory(CURL("cdda://local/"), vecItems);
+
+ // get cddb info
+ for (int i = 0; i < vecItems.Size(); ++i)
+ {
+ CFileItemPtr pItem = vecItems[i];
+ CMusicInfoTagLoaderFactory factory;
+ std::unique_ptr<IMusicInfoTagLoader> pLoader(factory.CreateLoader(*pItem));
+ if (nullptr != pLoader)
+ {
+ pLoader->Load(pItem->GetPath(), *pItem->GetMusicInfoTag()); // get tag from file
+ if (!pItem->GetMusicInfoTag()->Loaded())
+ break; // No CDDB info available
+ }
+ }
+
+ // construct directory where the tracks are stored
+ std::string strDirectory;
+ int legalType;
+ if (!CreateAlbumDir(*vecItems[0]->GetMusicInfoTag(), strDirectory, legalType))
+ return false;
+
+ // rip all tracks one by one
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ for (int i = 0; i < vecItems.Size(); i++)
+ {
+ CFileItemPtr item = vecItems[i];
+
+ // construct filename
+ std::string strFile = URIUtils::AddFileToFolder(
+ strDirectory, CUtil::MakeLegalFileName(GetTrackName(item.get()), legalType));
+
+ // don't rip non cdda items
+ if (item->GetPath().find(".cdda") == std::string::npos)
+ continue;
+
+ bool eject =
+ settings->GetBool(CSettings::SETTING_AUDIOCDS_EJECTONRIP) && i == vecItems.Size() - 1;
+ AddJob(new CCDDARipJob(item->GetPath(), strFile, *item->GetMusicInfoTag(),
+ settings->GetInt(CSettings::SETTING_AUDIOCDS_ENCODER), eject));
+ }
+
+ return true;
+}
+
+bool CCDDARipper::CreateAlbumDir(const MUSIC_INFO::CMusicInfoTag& infoTag,
+ std::string& strDirectory,
+ int& legalType)
+{
+ std::shared_ptr<CSettingPath> recordingpathSetting = std::static_pointer_cast<CSettingPath>(
+ CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(
+ CSettings::SETTING_AUDIOCDS_RECORDINGPATH));
+ if (recordingpathSetting != nullptr)
+ {
+ strDirectory = recordingpathSetting->GetValue();
+ if (strDirectory.empty())
+ {
+ if (CGUIControlButtonSetting::GetPath(recordingpathSetting, &g_localizeStrings))
+ strDirectory = recordingpathSetting->GetValue();
+ }
+ }
+ URIUtils::AddSlashAtEnd(strDirectory);
+
+ if (strDirectory.size() < 3)
+ {
+ // no rip path has been set, show error
+ CLog::Log(LOGERROR, "CCDDARipper::{} - Required path has not been set", __func__);
+ HELPERS::ShowOKDialogText(CVariant{257}, CVariant{608});
+ return false;
+ }
+
+ legalType = LEGAL_NONE;
+ CFileItem ripPath(strDirectory, true);
+ if (ripPath.IsSmb())
+ legalType = LEGAL_WIN32_COMPAT;
+#ifdef TARGET_WINDOWS
+ if (ripPath.IsHD())
+ legalType = LEGAL_WIN32_COMPAT;
+#endif
+
+ std::string strAlbumDir = GetAlbumDirName(infoTag);
+
+ if (!strAlbumDir.empty())
+ {
+ strDirectory = URIUtils::AddFileToFolder(strDirectory, strAlbumDir);
+ URIUtils::AddSlashAtEnd(strDirectory);
+ }
+
+ strDirectory = CUtil::MakeLegalPath(strDirectory, legalType);
+
+ // Create directory if it doesn't exist
+ if (!CUtil::CreateDirectoryEx(strDirectory))
+ {
+ CLog::Log(LOGERROR, "CCDDARipper::{} - Unable to create directory '{}'", __func__,
+ strDirectory);
+ return false;
+ }
+
+ return true;
+}
+
+std::string CCDDARipper::GetAlbumDirName(const MUSIC_INFO::CMusicInfoTag& infoTag)
+{
+ std::string strAlbumDir;
+
+ // use audiocds.trackpathformat setting to format
+ // directory name where CD tracks will be stored,
+ // use only format part ending at the last '/'
+ strAlbumDir = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
+ CSettings::SETTING_AUDIOCDS_TRACKPATHFORMAT);
+ size_t pos = strAlbumDir.find_last_of("/\\");
+ if (pos == std::string::npos)
+ return ""; // no directory
+
+ strAlbumDir = strAlbumDir.substr(0, pos);
+
+ // replace %A with album artist name
+ if (strAlbumDir.find("%A") != std::string::npos)
+ {
+ std::string strAlbumArtist = infoTag.GetAlbumArtistString();
+ if (strAlbumArtist.empty())
+ strAlbumArtist = infoTag.GetArtistString();
+ if (strAlbumArtist.empty())
+ strAlbumArtist = "Unknown Artist";
+ else
+ StringUtils::Replace(strAlbumArtist, '/', '_');
+ StringUtils::Replace(strAlbumDir, "%A", strAlbumArtist);
+ }
+
+ // replace %B with album title
+ if (strAlbumDir.find("%B") != std::string::npos)
+ {
+ std::string strAlbum = infoTag.GetAlbum();
+ if (strAlbum.empty())
+ strAlbum = StringUtils::Format("Unknown Album {}",
+ CDateTime::GetCurrentDateTime().GetAsLocalizedDateTime());
+ else
+ StringUtils::Replace(strAlbum, '/', '_');
+ StringUtils::Replace(strAlbumDir, "%B", strAlbum);
+ }
+
+ // replace %G with genre
+ if (strAlbumDir.find("%G") != std::string::npos)
+ {
+ std::string strGenre = StringUtils::Join(
+ infoTag.GetGenre(),
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicItemSeparator);
+ if (strGenre.empty())
+ strGenre = "Unknown Genre";
+ else
+ StringUtils::Replace(strGenre, '/', '_');
+ StringUtils::Replace(strAlbumDir, "%G", strGenre);
+ }
+
+ // replace %Y with year
+ if (strAlbumDir.find("%Y") != std::string::npos)
+ {
+ std::string strYear = infoTag.GetYearString();
+ if (strYear.empty())
+ strYear = "Unknown Year";
+ else
+ StringUtils::Replace(strYear, '/', '_');
+ StringUtils::Replace(strAlbumDir, "%Y", strYear);
+ }
+
+ return strAlbumDir;
+}
+
+std::string CCDDARipper::GetTrackName(CFileItem* item)
+{
+ // get track number from "cdda://local/01.cdda"
+ int trackNumber = atoi(item->GetPath().substr(13, item->GetPath().size() - 13 - 5).c_str());
+
+ // Format up our ripped file label
+ CFileItem destItem(*item);
+ destItem.SetLabel("");
+
+ // get track file name format from audiocds.trackpathformat setting,
+ // use only format part starting from the last '/'
+ std::string strFormat = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
+ CSettings::SETTING_AUDIOCDS_TRACKPATHFORMAT);
+ size_t pos = strFormat.find_last_of("/\\");
+ if (pos != std::string::npos)
+ strFormat.erase(0, pos + 1);
+
+ CLabelFormatter formatter(strFormat, "");
+ formatter.FormatLabel(&destItem);
+
+ // grab the label to use it as our ripped filename
+ std::string track = destItem.GetLabel();
+ if (track.empty())
+ track = StringUtils::Format("{}{:02}", "Track-", trackNumber);
+
+ const std::string encoder = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
+ CSettings::SETTING_AUDIOCDS_ENCODER);
+ const AddonInfoPtr addonInfo =
+ CServiceBroker::GetAddonMgr().GetAddonInfo(encoder, AddonType::AUDIOENCODER);
+ if (addonInfo)
+ track += addonInfo->Type(AddonType::AUDIOENCODER)->GetValue("@extension").asString();
+
+ return track;
+}
+
+void CCDDARipper::OnJobComplete(unsigned int jobID, bool success, CJob* job)
+{
+ if (success)
+ {
+ if (CJobQueue::QueueEmpty())
+ {
+ std::string dir = URIUtils::GetDirectory(static_cast<CCDDARipJob*>(job)->GetOutput());
+ bool unimportant;
+ int source = CUtil::GetMatchingSource(
+ dir, *CMediaSourceSettings::GetInstance().CMediaSourceSettings::GetSources("music"),
+ unimportant);
+
+ CMusicDatabase database;
+ database.Open();
+ if (source >= 0 && database.InsideScannedPath(dir))
+ CMusicLibraryQueue::GetInstance().ScanLibrary(
+ dir, MUSIC_INFO::CMusicInfoScanner::SCAN_NORMAL, false);
+
+ database.Close();
+ }
+ return CJobQueue::OnJobComplete(jobID, success, job);
+ }
+
+ CancelJobs();
+}