M3ULoader.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. #include <stdio.h>
  2. #include <shlwapi.h>
  3. #include <strsafe.h>
  4. #include <fstream>
  5. #include <string>
  6. #include "M3ULoader.h"
  7. #include "../nu/ns_wc.h"
  8. #include "../WAT/WAT.h"
  9. M3ULoader::M3ULoader() : _utf8( false )
  10. {
  11. wideTitle[ 0 ] = wideFilename[ 0 ] = 0;
  12. }
  13. M3ULoader::~M3ULoader( void )
  14. {
  15. //Close();
  16. }
  17. struct cmpWchar_t {
  18. bool operator()(const wchar_t* a, const wchar_t* b) const {
  19. return wcscmp(a, b) < 0;
  20. }
  21. };
  22. class M3UInfo : public ifc_plentryinfo
  23. {
  24. public:
  25. M3UInfo() {}
  26. M3UInfo( wchar_t *_mediahash, wchar_t *_metahash, wchar_t *_cloud_id, wchar_t *_cloud_status, wchar_t *_cloud_devices )
  27. {
  28. _extended_infos.emplace( _wcsdup( _INFO_NAME_MEDIA_HASH ), _wcsdup( _mediahash) );
  29. _extended_infos.emplace( _wcsdup( _INFO_NAME_META_HASH ), _wcsdup( _metahash ) );
  30. _extended_infos.emplace( _wcsdup( _INFO_NAME_CLOUD_ID ), _wcsdup( _cloud_id ) );
  31. _extended_infos.emplace( _wcsdup( _INFO_NAME_CLOUD_STATUS ), _wcsdup( _cloud_status ) );
  32. _extended_infos.emplace( _wcsdup( _INFO_NAME_CLOUD_DEVICES ), _wcsdup( _cloud_devices ) );
  33. }
  34. ~M3UInfo()
  35. {
  36. for ( auto l_extended_infos_iterator = _extended_infos.begin(); l_extended_infos_iterator != _extended_infos.end(); ++l_extended_infos_iterator )
  37. {
  38. free( ( *l_extended_infos_iterator ).first );
  39. free( ( *l_extended_infos_iterator ).second );
  40. }
  41. _extended_infos.clear();
  42. }
  43. void SetExtendedInfo( const wchar_t *p_parameter_name, const wchar_t *p_parameter_value )
  44. {
  45. _extended_infos.emplace( _wcsdup( p_parameter_name ), _wcsdup( p_parameter_value ) );
  46. }
  47. const wchar_t *GetExtendedInfo( wchar_t *parameter )
  48. {
  49. //for ( auto l_extended_infos_iterator = _extended_infos.begin(); l_extended_infos_iterator != _extended_infos.end(); ++l_extended_infos_iterator )
  50. //{
  51. // wchar_t *l_key = _wcsdup( ( *l_extended_infos_iterator ).first );
  52. // if ( wcscmp( l_key, parameter ) == 0 )
  53. // return _wcsdup( ( *l_extended_infos_iterator ).second );
  54. //}
  55. // OLD
  56. //std::map<wchar_t *, wchar_t *>::iterator l_extended_infos_iterator = _extended_infos.find( parameter );
  57. //if ( l_extended_infos_iterator != _extended_infos.end() )
  58. // return _wcsdup( ( *l_extended_infos_iterator ).second );
  59. auto it = _extended_infos.find(parameter);
  60. if (_extended_infos.end() != it)
  61. {
  62. return it->second;
  63. }
  64. return 0;
  65. }
  66. private:
  67. RECVS_DISPATCH;
  68. std::map<wchar_t *, wchar_t *, cmpWchar_t> _extended_infos;
  69. };
  70. #define CBCLASS M3UInfo
  71. START_DISPATCH;
  72. CB( IFC_PLENTRYINFO_GETEXTENDEDINFO, GetExtendedInfo )
  73. END_DISPATCH;
  74. #undef CBCLASS
  75. int M3ULoader::OnFileHelper( ifc_playlistloadercallback *playlist, const wchar_t *trackName, const wchar_t *title, int length, const wchar_t *rootPath, ifc_plentryinfo *extraInfo )
  76. {
  77. if ( length == -1000 )
  78. length = -1;
  79. wcsncpy( wideFilename, trackName, FILENAME_SIZE );
  80. int ret;
  81. if ( wcsstr( wideFilename, L"://" ) || PathIsRootW( wideFilename ) )
  82. {
  83. ret = playlist->OnFile( wideFilename, title, length, extraInfo );
  84. }
  85. else
  86. {
  87. wchar_t fullPath[ MAX_PATH ] = { 0 };
  88. if ( PathCombineW( fullPath, rootPath, wideFilename ) )
  89. {
  90. wchar_t canonicalizedPath[ MAX_PATH ] = { 0 };
  91. PathCanonicalizeW( canonicalizedPath, fullPath );
  92. ret = playlist->OnFile( canonicalizedPath, title, length, extraInfo );
  93. }
  94. else
  95. {
  96. ret = ifc_playlistloadercallback::LOAD_CONTINUE;
  97. }
  98. }
  99. return ret;
  100. }
  101. static bool StringEnds( const wchar_t *a, const wchar_t *b )
  102. {
  103. size_t aLen = wcslen( a );
  104. size_t bLen = wcslen( b );
  105. if ( aLen < bLen )
  106. return false; // too short
  107. if ( !_wcsicmp( a + aLen - bLen, b ) )
  108. return true;
  109. return false;
  110. }
  111. int M3ULoader::Load( const wchar_t *p_filename, ifc_playlistloadercallback *playlist )
  112. {
  113. // TODO: download temp file if it's a URL
  114. // TODO - WDP2-198
  115. FILE *fp = _wfopen( p_filename, L"rt,ccs=UNICODE" );
  116. if ( !fp )
  117. return IFC_PLAYLISTLOADER_FAILED;
  118. fseek( fp, 0, SEEK_END );
  119. int size = ftell( fp );
  120. fseek( fp, 0, SEEK_SET );
  121. if ( size == -1 )
  122. {
  123. fclose( fp );
  124. fp = 0;
  125. return IFC_PLAYLISTLOADER_FAILED;
  126. }
  127. if ( StringEnds( p_filename, L".m3u8" ) )
  128. _utf8 = true;
  129. int ext = 0;
  130. wchar_t *p;
  131. const int l_linebuf_size = 2048;
  132. wchar_t linebuf[ l_linebuf_size ] = { 0 };
  133. wchar_t ext_title[ MAX_PATH ] = { 0 };
  134. wchar_t ext_mediahash[ 128 ] = { 0 };
  135. wchar_t ext_metahash[ 128 ] = { 0 };
  136. wchar_t ext_cloud_id[ 128 ] = { 0 };
  137. wchar_t ext_cloud_status[ 16 ] = { 0 };
  138. wchar_t ext_cloud_devices[ 128 ] = { 0 };
  139. int ext_len = -1;
  140. wchar_t rootPath[ MAX_PATH ] = { 0 };
  141. const wchar_t *callbackPath = playlist->GetBasePath();
  142. if ( callbackPath )
  143. StringCchCopyW( rootPath, MAX_PATH, callbackPath );
  144. else
  145. {
  146. StringCchCopyW( rootPath, MAX_PATH, p_filename );
  147. PathRemoveFileSpecW( rootPath );
  148. }
  149. unsigned char BOM[ 3 ] = { 0, 0, 0 };
  150. if ( fread( BOM, 3, 1, fp ) == 1 && BOM[ 0 ] == 0xEF && BOM[ 1 ] == 0xBB && BOM[ 2 ] == 0xBF )
  151. _utf8 = true;
  152. else
  153. fseek( fp, 0, SEEK_SET );
  154. std::wstring l_separator = L"\" ";
  155. std::wstring l_key_separator = L"=";
  156. const wchar_t _ASF[] = L"ASF ";
  157. const wchar_t _DIRECTIVE_EXTINF[] = L"#EXTINF:";
  158. const wchar_t _DIRECTIVE_EXTM3U[] = L"#EXTM3U";
  159. const wchar_t _DIRECTIVE_EXT_X_NS_CLOUD[] = L"#EXT-X-NS-CLOUD:";
  160. const wchar_t _DIRECTIVE_UTF8[] = L"#UTF8";
  161. const wchar_t _END_LINE[] = L"\r\n";
  162. const int l_move_size = sizeof( wchar_t );
  163. wa::strings::wa_string l_key_value_pair = "";
  164. wa::strings::wa_string l_key = "";
  165. wa::strings::wa_string l_value = "";
  166. std::map<std::wstring, std::wstring> l_extended_infos;
  167. while ( 1 )
  168. {
  169. if ( feof( fp ) )
  170. break;
  171. linebuf[ 0 ] = 0;
  172. fgetws( linebuf, l_linebuf_size - 1, fp );
  173. linebuf[ wcscspn( linebuf, _END_LINE ) ] = 0;
  174. if ( wcslen( linebuf ) == 0 )
  175. continue;
  176. if ( ext == 0 && wcsstr( linebuf, _DIRECTIVE_EXTM3U ) )
  177. {
  178. ext = 1;
  179. continue;
  180. }
  181. if ( !wcsncmp( linebuf, _DIRECTIVE_UTF8, 5 ) )
  182. {
  183. _utf8 = true;
  184. continue;
  185. }
  186. p = linebuf;
  187. while ( p && *p == ' ' || *p == '\t' )
  188. p = CharNextW( p );
  189. if ( *p != '#' && *p != '\n' && *p != '\r' && *p )
  190. {
  191. wchar_t buf[ 4096 ] = { 0 };
  192. wchar_t *p2 = CharPrevW( linebuf, linebuf + wcslen( linebuf ) ); //GetLastCharacter(linebuf);
  193. if ( p2 && *p2 == '\n' )
  194. *p2 = 0;
  195. if ( !wcsncmp( p, _ASF, 4 ) && wcslen( p ) > 4 )
  196. p += 4;
  197. if ( wcsncmp( p, L"\\\\", 2 ) && wcsncmp( p + 1, L":\\", 2 ) && wcsncmp( p + 1, L":/", 2 ) && !wcsstr( p, L"://" ) )
  198. {
  199. if ( p[ 0 ] == '\\' )
  200. {
  201. buf[ 0 ] = rootPath[ 0 ];
  202. buf[ 1 ] = rootPath[ 1 ];
  203. StringCchCopyW( buf + 2, 4093, p );
  204. //buf[ wcslen( buf ) - 1 ] = 0;
  205. buf[ wcscspn( buf, _END_LINE ) ] = 0;
  206. p = buf;
  207. }
  208. }
  209. int ret;
  210. // generate extra info from the cloud specific values (if present)
  211. M3UInfo info( ext_mediahash, ext_metahash, ext_cloud_id, ext_cloud_status, ext_cloud_devices );
  212. if ( !l_extended_infos.empty() )
  213. {
  214. for ( auto l_extended_infos_iterator = l_extended_infos.begin(); l_extended_infos_iterator != l_extended_infos.end(); ++l_extended_infos_iterator )
  215. {
  216. info.SetExtendedInfo( ( *l_extended_infos_iterator ).first.c_str(), ( *l_extended_infos_iterator ).second.c_str() );
  217. }
  218. }
  219. l_extended_infos.clear();
  220. if ( ext_title[ 0 ] )
  221. {
  222. wcsncpy( wideTitle, ext_title, FILETITLE_SIZE );
  223. ret = OnFileHelper( playlist, p, wideTitle, ext_len * 1000, rootPath, &info );
  224. }
  225. else
  226. {
  227. ret = OnFileHelper( playlist, p, 0, -1, rootPath, &info );
  228. }
  229. if ( ret != ifc_playlistloadercallback::LOAD_CONTINUE )
  230. break;
  231. ext_len = -1;
  232. ext_title[ 0 ] = 0;
  233. }
  234. else
  235. {
  236. if ( ext && !wcsncmp( p, _DIRECTIVE_EXTINF, 8 ) )
  237. {
  238. p += 8;
  239. ext_len = _wtoi( p );
  240. int l_track_length = ext_len;
  241. int l_digits = ( l_track_length < 0 ? 1 : 0 );
  242. while ( l_track_length )
  243. {
  244. l_track_length /= 10;
  245. ++l_digits;
  246. }
  247. p += l_digits;
  248. if ( p && *p )
  249. {
  250. wchar_t *p2 = CharPrevW( p, p + wcslen( p ) ); // GetLastCharacter(p);
  251. if ( p2 && *p2 == '\n' )
  252. *p2 = 0;
  253. while ( p && *p == ' ' )
  254. p = CharNextW( p );
  255. std::wstring l_string( p );
  256. int l_pos = l_string.find_first_of( L"," );
  257. if ( l_pos > 0 )
  258. {
  259. int l_key_separator_pos = 0;
  260. wa::strings::wa_string l_line_trail( l_string.substr( 0, l_pos ) );
  261. while ( !l_line_trail.empty() )
  262. {
  263. int l_separator_pos = l_line_trail.find( l_separator );
  264. if ( l_separator_pos > 0 )
  265. l_key_value_pair = l_line_trail.mid( 0, l_separator_pos + 1 );
  266. else
  267. l_key_value_pair = l_line_trail;
  268. l_key_separator_pos = l_key_value_pair.find( l_key_separator );
  269. l_key = l_key_value_pair.mid( 0, l_key_separator_pos );
  270. l_value = l_key_value_pair.mid( l_key_separator_pos + 1, l_key_value_pair.lengthS() - l_key_separator_pos + 1 );
  271. l_value.replaceAll( "\"", "" );
  272. l_extended_infos.emplace( l_key.GetW(), l_value.GetW() );
  273. if ( l_separator_pos > 0 )
  274. l_line_trail = l_line_trail.mid( l_separator_pos + l_move_size, l_line_trail.lengthS() - l_separator_pos + 1 );
  275. else
  276. l_line_trail.clear();
  277. }
  278. l_string = l_string.substr( l_pos + 1, l_string.size() - l_pos );
  279. StringCchCopyW( ext_title, MAX_PATH, l_string.c_str() );
  280. }
  281. else
  282. StringCchCopyW( ext_title, MAX_PATH, CharNextW( p ) );
  283. }
  284. else
  285. {
  286. ext_len = -1;
  287. ext_title[ 0 ] = 0;
  288. }
  289. }
  290. // cloud specific playlist line for holding information about the entry
  291. else if ( ext && !wcsncmp( p, _DIRECTIVE_EXT_X_NS_CLOUD, 16 ) )
  292. {
  293. p += 16;
  294. wchar_t *pt = wcstok( p, L"," );
  295. while ( pt != NULL )
  296. {
  297. int end = (int)wcscspn( pt, L"=" );
  298. if ( !wcsncmp( pt, _INFO_NAME_MEDIA_HASH, end ) )
  299. {
  300. if ( ( lstrcpynW( ext_mediahash, pt + end + 1, 128 ) ) == NULL )
  301. return IFC_PLAYLISTLOADER_FAILED;
  302. }
  303. else if ( !wcsncmp( pt, _INFO_NAME_META_HASH, end ) )
  304. {
  305. if ( ( lstrcpynW( ext_metahash, pt + end + 1, 128 ) ) == NULL )
  306. return IFC_PLAYLISTLOADER_FAILED;
  307. }
  308. else if ( !wcsncmp( pt, _INFO_NAME_CLOUD_ID, end ) )
  309. {
  310. if ( ( lstrcpynW( ext_cloud_id, pt + end + 1, 128 ) ) == NULL )
  311. return IFC_PLAYLISTLOADER_FAILED;
  312. }
  313. else if ( !wcsncmp( pt, _INFO_NAME_CLOUD_STATUS, end ) )
  314. {
  315. if ( ( lstrcpynW( ext_cloud_status, pt + end + 1, 16 ) ) == NULL )
  316. return IFC_PLAYLISTLOADER_FAILED;
  317. }
  318. else if ( !wcsncmp( pt, _INFO_NAME_CLOUD_DEVICES, end ) )
  319. {
  320. wchar_t *p2 = pt + end + 1;
  321. while ( p2 && *p2 != '\n' )
  322. p2 = CharNextW( p2 );
  323. if ( p2 && *p2 == '\n' )
  324. *p2 = 0;
  325. if ( ( lstrcpynW( ext_cloud_devices, pt + end + 1, 128 ) ) == NULL )
  326. return IFC_PLAYLISTLOADER_FAILED;
  327. }
  328. pt = wcstok( NULL, L"," );
  329. }
  330. }
  331. else
  332. {
  333. ext_len = -1;
  334. ext_title[ 0 ] = 0;
  335. ext_mediahash[ 0 ] = 0;
  336. ext_metahash[ 0 ] = 0;
  337. ext_cloud_id[ 0 ] = 0;
  338. ext_cloud_status[ 0 ] = 0;
  339. ext_cloud_devices[ 0 ] = 0;
  340. }
  341. }
  342. }
  343. if ( fp )
  344. fclose( fp );
  345. return IFC_PLAYLISTLOADER_SUCCESS;
  346. }
  347. #ifdef CBCLASS
  348. #undef CBCLASS
  349. #endif
  350. #define CBCLASS M3ULoader
  351. START_DISPATCH;
  352. CB( IFC_PLAYLISTLOADER_LOAD, Load )
  353. #if 0
  354. VCB( IFC_PLAYLISTLOADER_CLOSE, Close )
  355. CB( IFC_PLAYLISTLOADER_GETITEM, GetItem )
  356. CB( IFC_PLAYLISTLOADER_GETITEMTITLE, GetItemTitle )
  357. CB( IFC_PLAYLISTLOADER_GETITEMLENGTHMILLISECONDS, GetItemLengthMilliseconds )
  358. CB( IFC_PLAYLISTLOADER_GETITEMEXTENDEDINFO, GetItemExtendedInfo )
  359. CB( IFC_PLAYLISTLOADER_NEXTITEM, NextItem )
  360. #endif
  361. END_DISPATCH;