1 /*
2 * Copyright (C) 2014 Arne Morten Kvarving
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 * See LICENSES/README.md for more information.
6 */
7
8 #include "AudioBookFileDirectory.h"
9
10 #include "FileItem.h"
11 #include "TextureDatabase.h"
12 #include "URL.h"
13 #include "Util.h"
14 #include "filesystem/File.h"
15 #include "guilib/LocalizeStrings.h"
16 #include "music/tags/MusicInfoTag.h"
17 #include "utils/StringUtils.h"
18
19 using namespace XFILE;
20
cfile_file_read(void * h,uint8_t * buf,int size)21 static int cfile_file_read(void *h, uint8_t* buf, int size)
22 {
23 CFile* pFile = static_cast<CFile*>(h);
24 return pFile->Read(buf, size);
25 }
26
cfile_file_seek(void * h,int64_t pos,int whence)27 static int64_t cfile_file_seek(void *h, int64_t pos, int whence)
28 {
29 CFile* pFile = static_cast<CFile*>(h);
30 if(whence == AVSEEK_SIZE)
31 return pFile->GetLength();
32 else
33 return pFile->Seek(pos, whence & ~AVSEEK_FORCE);
34 }
35
~CAudioBookFileDirectory(void)36 CAudioBookFileDirectory::~CAudioBookFileDirectory(void)
37 {
38 if (m_fctx)
39 avformat_close_input(&m_fctx);
40 if (m_ioctx)
41 {
42 av_free(m_ioctx->buffer);
43 av_free(m_ioctx);
44 }
45 }
46
GetDirectory(const CURL & url,CFileItemList & items)47 bool CAudioBookFileDirectory::GetDirectory(const CURL& url,
48 CFileItemList &items)
49 {
50 if (!m_fctx && !ContainsFiles(url))
51 return true;
52
53 std::string title;
54 std::string author;
55 std::string album;
56
57 AVDictionaryEntry* tag=nullptr;
58 while ((tag = av_dict_get(m_fctx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX)))
59 {
60 if (StringUtils::CompareNoCase(tag->key, "title") == 0)
61 title = tag->value;
62 else if (StringUtils::CompareNoCase(tag->key, "album") == 0)
63 album = tag->value;
64 else if (StringUtils::CompareNoCase(tag->key, "artist") == 0)
65 author = tag->value;
66 }
67
68 std::string thumb;
69 if (m_fctx->nb_chapters > 1)
70 thumb = CTextureUtils::GetWrappedImageURL(url.Get(), "music");
71
72 for (size_t i=0;i<m_fctx->nb_chapters;++i)
73 {
74 tag=nullptr;
75 std::string chaptitle = StringUtils::Format(g_localizeStrings.Get(25010).c_str(), i+1);
76 std::string chapauthor;
77 std::string chapalbum;
78 while ((tag=av_dict_get(m_fctx->chapters[i]->metadata, "", tag, AV_DICT_IGNORE_SUFFIX)))
79 {
80 if (StringUtils::CompareNoCase(tag->key, "title") == 0)
81 chaptitle = tag->value;
82 else if (StringUtils::CompareNoCase(tag->key, "artist") == 0)
83 chapauthor = tag->value;
84 else if (StringUtils::CompareNoCase(tag->key, "album") == 0)
85 chapalbum = tag->value;
86 }
87 CFileItemPtr item(new CFileItem(url.Get(),false));
88 item->GetMusicInfoTag()->SetTrackNumber(i+1);
89 item->GetMusicInfoTag()->SetLoaded(true);
90 item->GetMusicInfoTag()->SetTitle(chaptitle);
91 if (album.empty())
92 item->GetMusicInfoTag()->SetAlbum(title);
93 else if (chapalbum.empty())
94 item->GetMusicInfoTag()->SetAlbum(album);
95 else
96 item->GetMusicInfoTag()->SetAlbum(chapalbum);
97 if (chapauthor.empty())
98 item->GetMusicInfoTag()->SetArtist(author);
99 else
100 item->GetMusicInfoTag()->SetArtist(chapauthor);
101
102 item->SetLabel(StringUtils::Format("{0:02}. {1} - {2}",i+1,
103 item->GetMusicInfoTag()->GetAlbum().c_str(),
104 item->GetMusicInfoTag()->GetTitle()).c_str());
105 item->m_lStartOffset = CUtil::ConvertSecsToMilliSecs(m_fctx->chapters[i]->start*av_q2d(m_fctx->chapters[i]->time_base));
106 item->m_lEndOffset = m_fctx->chapters[i]->end*av_q2d(m_fctx->chapters[i]->time_base);
107 int compare = m_fctx->duration / (AV_TIME_BASE);
108 if (item->m_lEndOffset < 0 || item->m_lEndOffset > compare)
109 {
110 if (i < m_fctx->nb_chapters-1)
111 item->m_lEndOffset = m_fctx->chapters[i+1]->start*av_q2d(m_fctx->chapters[i+1]->time_base);
112 else
113 item->m_lEndOffset = compare;
114 }
115 item->m_lEndOffset = CUtil::ConvertSecsToMilliSecs(item->m_lEndOffset);
116 item->GetMusicInfoTag()->SetDuration(CUtil::ConvertMilliSecsToSecsInt(item->m_lEndOffset - item->m_lStartOffset));
117 item->SetProperty("item_start", item->m_lStartOffset);
118 if (!thumb.empty())
119 item->SetArt("thumb", thumb);
120 items.Add(item);
121 }
122
123 return true;
124 }
125
Exists(const CURL & url)126 bool CAudioBookFileDirectory::Exists(const CURL& url)
127 {
128 return CFile::Exists(url) && ContainsFiles(url);
129 }
130
ContainsFiles(const CURL & url)131 bool CAudioBookFileDirectory::ContainsFiles(const CURL& url)
132 {
133 CFile file;
134 if (!file.Open(url))
135 return false;
136
137 uint8_t* buffer = (uint8_t*)av_malloc(32768);
138 m_ioctx = avio_alloc_context(buffer, 32768, 0, &file, cfile_file_read,
139 nullptr, cfile_file_seek);
140
141 m_fctx = avformat_alloc_context();
142 m_fctx->pb = m_ioctx;
143
144 if (file.IoControl(IOCTRL_SEEK_POSSIBLE, nullptr) == 0)
145 m_ioctx->seekable = 0;
146
147 m_ioctx->max_packet_size = 32768;
148
149 AVInputFormat* iformat=nullptr;
150 av_probe_input_buffer(m_ioctx, &iformat, url.Get().c_str(), nullptr, 0, 0);
151
152 bool contains = false;
153 if (avformat_open_input(&m_fctx, url.Get().c_str(), iformat, nullptr) < 0)
154 {
155 if (m_fctx)
156 avformat_close_input(&m_fctx);
157 av_free(m_ioctx->buffer);
158 av_free(m_ioctx);
159 return false;
160 }
161
162 contains = m_fctx->nb_chapters > 1;
163
164 return contains;
165 }
166