Skip to content

Commit fed4396

Browse files
authored
Merge pull request xbmc#27321 from damienruscoe/nfofile_refactor
[NFO] File parsing refactor
2 parents 13cd060 + ae8cc65 commit fed4396

File tree

3 files changed

+129
-79
lines changed

3 files changed

+129
-79
lines changed

xbmc/NfoFile.cpp

Lines changed: 112 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -27,105 +27,141 @@
2727
using namespace XFILE;
2828
using namespace ADDON;
2929

30-
CInfoScanner::InfoType CNfoFile::Create(const std::string& strPath,
31-
const ScraperPtr& info,
32-
int index)
30+
// Forward declarations
31+
namespace
3332
{
34-
m_info = info; // assume we can use these settings
35-
m_type = ScraperTypeFromContent(info->Content());
36-
if (Load(strPath) != 0)
37-
return CInfoScanner::InfoType::NONE;
33+
std::vector<ScraperPtr> GetScrapers(AddonType type, const ScraperPtr& selectedScraper);
3834

39-
CFileItemList items;
40-
bool bNfo=false;
41-
bool overrideNfo{false};
35+
} // unnamed namespace
4236

43-
if (m_type == AddonType::SCRAPER_ALBUMS)
37+
CInfoScanner::InfoType CNfoFile::TryParsing(ADDON::AddonType addonType) const
38+
{
39+
using enum CInfoScanner::InfoType;
40+
using enum ADDON::AddonType;
41+
42+
if (addonType == SCRAPER_ALBUMS)
4443
{
4544
CAlbum album;
46-
bNfo = GetDetails(album);
45+
return GetDetails(album) ? FULL : NONE;
4746
}
48-
else if (m_type == AddonType::SCRAPER_ARTISTS)
47+
if (addonType == SCRAPER_ARTISTS)
4948
{
5049
CArtist artist;
51-
bNfo = GetDetails(artist);
50+
return GetDetails(artist) ? FULL : NONE;
5251
}
53-
else if (m_type == AddonType::SCRAPER_TVSHOWS || m_type == AddonType::SCRAPER_MOVIES ||
54-
m_type == AddonType::SCRAPER_MUSICVIDEOS)
52+
if (addonType == SCRAPER_MOVIES || addonType == SCRAPER_TVSHOWS ||
53+
addonType == SCRAPER_MUSICVIDEOS)
5554
{
56-
CVideoInfoTag details;
57-
// Find nth <movie> tag (index is 1 based)
58-
// If it doesn't exist then set bNfo to false
59-
if (m_type == AddonType::SCRAPER_MOVIES)
60-
{
61-
m_headPos = m_doc.find("<movies>");
62-
while (index-- > 0)
63-
{
64-
m_headPos = m_doc.find("<movie", m_headPos + 1);
65-
if (m_headPos == std::string::npos)
66-
break;
67-
}
68-
}
69-
bNfo = m_headPos != std::string::npos ? GetDetails(details) : false;
70-
overrideNfo = details.GetOverride();
55+
if (CVideoInfoTag details; GetDetails(details))
56+
return details.GetOverride() ? OVERRIDE : FULL;
7157
}
58+
return NONE;
59+
}
7260

73-
std::vector<ScraperPtr> vecScrapers = GetScrapers(m_type, m_info);
74-
75-
// search ..
76-
int res = -1;
77-
for (const auto& scraper : vecScrapers)
78-
if ((res = Scrape(scraper, m_scurl, m_doc)) == 0 || res == 2)
61+
bool CNfoFile::SeekToMovieIndex(int index)
62+
{
63+
// Find nth <movie> tag (index is 1 based)
64+
m_headPos = m_doc.find("<movies>");
65+
while (index-- > 0)
66+
{
67+
m_headPos = m_doc.find("<movie", m_headPos + 1);
68+
if (m_headPos == std::string::npos)
7969
break;
70+
}
71+
return m_headPos != std::string::npos;
72+
}
8073

