foobar2000 SDK  2015-08-03
playlist_loader.cpp
Go to the documentation of this file.
1 #include "foobar2000.h"
2 
3 #if FOOBAR2000_TARGET_VERSION >= 76
4 static void process_path_internal(const char * p_path,const service_ptr_t<file> & p_reader,playlist_loader_callback::ptr callback, abort_callback & abort,playlist_loader_callback::t_entry_type type,const t_filestats & p_stats);
5 
6 namespace {
7  class archive_callback_impl : public archive_callback {
8  public:
9  archive_callback_impl(playlist_loader_callback::ptr p_callback, abort_callback & p_abort) : m_callback(p_callback), m_abort(p_abort) {}
10  bool on_entry(archive * owner,const char * p_path,const t_filestats & p_stats,const service_ptr_t<file> & p_reader)
11  {
12  process_path_internal(p_path,p_reader,m_callback,m_abort,playlist_loader_callback::entry_directory_enumerated,p_stats);
13  return !m_abort.is_aborting();
14  }
15  bool is_aborting() const {return m_abort.is_aborting();}
16  abort_callback_event get_abort_event() const {return m_abort.get_abort_event();}
17  private:
18  const playlist_loader_callback::ptr m_callback;
19  abort_callback & m_abort;
20  };
21 }
22 
23 bool playlist_loader::g_try_load_playlist(file::ptr fileHint,const char * p_path,playlist_loader_callback::ptr p_callback, abort_callback & p_abort) {
24  pfc::string8 filepath;
25 
26  filesystem::g_get_canonical_path(p_path,filepath);
27 
28  pfc::string_extension extension(filepath);
29 
30  service_ptr_t<file> l_file = fileHint;
31 
32  if (l_file.is_empty()) {
33  filesystem::ptr fs;
34  if (filesystem::g_get_interface(fs,filepath)) {
35  if (fs->supports_content_types()) {
36  fs->open(l_file,filepath,filesystem::open_mode_read,p_abort);
37  }
38  }
39  }
40 
41  {
43 
44  if (l_file.is_valid()) {
45  pfc::string8 content_type;
46  if (l_file->get_content_type(content_type)) {
48  e.reset(); while(e.next(l)) {
49  if (l->is_our_content_type(content_type)) {
50  try {
51  TRACK_CODE("playlist_loader::open",l->open(filepath,l_file,p_callback, p_abort));
52  return true;
53  } catch(exception_io_unsupported_format) {
54  l_file->reopen(p_abort);
55  }
56  }
57  }
58  }
59  }
60 
61  if (extension.length()>0) {
62  playlist_loader::ptr l;
63  e.reset(); while(e.next(l)) {
64  if (stricmp_utf8(l->get_extension(),extension) == 0) {
65  if (l_file.is_empty()) filesystem::g_open_read(l_file,filepath,p_abort);
66  try {
67  TRACK_CODE("playlist_loader::open",l->open(filepath,l_file,p_callback,p_abort));
68  return true;
69  } catch(exception_io_unsupported_format) {
70  l_file->reopen(p_abort);
71  }
72  }
73  }
74  }
75  }
76 
77  return false;
78 }
79 
80 void playlist_loader::g_load_playlist_filehint(file::ptr fileHint,const char * p_path,playlist_loader_callback::ptr p_callback, abort_callback & p_abort) {
81  if (!g_try_load_playlist(fileHint, p_path, p_callback, p_abort)) throw exception_io_unsupported_format();
82 }
83 
84 void playlist_loader::g_load_playlist(const char * p_path,playlist_loader_callback::ptr callback, abort_callback & abort) {
85  g_load_playlist_filehint(NULL,p_path,callback,abort);
86 }
87 
88 static void index_tracks_helper(const char * p_path,const service_ptr_t<file> & p_reader,const t_filestats & p_stats,playlist_loader_callback::t_entry_type p_type,playlist_loader_callback::ptr p_callback, abort_callback & p_abort,bool & p_got_input)
89 {
90  TRACK_CALL_TEXT("index_tracks_helper");
91  if (p_reader.is_empty() && filesystem::g_is_remote_safe(p_path))
92  {
93  TRACK_CALL_TEXT("remote");
94  metadb_handle_ptr handle;
95  p_callback->handle_create(handle,make_playable_location(p_path,0));
96  p_got_input = true;
97  p_callback->on_entry(handle,p_type,p_stats,true);
98  } else {
99  TRACK_CALL_TEXT("hintable");
101  input_entry::g_open_for_info_read(instance,p_reader,p_path,p_abort);
102 
103  t_filestats stats = instance->get_file_stats(p_abort);
104 
105  t_uint32 subsong,subsong_count = instance->get_subsong_count();
106  bool bInfoGetError = false;
107  for(subsong=0;subsong<subsong_count;subsong++)
108  {
109  TRACK_CALL_TEXT("subsong-loop");
110  p_abort.check();
111  metadb_handle_ptr handle;
112  t_uint32 index = instance->get_subsong(subsong);
113  p_callback->handle_create(handle,make_playable_location(p_path,index));
114 
115  p_got_input = true;
116  if (! bInfoGetError && p_callback->want_info(handle,p_type,stats,true) )
117  {
119  try {
120  TRACK_CODE("get_info",instance->get_info(index,info,p_abort));
121  } catch(...) {
122  bInfoGetError = true;
123  }
124  p_callback->on_entry_info(handle,p_type,stats,info,true);
125  }
126  else
127  {
128  p_callback->on_entry(handle,p_type,stats,true);
129  }
130  }
131  }
132 }
133 
134 static void track_indexer__g_get_tracks_wrap(const char * p_path,const service_ptr_t<file> & p_reader,const t_filestats & p_stats,playlist_loader_callback::t_entry_type p_type,playlist_loader_callback::ptr p_callback, abort_callback & p_abort) {
135  bool got_input = false;
136  bool fail = false;
137  try {
138  index_tracks_helper(p_path,p_reader,p_stats,p_type,p_callback,p_abort, got_input);
139  } catch(exception_aborted) {
140  throw;
141  } catch(exception_io_unsupported_format) {
142  fail = true;
143  } catch(std::exception const & e) {
144  fail = true;
145  console::formatter() << "could not enumerate tracks (" << e << ") on:\n" << file_path_display(p_path);
146  }
147  if (fail) {
148  if (!got_input && !p_abort.is_aborting()) {
149  if (p_type == playlist_loader_callback::entry_user_requested)
150  {
151  metadb_handle_ptr handle;
152  p_callback->handle_create(handle,make_playable_location(p_path,0));
153  p_callback->on_entry(handle,p_type,p_stats,true);
154  }
155  }
156  }
157 }
158 
159 namespace {
160  // SPECIAL HACK
161  // filesystem service does not present file hidden attrib but we want to weed files/folders out
162  // so check separately on all native paths (inefficient but meh)
163  class directory_callback_impl2 : public directory_callback_impl
164  {
165  public:
166  bool on_entry(filesystem * owner,abort_callback & p_abort,const char * url,bool is_subdirectory,const t_filestats & p_stats) {
167  p_abort.check();
168 
169  if (!m_addHidden) {
170  const char * n = url;
171  if (_extract_native_path_ptr(n)) {
172  DWORD att = uGetFileAttributes(n);
173  if (att == ~0 || (att & FILE_ATTRIBUTE_HIDDEN) != 0) return true;
174  }
175  }
176 
177  return directory_callback_impl::on_entry(owner, p_abort, url, is_subdirectory, p_stats);
178  }
179 
180  directory_callback_impl2(bool p_recur) : directory_callback_impl(p_recur), m_addHidden(queryAddHidden()) {}
181  private:
182  static bool queryAddHidden() {
183  // {2F9F4956-363F-4045-9531-603B1BF39BA8}
184  static const GUID guid_cfg_addhidden =
185  { 0x2f9f4956, 0x363f, 0x4045, { 0x95, 0x31, 0x60, 0x3b, 0x1b, 0xf3, 0x9b, 0xa8 } };
186 
187  advconfig_entry_checkbox::ptr ptr;
188  if (advconfig_entry::g_find_t(ptr, guid_cfg_addhidden)) {
189  return ptr->get_state();
190  }
191  return false;
192  }
193  const bool m_addHidden;
194  };
195 }
196 
197 
198 static void process_path_internal(const char * p_path,const service_ptr_t<file> & p_reader,playlist_loader_callback::ptr callback, abort_callback & abort,playlist_loader_callback::t_entry_type type,const t_filestats & p_stats)
199 {
200  //p_path must be canonical
201 
202  abort.check();
203 
204  callback->on_progress(p_path);
205 
206 
207  {
208  if (p_reader.is_empty() && type != playlist_loader_callback::entry_directory_enumerated) {
209  directory_callback_impl2 directory_results(true);
210  try {
211  filesystem::g_list_directory(p_path,directory_results,abort);
212  for(t_size n=0;n<directory_results.get_count();n++) {
213  process_path_internal(directory_results.get_item(n),0,callback,abort,playlist_loader_callback::entry_directory_enumerated,directory_results.get_item_stats(n));
214  }
215  return;
216  } catch(exception_aborted) {throw;}
217  catch(...) {
218  //do nothing, fall thru
219  //fixme - catch only filesystem exceptions?
220  }
221  }
222 
223  bool found = false;
224 
225 
226  {
227  archive_callback_impl archive_results(callback, abort);
230  while(e.next(f)) {
231  abort.check();
233  if (f->service_query_t(arch)) {
234  if (p_reader.is_valid()) p_reader->reopen(abort);
235 
236  try {
237  TRACK_CODE("archive::archive_list",arch->archive_list(p_path,p_reader,archive_results,true));
238  return;
239  } catch(exception_aborted) {throw;}
240  catch(...) {}
241  }
242  }
243  }
244  }
245 
246 
247 
248  {
250  if (link_resolver::g_find(ptr,p_path))
251  {
252  if (p_reader.is_valid()) p_reader->reopen(abort);
253 
254  pfc::string8 temp;
255  try {
256  TRACK_CODE("link_resolver::resolve",ptr->resolve(p_reader,p_path,temp,abort));
257 
258  track_indexer__g_get_tracks_wrap(temp,0,filestats_invalid,playlist_loader_callback::entry_from_playlist,callback, abort);
259  return;//success
260  } catch(exception_aborted) {throw;}
261  catch(...) {}
262  }
263  }
264 
265  if (callback->is_path_wanted(p_path,type)) {
266  track_indexer__g_get_tracks_wrap(p_path,p_reader,p_stats,type,callback, abort);
267  }
268 }
269 
270 void playlist_loader::g_process_path(const char * p_filename,playlist_loader_callback::ptr callback, abort_callback & abort,playlist_loader_callback::t_entry_type type)
271 {
272  TRACK_CALL_TEXT("playlist_loader::g_process_path");
273 
274  file_path_canonical filename(p_filename);
275 
276  process_path_internal(filename,0,callback,abort, type,filestats_invalid);
277 }
278 
280 {
281  TRACK_CALL_TEXT("playlist_loader::g_save_playlist");
282  pfc::string8 filename;
283  filesystem::g_get_canonical_path(p_filename,filename);
284  try {
286  filesystem::g_open(r,filename,filesystem::open_mode_write_new,p_abort);
287 
288  pfc::string_extension ext(filename);
289 
292  if (e.first(l)) do {
293  if (l->can_write() && !stricmp_utf8(ext,l->get_extension())) {
294  try {
295  TRACK_CODE("playlist_loader::write",l->write(filename,r,data,p_abort));
296  return;
297  } catch(exception_io_data) {}
298  }
299  } while(e.next(l));
300  throw exception_io_data();
301  } catch(...) {
302  try {filesystem::g_remove(filename,p_abort);} catch(...) {}
303  throw;
304  }
305 }
306 
307 
308 bool playlist_loader::g_process_path_ex(const char * filename,playlist_loader_callback::ptr callback, abort_callback & abort,playlist_loader_callback::t_entry_type type)
309 {
310  if (g_try_load_playlist(NULL, filename, callback, abort)) return true;
311  //not a playlist format
312  g_process_path(filename,callback,abort,type);
313  return false;
314 }
315 
316 #endif
void reset()
Definition: service.h:578
DWORD SHARED_EXPORT uGetFileAttributes(const char *fn)
Definition: pfc.h:71
bool is_empty() const
Definition: service.h:120
pfc::eventHandle_t abort_callback_event
Definition: abort_callback.h:8
void info(const char *p_message)
Definition: console.cpp:4
bool first(service_ptr_t< t_query > &p_out)
Definition: service.h:581
bool _extract_native_path_ptr(const char *&p_fspath)
Definition: filesystem.cpp:942
int SHARED_EXPORT stricmp_utf8(const char *p1, const char *p2)
static bool g_process_path_ex(const char *p_path, playlist_loader_callback::ptr p_callback, abort_callback &p_abort, playlist_loader_callback::t_entry_type p_type=playlist_loader_callback::entry_user_requested)
Calls attempts to process specified path as a playlist; if that fails (i.e. not a playlist)...
Usage: console::formatter() << "blah " << somenumber << " asdf" << somestring;.
Definition: console.h:17
static void fail(completion_notify_ptr p_notify)
static const t_filestats filestats_invalid
Invalid/unknown file stats constant. See: t_filestats.
Definition: filesystem.h:80
bool is_valid() const
Definition: service.h:119
static void g_open_for_info_read(service_ptr_t< input_info_reader > &p_instance, service_ptr_t< file > p_filehint, const char *p_path, abort_callback &p_abort, bool p_from_redirect=false)
Definition: input.cpp:175
static void index_tracks_helper(const char *p_path, const service_ptr_t< file > &p_reader, const t_filestats &p_stats, playlist_loader_callback::t_entry_type p_type, playlist_loader_callback::ptr p_callback, abort_callback &p_abort, bool &p_got_input)
static bool g_find_t(outptr &out, const GUID &id)
Definition: advconfig.h:18
size_t t_size
Definition: int_types.h:48
static void g_process_path(const char *p_path, playlist_loader_callback::ptr p_callback, abort_callback &p_abort, playlist_loader_callback::t_entry_type p_type=playlist_loader_callback::entry_user_requested)
Processes specified path to generate list of playable items. Includes recursive directory/archive enu...
static void process_path_internal(const char *p_path, const service_ptr_t< file > &p_reader, playlist_loader_callback::ptr callback, abort_callback &abort, playlist_loader_callback::t_entry_type type, const t_filestats &p_stats)
static void g_save_playlist(const char *p_path, metadb_handle_list_cref p_data, abort_callback &p_abort)
Saves specified list of locations into a playlist file. Throws exception_io or derivatives on failure...
std::exception exception
Definition: primitives.h:193
static void g_load_playlist_filehint(file::ptr fileHint, const char *p_path, playlist_loader_callback::ptr p_callback, abort_callback &p_abort)
Attempts to load a playlist file from specified filesystem path. Throws exception_io or derivatives o...
static void track_indexer__g_get_tracks_wrap(const char *p_path, const service_ptr_t< file > &p_reader, const t_filestats &p_stats, playlist_loader_callback::t_entry_type p_type, playlist_loader_callback::ptr p_callback, abort_callback &p_abort)
Implements file_info.
bool next(service_ptr_t< t_query > &p_out)
Definition: service.h:587
static void g_load_playlist(const char *p_path, playlist_loader_callback::ptr p_callback, abort_callback &p_abort)
Attempts to load a playlist file from specified filesystem path. Throws exception_io or derivatives o...
static bool g_try_load_playlist(file::ptr fileHint, const char *p_path, playlist_loader_callback::ptr p_callback, abort_callback &p_abort)
Attempts to load a playlist file from specified filesystem path. Throws exception_io or derivatives o...
t_size length() const
Definition: string_base.h:516
uint32_t t_uint32
Definition: int_types.h:5