2 * Copyright (C) 2012-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "CDDARipJob.h"
12 #include "EncoderAddon.h"
13 #include "EncoderFFmpeg.h"
15 #include "ServiceBroker.h"
17 #include "addons/AddonManager.h"
18 #include "addons/addoninfo/AddonType.h"
19 #include "dialogs/GUIDialogExtendedProgressBar.h"
20 #include "filesystem/File.h"
21 #include "filesystem/SpecialProtocol.h"
22 #include "guilib/GUIComponent.h"
23 #include "guilib/GUIWindowManager.h"
24 #include "guilib/LocalizeStrings.h"
25 #include "network/NetworkFileItemClassify.h"
26 #include "settings/AdvancedSettings.h"
27 #include "settings/Settings.h"
28 #include "settings/SettingsComponent.h"
29 #include "storage/MediaManager.h"
30 #include "utils/StringUtils.h"
31 #include "utils/SystemInfo.h"
32 #include "utils/log.h"
34 #if defined(TARGET_WINDOWS)
35 #include "platform/win32/CharsetConverter.h"
38 using namespace ADDON
;
39 using namespace MUSIC_INFO
;
40 using namespace XFILE
;
42 using namespace KODI::CDRIP
;
44 CCDDARipJob::CCDDARipJob(const std::string
& input
,
45 const std::string
& output
,
46 const CMusicInfoTag
& tag
,
50 unsigned int channels
,
57 m_output(CUtil::MakeLegalPath(output
)),
63 CCDDARipJob::~CCDDARipJob() = default;
65 bool CCDDARipJob::DoWork()
67 CLog::Log(LOGINFO
, "CCDDARipJob::{} - Start ripping track {} to {}", __func__
, m_input
, m_output
);
69 // if we are ripping to a samba share, rip it to hd first and then copy it to the share
70 CFileItem
file(m_output
, false);
71 if (NETWORK::IsRemote(file
))
72 m_output
= SetupTempFile();
76 CLog::Log(LOGERROR
, "CCDDARipJob::{} - Error opening file", __func__
);
82 std::unique_ptr
<CEncoder
> encoder
{};
83 if (!reader
.Open(m_input
, READ_CACHED
) || !(encoder
= SetupEncoder(reader
)))
85 CLog::Log(LOGERROR
, "CCDDARipJob::{} - Opening failed", __func__
);
89 // setup the progress dialog
90 CGUIDialogExtendedProgressBar
* pDlgProgress
=
91 CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogExtendedProgressBar
>(
92 WINDOW_DIALOG_EXT_PROGRESS
);
93 CGUIDialogProgressBarHandle
* handle
= pDlgProgress
->GetHandle(g_localizeStrings
.Get(605));
95 int iTrack
= atoi(m_input
.substr(13, m_input
.size() - 13 - 5).c_str());
96 std::string strLine0
=
97 StringUtils::Format("{:02}. {} - {}", iTrack
, m_tag
.GetArtistString(), m_tag
.GetTitle());
98 handle
->SetText(strLine0
);
103 bool cancelled
{false};
105 while (!cancelled
&& (result
= RipChunk(reader
, encoder
, percent
)) == 0)
107 cancelled
= ShouldCancel(percent
, 100);
108 if (percent
> oldpercent
)
110 oldpercent
= percent
;
111 handle
->SetPercentage(static_cast<float>(percent
));
115 // close encoder ripper
116 encoder
->EncoderClose();
120 if (NETWORK::IsRemote(file
) && !cancelled
&& result
== 2)
122 // copy the ripped track to the share
123 if (!CFile::Copy(m_output
, file
.GetPath()))
125 CLog::Log(LOGERROR
, "CCDDARipJob::{} - Error copying file from {} to {}", __func__
, m_output
,
127 CFile::Delete(m_output
);
130 // delete cached file
131 CFile::Delete(m_output
);
136 CLog::Log(LOGWARNING
, "CCDDARipJob::{} - User Cancelled CDDA Rip", __func__
);
137 CFile::Delete(m_output
);
139 else if (result
== 1)
140 CLog::Log(LOGERROR
, "CCDDARipJob::{} - Error ripping {}", __func__
, m_input
);
142 CLog::Log(LOGERROR
, "CCDDARipJob::{} - Error encoding {}", __func__
, m_input
);
145 CLog::Log(LOGINFO
, "CCDDARipJob::{} - Finished ripping {}", __func__
, m_input
);
148 CLog::Log(LOGINFO
, "CCDDARipJob::{} - Ejecting CD", __func__
);
149 CServiceBroker::GetMediaManager().EjectTray();
153 handle
->MarkFinished();
155 return !cancelled
&& result
== 2;
158 int CCDDARipJob::RipChunk(CFile
& reader
, const std::unique_ptr
<CEncoder
>& encoder
, int& percent
)
162 uint8_t stream
[1024];
165 ssize_t result
= reader
.Read(stream
, 1024);
167 // return if rip is done or on some kind of error
172 ssize_t encres
= encoder
->EncoderEncode(stream
, result
);
174 // Get progress indication
175 percent
= static_cast<int>(reader
.GetPosition() * 100 / reader
.GetLength());
177 if (reader
.GetPosition() == reader
.GetLength())
180 return -(1 - encres
);
183 std::unique_ptr
<CEncoder
> CCDDARipJob::SetupEncoder(CFile
& reader
)
185 std::unique_ptr
<CEncoder
> encoder
;
186 const std::string audioEncoder
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
187 CSettings::SETTING_AUDIOCDS_ENCODER
);
188 if (audioEncoder
== "audioencoder.kodi.builtin.aac" ||
189 audioEncoder
== "audioencoder.kodi.builtin.wma")
191 encoder
= std::make_unique
<CEncoderFFmpeg
>();
195 const AddonInfoPtr addonInfo
=
196 CServiceBroker::GetAddonMgr().GetAddonInfo(audioEncoder
, AddonType::AUDIOENCODER
);
199 encoder
= std::make_unique
<CEncoderAddon
>(addonInfo
);
203 return std::unique_ptr
<CEncoder
>{};
205 // we have to set the tags before we init the Encoder
206 const std::string strTrack
= StringUtils::Format(
207 "{}", std::stol(m_input
.substr(13, m_input
.size() - 13 - 5), nullptr, 10));
209 const std::string itemSeparator
=
210 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicItemSeparator
;
212 encoder
->SetComment(std::string("Ripped with ") + CSysInfo::GetAppName());
213 encoder
->SetArtist(StringUtils::Join(m_tag
.GetArtist(), itemSeparator
));
214 encoder
->SetTitle(m_tag
.GetTitle());
215 encoder
->SetAlbum(m_tag
.GetAlbum());
216 encoder
->SetAlbumArtist(StringUtils::Join(m_tag
.GetAlbumArtist(), itemSeparator
));
217 encoder
->SetGenre(StringUtils::Join(m_tag
.GetGenre(), itemSeparator
));
218 encoder
->SetTrack(strTrack
);
219 encoder
->SetTrackLength(static_cast<int>(reader
.GetLength()));
220 encoder
->SetYear(m_tag
.GetYearString());
223 if (!encoder
->EncoderInit(m_output
, m_channels
, m_rate
, m_bps
))
229 std::string
CCDDARipJob::SetupTempFile()
231 char tmp
[MAX_PATH
+ 1];
232 #if defined(TARGET_WINDOWS)
233 using namespace KODI::PLATFORM::WINDOWS
;
234 wchar_t tmpW
[MAX_PATH
];
235 GetTempFileName(ToW(CSpecialProtocol::TranslatePath("special://temp/")).c_str(), L
"riptrack", 0,
237 auto tmpString
= FromW(tmpW
);
238 strncpy_s(tmp
, tmpString
.length(), tmpString
.c_str(), MAX_PATH
);
241 strncpy(tmp
, CSpecialProtocol::TranslatePath("special://temp/riptrackXXXXXX").c_str(), MAX_PATH
);
242 if ((fd
= mkstemp(tmp
)) == -1)
250 bool CCDDARipJob::operator==(const CJob
* job
) const
252 if (strcmp(job
->GetType(), GetType()) == 0)
254 const CCDDARipJob
* rjob
= dynamic_cast<const CCDDARipJob
*>(job
);
257 return m_input
== rjob
->m_input
&& m_output
== rjob
->m_output
;