81-
if (res == 2)
74+
CInfoScanner::InfoType CNfoFile::TryParsing(const CURL& nfoPath,
75+
ADDON::ContentType contentType,
76+
int index /* =1 */)
77+
{
78+
if (Load(nfoPath) != 0) // Setup m_doc and m_headPos
8279
return CInfoScanner::InfoType::ERROR_NFO;
83-
if (bNfo)
84-
{
85-
if (!m_scurl.HasUrls())
86-
{
87-
if (overrideNfo || m_doc.find("[scrape url]") != std::string::npos)
88-
return CInfoScanner::InfoType::OVERRIDE;
89-
else
90-
return CInfoScanner::InfoType::FULL;
91-
}
92-
else
93-
return CInfoScanner::InfoType::COMBINED;
94-
}
95-
return m_scurl.HasUrls() ? CInfoScanner::InfoType::URL : CInfoScanner::InfoType::NONE;
80+
81+
const AddonType addonType = ScraperTypeFromContent(contentType);
82+
83+
if (addonType == ADDON::AddonType::SCRAPER_MOVIES && !SeekToMovieIndex(index))
84+
return CInfoScanner::InfoType::NONE;
85+
86+
return TryParsing(addonType);
87+
}
88+
89+
CInfoScanner::InfoType CNfoFile::Create(const std::string& nfoPath,
90+
const ScraperPtr& info,
91+
int index)
92+
{
93+
/* `TryParsing` creates a close approximation to the desired result.
94+
* The desired result would be knowing if any valid URLs have been
95+
* found in the NFO which could be interpreted by a scraper.
96+
* Determining if any valid URLs exist in the NFO file is an expensive
97+
* operation so should only be called if necessary. If the approximate
98+
* result from `TryParsing` is sufficient then use that result.
99+
*
100+
* Below is a table to show the result from `TryParsing` and the
101+
* potential desired results.
102+
*
103+
* | result | Could be converted to: |
104+
* | -------- | -------------------------- |
105+
* | NONE | URL NONE |
106+
* | OVERRIDE | COMBINED OVERRIDE |
107+
* | FULL | COMBINED OVERRIDE FULL |
108+
*
109+
* The following call to `SearchNfoForScraperUrls` will generate the
110+
* desired result from this approximation.
111+
*
112+
* This call is expensive as it encodes the NFO file into a URL param
113+
* and executes a python interpreter for each installed python scraper.
114+
*/
115+
const CInfoScanner::InfoType result = TryParsing(CURL{nfoPath}, info->Content(), index);
116+
if (result == CInfoScanner::InfoType::ERROR_NFO)
117+
return CInfoScanner::InfoType::NONE;
118+
return SearchNfoForScraperUrls(result, info);
96119
}
97120

98-
// return value: 0 - success; 1 - no result; skip; 2 - error
99-
int CNfoFile::Scrape(const ScraperPtr& scraper, CScraperUrl& url, const std::string& content)
121+
CInfoScanner::InfoType CNfoFile::SearchNfoForScraperUrls(CInfoScanner::InfoType parseResult,
122+
const ScraperPtr& info)
100123
{
101-
if (scraper->IsNoop())
102-
{
103-
url = CScraperUrl();
104-
return 0;
105-
}
124+
using enum CInfoScanner::InfoType;
106125

107-
scraper->ClearCache();
126+
SetScraperInfo(info); // assume we can use these settings
127+
const AddonType addonType = ScraperTypeFromContent(info->Content());
108128

109-
try
110-
{
111-
url = scraper->NfoUrl(content);
112-
}
113-
catch (const CScraperError &sce)
129+
for (const auto& scraper : GetScrapers(addonType, info))
114130
{
115-
CVideoInfoDownloader::ShowErrorDialog(sce);
116-
if (!sce.FAborted())
117-
return 2;
131+
if (scraper->IsNoop())
132+
{
133+
m_scurl = CScraperUrl();
134+
break;
135+
}
136+
137+
scraper->ClearCache();
138+
try
139+
{
140+
m_scurl = scraper->NfoUrl(m_doc);
141+
}
142+
catch (const CScraperError& sce)
143+
{
144+
CVideoInfoDownloader::ShowErrorDialog(sce);
145+
if (!sce.FAborted())
146+
return ERROR_NFO;
147+
}
148+
149+
if (m_scurl.HasUrls())
150+
return (parseResult == FULL || parseResult == OVERRIDE) ? COMBINED : URL;
118151
}
119152

120-
return url.HasUrls() ? 0 : 1;
153+
if (parseResult == FULL && m_doc.find("[scrape url]") != std::string::npos)
154+
return OVERRIDE;
155+
156+
return parseResult;
121157
}
122158

