summaryrefslogtreecommitdiffstats
path: root/xbmc/cdrip/CDDARipJob.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xbmc/cdrip/CDDARipJob.cpp259
1 files changed, 259 insertions, 0 deletions
diff --git a/xbmc/cdrip/CDDARipJob.cpp b/xbmc/cdrip/CDDARipJob.cpp
new file mode 100644
index 0000000..d8cbab9
--- /dev/null
+++ b/xbmc/cdrip/CDDARipJob.cpp
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2012-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 "CDDARipJob.h"
+
+#include "Encoder.h"
+#include "EncoderAddon.h"
+#include "EncoderFFmpeg.h"
+#include "FileItem.h"
+#include "ServiceBroker.h"
+#include "Util.h"
+#include "addons/AddonManager.h"
+#include "addons/addoninfo/AddonType.h"
+#include "dialogs/GUIDialogExtendedProgressBar.h"
+#include "filesystem/File.h"
+#include "filesystem/SpecialProtocol.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "storage/MediaManager.h"
+#include "utils/StringUtils.h"
+#include "utils/SystemInfo.h"
+#include "utils/log.h"
+
+#if defined(TARGET_WINDOWS)
+#include "platform/win32/CharsetConverter.h"
+#endif
+
+using namespace ADDON;
+using namespace MUSIC_INFO;
+using namespace XFILE;
+using namespace KODI::CDRIP;
+
+CCDDARipJob::CCDDARipJob(const std::string& input,
+ const std::string& output,
+ const CMusicInfoTag& tag,
+ int encoder,
+ bool eject,
+ unsigned int rate,
+ unsigned int channels,
+ unsigned int bps)
+ : m_rate(rate),
+ m_channels(channels),
+ m_bps(bps),
+ m_tag(tag),
+ m_input(input),
+ m_output(CUtil::MakeLegalPath(output)),
+ m_eject(eject),
+ m_encoder(encoder)
+{
+}
+
+CCDDARipJob::~CCDDARipJob() = default;
+
+bool CCDDARipJob::DoWork()
+{
+ CLog::Log(LOGINFO, "CCDDARipJob::{} - Start ripping track {} to {}", __func__, m_input, m_output);
+
+ // if we are ripping to a samba share, rip it to hd first and then copy it to the share
+ CFileItem file(m_output, false);
+ if (file.IsRemote())
+ m_output = SetupTempFile();
+
+ if (m_output.empty())
+ {
+ CLog::Log(LOGERROR, "CCDDARipJob::{} - Error opening file", __func__);
+ return false;
+ }
+
+ // init ripper
+ CFile reader;
+ std::unique_ptr<CEncoder> encoder{};
+ if (!reader.Open(m_input, READ_CACHED) || !(encoder = SetupEncoder(reader)))
+ {
+ CLog::Log(LOGERROR, "CCDDARipJob::{} - Opening failed", __func__);
+ return false;
+ }
+
+ // setup the progress dialog
+ CGUIDialogExtendedProgressBar* pDlgProgress =
+ CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogExtendedProgressBar>(
+ WINDOW_DIALOG_EXT_PROGRESS);
+ CGUIDialogProgressBarHandle* handle = pDlgProgress->GetHandle(g_localizeStrings.Get(605));
+
+ int iTrack = atoi(m_input.substr(13, m_input.size() - 13 - 5).c_str());
+ std::string strLine0 =
+ StringUtils::Format("{:02}. {} - {}", iTrack, m_tag.GetArtistString(), m_tag.GetTitle());
+ handle->SetText(strLine0);
+
+ // start ripping
+ int percent = 0;
+ int oldpercent = 0;
+ bool cancelled(false);
+ int result;
+ while (!cancelled && (result = RipChunk(reader, encoder, percent)) == 0)
+ {
+ cancelled = ShouldCancel(percent, 100);
+ if (percent > oldpercent)
+ {
+ oldpercent = percent;
+ handle->SetPercentage(static_cast<float>(percent));
+ }
+ }
+
+ // close encoder ripper
+ encoder->EncoderClose();
+ encoder.reset();
+ reader.Close();
+
+ if (file.IsRemote() && !cancelled && result == 2)
+ {
+ // copy the ripped track to the share
+ if (!CFile::Copy(m_output, file.GetPath()))
+ {
+ CLog::Log(LOGERROR, "CCDDARipJob::{} - Error copying file from {} to {}", __func__, m_output,
+ file.GetPath());
+ CFile::Delete(m_output);
+ return false;
+ }
+ // delete cached file
+ CFile::Delete(m_output);
+ }
+
+ if (cancelled)
+ {
+ CLog::Log(LOGWARNING, "CCDDARipJob::{} - User Cancelled CDDA Rip", __func__);
+ CFile::Delete(m_output);
+ }
+ else if (result == 1)
+ CLog::Log(LOGERROR, "CCDDARipJob::{} - Error ripping {}", __func__, m_input);
+ else if (result < 0)
+ CLog::Log(LOGERROR, "CCDDARipJob::{} - Error encoding {}", __func__, m_input);
+ else
+ {
+ CLog::Log(LOGINFO, "CCDDARipJob::{} - Finished ripping {}", __func__, m_input);
+ if (m_eject)
+ {
+ CLog::Log(LOGINFO, "CCDDARipJob::{} - Ejecting CD", __func__);
+ CServiceBroker::GetMediaManager().EjectTray();
+ }
+ }
+
+ handle->MarkFinished();
+
+ return !cancelled && result == 2;
+}
+
+int CCDDARipJob::RipChunk(CFile& reader, const std::unique_ptr<CEncoder>& encoder, int& percent)
+{
+ percent = 0;
+
+ uint8_t stream[1024];
+
+ // get data
+ ssize_t result = reader.Read(stream, 1024);
+
+ // return if rip is done or on some kind of error
+ if (result <= 0)
+ return 1;
+
+ // encode data
+ ssize_t encres = encoder->EncoderEncode(stream, result);
+
+ // Get progress indication
+ percent = static_cast<int>(reader.GetPosition() * 100 / reader.GetLength());
+
+ if (reader.GetPosition() == reader.GetLength())
+ return 2;
+
+ return -(1 - encres);
+}
+
+std::unique_ptr<CEncoder> CCDDARipJob::SetupEncoder(CFile& reader)
+{
+ std::unique_ptr<CEncoder> encoder;
+ const std::string audioEncoder = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
+ CSettings::SETTING_AUDIOCDS_ENCODER);
+ if (audioEncoder == "audioencoder.kodi.builtin.aac" ||
+ audioEncoder == "audioencoder.kodi.builtin.wma")
+ {
+ encoder = std::make_unique<CEncoderFFmpeg>();
+ }
+ else
+ {
+ const AddonInfoPtr addonInfo =
+ CServiceBroker::GetAddonMgr().GetAddonInfo(audioEncoder, AddonType::AUDIOENCODER);
+ if (addonInfo)
+ {
+ encoder = std::make_unique<CEncoderAddon>(addonInfo);
+ }
+ }
+ if (!encoder)
+ return std::unique_ptr<CEncoder>{};
+
+ // we have to set the tags before we init the Encoder
+ const std::string strTrack = StringUtils::Format(
+ "{}", std::stol(m_input.substr(13, m_input.size() - 13 - 5), nullptr, 10));
+
+ const std::string itemSeparator =
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicItemSeparator;
+
+ encoder->SetComment(std::string("Ripped with ") + CSysInfo::GetAppName());
+ encoder->SetArtist(StringUtils::Join(m_tag.GetArtist(), itemSeparator));
+ encoder->SetTitle(m_tag.GetTitle());
+ encoder->SetAlbum(m_tag.GetAlbum());
+ encoder->SetAlbumArtist(StringUtils::Join(m_tag.GetAlbumArtist(), itemSeparator));
+ encoder->SetGenre(StringUtils::Join(m_tag.GetGenre(), itemSeparator));
+ encoder->SetTrack(strTrack);
+ encoder->SetTrackLength(static_cast<int>(reader.GetLength()));
+ encoder->SetYear(m_tag.GetYearString());
+
+ // init encoder
+ if (!encoder->EncoderInit(m_output, m_channels, m_rate, m_bps))
+ encoder.reset();
+
+ return encoder;
+}
+
+std::string CCDDARipJob::SetupTempFile()
+{
+ char tmp[MAX_PATH + 1];
+#if defined(TARGET_WINDOWS)
+ using namespace KODI::PLATFORM::WINDOWS;
+ wchar_t tmpW[MAX_PATH];
+ GetTempFileName(ToW(CSpecialProtocol::TranslatePath("special://temp/")).c_str(), L"riptrack", 0,
+ tmpW);
+ auto tmpString = FromW(tmpW);
+ strncpy_s(tmp, tmpString.length(), tmpString.c_str(), MAX_PATH);
+#else
+ int fd;
+ strncpy(tmp, CSpecialProtocol::TranslatePath("special://temp/riptrackXXXXXX").c_str(), MAX_PATH);
+ if ((fd = mkstemp(tmp)) == -1)
+ tmp[0] = '\0';
+ if (fd != -1)
+ close(fd);
+#endif
+ return tmp;
+}
+
+bool CCDDARipJob::operator==(const CJob* job) const
+{
+ if (strcmp(job->GetType(), GetType()) == 0)
+ {
+ const CCDDARipJob* rjob = dynamic_cast<const CCDDARipJob*>(job);
+ if (rjob)
+ {
+ return m_input == rjob->m_input && m_output == rjob->m_output;
+ }
+ }
+ return false;
+}