2 * Copyright (C) 2005-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.
8 #include "BlurayDirectory.h"
12 #include "FileItemList.h"
15 #include "filesystem/BlurayCallback.h"
16 #include "filesystem/Directory.h"
17 #include "guilib/LocalizeStrings.h"
18 #include "utils/LangCodeExpander.h"
19 #include "utils/RegExp.h"
20 #include "utils/StringUtils.h"
21 #include "utils/URIUtils.h"
22 #include "utils/log.h"
23 #include "video/VideoInfoTag.h"
32 #include <libbluray/bluray-version.h>
33 #include <libbluray/bluray.h>
34 #include <libbluray/filesystem.h>
35 #include <libbluray/log_control.h>
40 #define MAIN_TITLE_LENGTH_PERCENT 70 /** Minimum length of main titles, based on longest title */
42 CBlurayDirectory::~CBlurayDirectory()
47 void CBlurayDirectory::Dispose()
56 std::string
CBlurayDirectory::GetBlurayTitle()
58 return GetDiscInfoString(DiscInfo::TITLE
);
61 std::string
CBlurayDirectory::GetBlurayID()
63 return GetDiscInfoString(DiscInfo::ID
);
66 std::string
CBlurayDirectory::GetDiscInfoString(DiscInfo info
)
70 case XFILE::CBlurayDirectory::DiscInfo::TITLE
:
72 if (!m_blurayInitialized
)
74 const BLURAY_DISC_INFO
* disc_info
= bd_get_disc_info(m_bd
);
75 if (!disc_info
|| !disc_info
->bluray_detected
)
78 std::string title
= "";
80 #if (BLURAY_VERSION > BLURAY_VERSION_CODE(1,0,0))
81 title
= disc_info
->disc_name
? disc_info
->disc_name
: "";
86 case XFILE::CBlurayDirectory::DiscInfo::ID
:
88 if (!m_blurayInitialized
)
91 const BLURAY_DISC_INFO
* disc_info
= bd_get_disc_info(m_bd
);
92 if (!disc_info
|| !disc_info
->bluray_detected
)
97 #if (BLURAY_VERSION > BLURAY_VERSION_CODE(1,0,0))
98 id
= disc_info
->udf_volume_id
? disc_info
->udf_volume_id
: "";
102 id
= HexToString(disc_info
->disc_id
, 20);
115 std::shared_ptr
<CFileItem
> CBlurayDirectory::GetTitle(const BLURAY_TITLE_INFO
* title
,
116 const std::string
& label
)
120 CFileItemPtr
item(new CFileItem("", false));
122 buf
= StringUtils::Format("BDMV/PLAYLIST/{:05}.mpls", title
->playlist
);
123 path
.SetFileName(buf
);
124 item
->SetPath(path
.Get());
125 int duration
= (int)(title
->duration
/ 90000);
126 item
->GetVideoInfoTag()->SetDuration(duration
);
127 item
->GetVideoInfoTag()->m_iTrack
= title
->playlist
;
128 buf
= StringUtils::Format(label
, title
->playlist
);
129 item
->m_strTitle
= buf
;
131 chap
= StringUtils::Format(g_localizeStrings
.Get(25007), title
->chapter_count
,
132 StringUtils::SecondsToTimeString(duration
));
133 item
->SetLabel2(chap
);
135 item
->SetArt("icon", "DefaultVideo.png");
136 for(unsigned int i
= 0; i
< title
->clip_count
; ++i
)
137 item
->m_dwSize
+= title
->clips
[i
].pkt_count
* 192;
142 void CBlurayDirectory::GetTitles(bool main
, CFileItemList
&items
)
144 std::vector
<BLURAY_TITLE_INFO
*> titleList
;
145 uint64_t minDuration
= 0;
147 // Searching for a user provided list of playlists.
149 titleList
= GetUserPlaylists();
151 if (!main
|| titleList
.empty())
153 uint32_t numTitles
= bd_get_titles(m_bd
, TITLES_RELEVANT
, 0);
155 for (uint32_t i
= 0; i
< numTitles
; i
++)
157 BLURAY_TITLE_INFO
* t
= bd_get_title_info(m_bd
, i
, 0);
161 CLog::Log(LOGDEBUG
, "CBlurayDirectory - unable to get title {}", i
);
165 if (main
&& t
->duration
> minDuration
)
166 minDuration
= t
->duration
;
168 titleList
.emplace_back(t
);
172 minDuration
= minDuration
* MAIN_TITLE_LENGTH_PERCENT
/ 100;
174 for (auto& title
: titleList
)
176 if (title
->duration
< minDuration
)
179 items
.Add(GetTitle(title
, main
? g_localizeStrings
.Get(25004) /* Main Title */ : g_localizeStrings
.Get(25005) /* Title */));
180 bd_free_title_info(title
);
184 void CBlurayDirectory::GetRoot(CFileItemList
&items
)
186 GetTitles(true, items
);
191 path
.SetFileName(URIUtils::AddFileToFolder(m_url
.GetFileName(), "titles"));
192 item
= std::make_shared
<CFileItem
>();
193 item
->SetPath(path
.Get());
194 item
->m_bIsFolder
= true;
195 item
->SetLabel(g_localizeStrings
.Get(25002) /* All titles */);
196 item
->SetArt("icon", "DefaultVideoPlaylists.png");
199 const BLURAY_DISC_INFO
* disc_info
= bd_get_disc_info(m_bd
);
200 if (disc_info
&& disc_info
->no_menu_support
)
202 CLog::Log(LOGDEBUG
, "CBlurayDirectory::GetRoot - no menu support, skipping menu entry");
206 path
.SetFileName("menu");
207 item
= std::make_shared
<CFileItem
>();
208 item
->SetPath(path
.Get());
209 item
->m_bIsFolder
= false;
210 item
->SetLabel(g_localizeStrings
.Get(25003) /* Menus */);
211 item
->SetArt("icon", "DefaultProgram.png");
215 bool CBlurayDirectory::GetDirectory(const CURL
& url
, CFileItemList
&items
)
219 std::string root
= m_url
.GetHostName();
220 std::string file
= m_url
.GetFileName();
221 URIUtils::RemoveSlashAtEnd(file
);
222 URIUtils::RemoveSlashAtEnd(root
);
224 if (!InitializeBluray(root
))
229 else if(file
== "root/titles")
230 GetTitles(false, items
);
233 CURL url2
= GetUnderlyingCURL(url
);
234 CDirectory::CHints hints
;
235 hints
.flags
= m_flags
;
236 if (!CDirectory::GetDirectory(url2
, items
, hints
))
240 items
.AddSortMethod(SortByTrackNumber
, 554, LABEL_MASKS("%L", "%D", "%L", "")); // FileName, Duration | Foldername, empty
241 items
.AddSortMethod(SortBySize
, 553, LABEL_MASKS("%L", "%I", "%L", "%I")); // FileName, Size | Foldername, Size
246 CURL
CBlurayDirectory::GetUnderlyingCURL(const CURL
& url
)
248 assert(url
.IsProtocol("bluray"));
249 std::string host
= url
.GetHostName();
250 const std::string
& filename
= url
.GetFileName();
251 return CURL(host
.append(filename
));
254 bool CBlurayDirectory::InitializeBluray(const std::string
&root
)
256 bd_set_debug_handler(CBlurayCallback::bluray_logger
);
257 bd_set_debug_mask(DBG_CRIT
| DBG_BLURAY
| DBG_NAV
);
263 CLog::Log(LOGERROR
, "CBlurayDirectory::InitializeBluray - failed to initialize libbluray");
267 std::string langCode
;
268 g_LangCodeExpander
.ConvertToISO6392T(g_langInfo
.GetDVDMenuLanguage(), langCode
);
269 bd_set_player_setting_str(m_bd
, BLURAY_PLAYER_SETTING_MENU_LANG
, langCode
.c_str());
271 if (!bd_open_files(m_bd
, const_cast<std::string
*>(&root
), CBlurayCallback::dir_open
, CBlurayCallback::file_open
))
273 CLog::Log(LOGERROR
, "CBlurayDirectory::InitializeBluray - failed to open {}",
274 CURL::GetRedacted(root
));
277 m_blurayInitialized
= true;
282 std::string
CBlurayDirectory::HexToString(const uint8_t *buf
, int count
)
284 std::array
<char, 42> tmp
;
286 for (int i
= 0; i
< count
; i
++)
288 sprintf(tmp
.data() + (i
* 2), "%02x", buf
[i
]);
291 return std::string(std::begin(tmp
), std::end(tmp
));
294 std::vector
<BLURAY_TITLE_INFO
*> CBlurayDirectory::GetUserPlaylists()
296 std::string root
= m_url
.GetHostName();
297 std::string discInfPath
= URIUtils::AddFileToFolder(root
, "disc.inf");
298 std::vector
<BLURAY_TITLE_INFO
*> userTitles
;
302 if (file
.Open(discInfPath
))
304 CLog::Log(LOGDEBUG
, "CBlurayDirectory::GetTitles - disc.inf found");
307 if (!pl
.RegComp("(\\d+)"))
313 uint8_t maxLines
= 100;
314 while ((maxLines
> 0) && file
.ReadString(buffer
, 1024))
317 if (StringUtils::StartsWithNoCase(buffer
, "playlists"))
320 while ((pos
= pl
.RegFind(buffer
, static_cast<unsigned int>(pos
))) >= 0)
322 std::string playlist
= pl
.GetMatch(0);
323 uint32_t len
= static_cast<uint32_t>(playlist
.length());
327 unsigned long int plNum
= strtoul(playlist
.c_str(), nullptr, 10);
329 BLURAY_TITLE_INFO
* t
= bd_get_playlist_info(m_bd
, static_cast<uint32_t>(plNum
), 0);
331 userTitles
.emplace_back(t
);
334 if (static_cast<int64_t>(pos
) + static_cast<int64_t>(len
) > INT_MAX
)
346 } /* namespace XFILE */