123-
int CNfoFile::Load(const std::string& strFile)
159+
int CNfoFile::Load(const CURL& nfoPath)
124160
{
125161
Close();
126162
XFILE::CFile file;
127163
std::vector<uint8_t> buf;
128-
if (file.LoadFile(strFile, buf) > 0)
164+
if (file.LoadFile(nfoPath, buf) > 0)
129165
{
130166
m_doc.assign(reinterpret_cast<char*>(buf.data()), buf.size());
131167
m_headPos = 0;
@@ -142,7 +178,10 @@ void CNfoFile::Close()
142178
m_scurl.Clear();
143179
}
144180

145-
std::vector<ScraperPtr> CNfoFile::GetScrapers(AddonType type, const ScraperPtr& selectedScraper)
181+
namespace
182+
{
183+
184+
std::vector<ScraperPtr> GetScrapers(AddonType type, const ScraperPtr& selectedScraper)
146185
{
147186
AddonPtr addon;
148187
ScraperPtr defaultScraper;
@@ -180,3 +219,5 @@ std::vector<ScraperPtr> CNfoFile::GetScrapers(AddonType type, const ScraperPtr&
180219

181220
return vecScrapers;
182221
}
222+
223+
} // unnamed namespace

xbmc/NfoFile.h

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//////////////////////////////////////////////////////////////////////
1414

1515
#include "InfoScanner.h"
16+
#include "URL.h"
1617
#include "addons/Scraper.h"
1718
#include "utils/XBMCTinyXML.h"
1819

@@ -33,7 +34,7 @@ class CNfoFile
3334
CInfoScanner::InfoType Create(const std::string&, const ADDON::ScraperPtr&, int index = 1);
3435

3536
template<class T>
36-
bool GetDetails(T& details, const char* document = nullptr, bool prioritise = false)
37+
bool GetDetails(T& details, const char* document = nullptr, bool prioritise = false) const
3738
{
3839
CXBMCTinyXML doc;
3940
if (document)
@@ -51,17 +52,19 @@ class CNfoFile
5152
ADDON::ScraperPtr GetScraperInfo() { return m_info; }
5253
const CScraperUrl &ScraperUrl() const { return m_scurl; }
5354

54-
static int Scrape(const ADDON::ScraperPtr& scraper, CScraperUrl& url, const std::string& content);
55-
56-
static std::vector<ADDON::ScraperPtr> GetScrapers(ADDON::AddonType type,
57-
const ADDON::ScraperPtr& selectedScraper);
58-
5955
private:
56+
CInfoScanner::InfoType TryParsing(ADDON::AddonType addonType) const;
57+
CInfoScanner::InfoType TryParsing(const CURL& nfoPath,
58+
ADDON::ContentType contentType,
59+
int index = 1);
60+
CInfoScanner::InfoType SearchNfoForScraperUrls(CInfoScanner::InfoType parseResult,
61+
const ADDON::ScraperPtr& info);
62+
bool SeekToMovieIndex(int index);
63+
6064
std::string m_doc;
6165
size_t m_headPos = 0;
6266
ADDON::ScraperPtr m_info;
63-
ADDON::AddonType m_type{};
6467
CScraperUrl m_scurl;
6568

66-
int Load(const std::string&);
69+
int Load(const CURL&);
6770
};

xbmc/video/tags/VideoTagLoaderFFmpeg.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,12 @@ CInfoScanner::InfoType CVideoTagLoaderFFmpeg::LoadMKV(CVideoInfoTag& tag,
191191
}
192192
else
193193
{
194+
/*! @todo Investigate if this is correct.
195+
* The first parameter to `CNfoFile::Create` is the **path** to the NFO file.
196+
* `content` here appears to be the __contents__ of an NFO file.
197+
* If this assumption is correct the parsing will always fail and
198+
* `nfo.ScraperUrl()` will always return an empty URL.
199+
*/
194200
nfo.Create(content, m_info);
195201
m_url = nfo.ScraperUrl();
196202
return CInfoScanner::InfoType::URL;

0 commit comments

Comments
 (0)