2 * Copyright (C) 2014 Arne Morten Kvarving
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 * See LICENSES/README.md for more information.
8 #include "AudioBookFileDirectory.h"
11 #include "FileItemList.h"
14 #include "cores/FFmpeg.h"
15 #include "filesystem/File.h"
16 #include "guilib/LocalizeStrings.h"
17 #include "imagefiles/ImageFileURL.h"
18 #include "music/tags/MusicInfoTag.h"
19 #include "utils/StringUtils.h"
21 using namespace XFILE
;
23 static int cfile_file_read(void *h
, uint8_t* buf
, int size
)
25 CFile
* pFile
= static_cast<CFile
*>(h
);
26 return pFile
->Read(buf
, size
);
29 static int64_t cfile_file_seek(void *h
, int64_t pos
, int whence
)
31 CFile
* pFile
= static_cast<CFile
*>(h
);
32 if(whence
== AVSEEK_SIZE
)
33 return pFile
->GetLength();
35 return pFile
->Seek(pos
, whence
& ~AVSEEK_FORCE
);
38 CAudioBookFileDirectory::~CAudioBookFileDirectory(void)
41 avformat_close_input(&m_fctx
);
44 av_free(m_ioctx
->buffer
);
49 bool CAudioBookFileDirectory::GetDirectory(const CURL
& url
,
52 if (!m_fctx
&& !ContainsFiles(url
))
59 AVDictionaryEntry
* tag
=nullptr;
60 while ((tag
= av_dict_get(m_fctx
->metadata
, "", tag
, AV_DICT_IGNORE_SUFFIX
)))
62 if (StringUtils::CompareNoCase(tag
->key
, "title") == 0)
64 else if (StringUtils::CompareNoCase(tag
->key
, "album") == 0)
66 else if (StringUtils::CompareNoCase(tag
->key
, "artist") == 0)
71 if (m_fctx
->nb_chapters
> 1)
72 thumb
= IMAGE_FILES::URLFromFile(url
.Get(), "music");
74 for (size_t i
=0;i
<m_fctx
->nb_chapters
;++i
)
77 std::string chaptitle
= StringUtils::Format(g_localizeStrings
.Get(25010), i
+ 1);
78 std::string chapauthor
;
79 std::string chapalbum
;
80 while ((tag
=av_dict_get(m_fctx
->chapters
[i
]->metadata
, "", tag
, AV_DICT_IGNORE_SUFFIX
)))
82 if (StringUtils::CompareNoCase(tag
->key
, "title") == 0)
83 chaptitle
= tag
->value
;
84 else if (StringUtils::CompareNoCase(tag
->key
, "artist") == 0)
85 chapauthor
= tag
->value
;
86 else if (StringUtils::CompareNoCase(tag
->key
, "album") == 0)
87 chapalbum
= tag
->value
;
89 CFileItemPtr
item(new CFileItem(url
.Get(),false));
90 item
->GetMusicInfoTag()->SetTrackNumber(i
+1);
91 item
->GetMusicInfoTag()->SetLoaded(true);
92 item
->GetMusicInfoTag()->SetTitle(chaptitle
);
94 item
->GetMusicInfoTag()->SetAlbum(title
);
95 else if (chapalbum
.empty())
96 item
->GetMusicInfoTag()->SetAlbum(album
);
98 item
->GetMusicInfoTag()->SetAlbum(chapalbum
);
99 if (chapauthor
.empty())
100 item
->GetMusicInfoTag()->SetArtist(author
);
102 item
->GetMusicInfoTag()->SetArtist(chapauthor
);
104 item
->SetLabel(StringUtils::Format("{0:02}. {1} - {2}", i
+ 1,
105 item
->GetMusicInfoTag()->GetAlbum(),
106 item
->GetMusicInfoTag()->GetTitle()));
107 item
->SetStartOffset(CUtil::ConvertSecsToMilliSecs(m_fctx
->chapters
[i
]->start
*
108 av_q2d(m_fctx
->chapters
[i
]->time_base
)));
109 item
->SetEndOffset(CUtil::ConvertSecsToMilliSecs(m_fctx
->chapters
[i
]->end
*
110 av_q2d(m_fctx
->chapters
[i
]->time_base
)));
111 int compare
= m_fctx
->streams
[0]->duration
* av_q2d(m_fctx
->streams
[0]->time_base
);
112 if (item
->GetEndOffset() < 0 ||
113 item
->GetEndOffset() > CUtil::ConvertMilliSecsToSecs(m_fctx
->duration
))
115 if (i
< m_fctx
->nb_chapters
- 1)
116 item
->SetEndOffset(CUtil::ConvertSecsToMilliSecs(
117 m_fctx
->chapters
[i
+ 1]->start
* av_q2d(m_fctx
->chapters
[i
+ 1]->time_base
)));
120 item
->SetEndOffset(m_fctx
->duration
); // mka file
121 if (item
->GetEndOffset() < 0)
122 item
->SetEndOffset(compare
); // m4b file
125 item
->GetMusicInfoTag()->SetDuration(
126 CUtil::ConvertMilliSecsToSecsInt(item
->GetEndOffset() - item
->GetStartOffset()));
127 item
->SetProperty("item_start", item
->GetStartOffset());
128 item
->SetProperty("audio_bookmark", item
->GetStartOffset());
130 item
->SetArt("thumb", thumb
);
137 bool CAudioBookFileDirectory::Exists(const CURL
& url
)
139 return CFile::Exists(url
) && ContainsFiles(url
);
142 bool CAudioBookFileDirectory::ContainsFiles(const CURL
& url
)
148 uint8_t* buffer
= (uint8_t*)av_malloc(32768);
149 m_ioctx
= avio_alloc_context(buffer
, 32768, 0, &file
, cfile_file_read
,
150 nullptr, cfile_file_seek
);
152 m_fctx
= avformat_alloc_context();
153 m_fctx
->pb
= m_ioctx
;
155 if (file
.IoControl(IOCTRL_SEEK_POSSIBLE
, nullptr) == 0)
156 m_ioctx
->seekable
= 0;
158 m_ioctx
->max_packet_size
= 32768;
160 const AVInputFormat
* iformat
= nullptr;
161 av_probe_input_buffer(m_ioctx
, &iformat
, url
.Get().c_str(), nullptr, 0, 0);
163 bool contains
= false;
164 if (avformat_open_input(&m_fctx
, url
.Get().c_str(), iformat
, nullptr) < 0)
167 avformat_close_input(&m_fctx
);
168 av_free(m_ioctx
->buffer
);
173 contains
= m_fctx
->nb_chapters
> 1;