aboutsummaryrefslogtreecommitdiff
path: root/Src/playlist/M3ULoader.cpp
diff options
context:
space:
mode:
authorJean-Francois Mauguit <jfmauguit@mac.com>2024-09-24 09:03:25 -0400
committerGitHub <noreply@github.com>2024-09-24 09:03:25 -0400
commitbab614c421ed7ae329d26bf028c4a3b1d2450f5a (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/playlist/M3ULoader.cpp
parent4bde6044fddf053f31795b9eaccdd2a5a527d21f (diff)
parent20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (diff)
downloadwinamp-bab614c421ed7ae329d26bf028c4a3b1d2450f5a.tar.gz
Merge pull request #5 from WinampDesktop/community
Merge to main
Diffstat (limited to 'Src/playlist/M3ULoader.cpp')
-rw-r--r--Src/playlist/M3ULoader.cpp466
1 files changed, 466 insertions, 0 deletions
diff --git a/Src/playlist/M3ULoader.cpp b/Src/playlist/M3ULoader.cpp
new file mode 100644
index 00000000..ebab35da
--- /dev/null
+++ b/Src/playlist/M3ULoader.cpp
@@ -0,0 +1,466 @@
+#include <stdio.h>
+#include <shlwapi.h>
+#include <strsafe.h>
+#include <fstream>
+#include <string>
+
+#include "M3ULoader.h"
+#include "../nu/ns_wc.h"
+
+#include "../WAT/WAT.h"
+
+
+M3ULoader::M3ULoader() : _utf8( false )
+{
+ wideTitle[ 0 ] = wideFilename[ 0 ] = 0;
+}
+
+M3ULoader::~M3ULoader( void )
+{
+ //Close();
+}
+
+struct cmpWchar_t {
+ bool operator()(const wchar_t* a, const wchar_t* b) const {
+ return wcscmp(a, b) < 0;
+ }
+};
+
+class M3UInfo : public ifc_plentryinfo
+{
+public:
+ M3UInfo() {}
+ M3UInfo( wchar_t *_mediahash, wchar_t *_metahash, wchar_t *_cloud_id, wchar_t *_cloud_status, wchar_t *_cloud_devices )
+ {
+ _extended_infos.emplace( _wcsdup( _INFO_NAME_MEDIA_HASH ), _wcsdup( _mediahash) );
+ _extended_infos.emplace( _wcsdup( _INFO_NAME_META_HASH ), _wcsdup( _metahash ) );
+
+ _extended_infos.emplace( _wcsdup( _INFO_NAME_CLOUD_ID ), _wcsdup( _cloud_id ) );
+ _extended_infos.emplace( _wcsdup( _INFO_NAME_CLOUD_STATUS ), _wcsdup( _cloud_status ) );
+ _extended_infos.emplace( _wcsdup( _INFO_NAME_CLOUD_DEVICES ), _wcsdup( _cloud_devices ) );
+ }
+
+ ~M3UInfo()
+ {
+ for ( auto l_extended_infos_iterator = _extended_infos.begin(); l_extended_infos_iterator != _extended_infos.end(); ++l_extended_infos_iterator )
+ {
+ free( ( *l_extended_infos_iterator ).first );
+ free( ( *l_extended_infos_iterator ).second );
+ }
+
+ _extended_infos.clear();
+ }
+
+ void SetExtendedInfo( const wchar_t *p_parameter_name, const wchar_t *p_parameter_value )
+ {
+ _extended_infos.emplace( _wcsdup( p_parameter_name ), _wcsdup( p_parameter_value ) );
+ }
+
+ const wchar_t *GetExtendedInfo( wchar_t *parameter )
+ {
+ //for ( auto l_extended_infos_iterator = _extended_infos.begin(); l_extended_infos_iterator != _extended_infos.end(); ++l_extended_infos_iterator )
+ //{
+ // wchar_t *l_key = _wcsdup( ( *l_extended_infos_iterator ).first );
+ // if ( wcscmp( l_key, parameter ) == 0 )
+ // return _wcsdup( ( *l_extended_infos_iterator ).second );
+ //}
+
+ // OLD
+ //std::map<wchar_t *, wchar_t *>::iterator l_extended_infos_iterator = _extended_infos.find( parameter );
+
+ //if ( l_extended_infos_iterator != _extended_infos.end() )
+ // return _wcsdup( ( *l_extended_infos_iterator ).second );
+
+ auto it = _extended_infos.find(parameter);
+ if (_extended_infos.end() != it)
+ {
+ return it->second;
+ }
+
+ return 0;
+ }
+
+private:
+ RECVS_DISPATCH;
+
+ std::map<wchar_t *, wchar_t *, cmpWchar_t> _extended_infos;
+};
+
+#define CBCLASS M3UInfo
+START_DISPATCH;
+CB( IFC_PLENTRYINFO_GETEXTENDEDINFO, GetExtendedInfo )
+END_DISPATCH;
+#undef CBCLASS
+
+
+int M3ULoader::OnFileHelper( ifc_playlistloadercallback *playlist, const wchar_t *trackName, const wchar_t *title, int length, const wchar_t *rootPath, ifc_plentryinfo *extraInfo )
+{
+ if ( length == -1000 )
+ length = -1;
+
+ wcsncpy( wideFilename, trackName, FILENAME_SIZE );
+
+ int ret;
+
+ if ( wcsstr( wideFilename, L"://" ) || PathIsRootW( wideFilename ) )
+ {
+ ret = playlist->OnFile( wideFilename, title, length, extraInfo );
+ }
+ else
+ {
+ wchar_t fullPath[ MAX_PATH ] = { 0 };
+ if ( PathCombineW( fullPath, rootPath, wideFilename ) )
+ {
+ wchar_t canonicalizedPath[ MAX_PATH ] = { 0 };
+ PathCanonicalizeW( canonicalizedPath, fullPath );
+ ret = playlist->OnFile( canonicalizedPath, title, length, extraInfo );
+ }
+ else
+ {
+ ret = ifc_playlistloadercallback::LOAD_CONTINUE;
+ }
+ }
+
+ return ret;
+}
+
+static bool StringEnds( const wchar_t *a, const wchar_t *b )
+{
+ size_t aLen = wcslen( a );
+ size_t bLen = wcslen( b );
+
+ if ( aLen < bLen )
+ return false; // too short
+
+ if ( !_wcsicmp( a + aLen - bLen, b ) )
+ return true;
+
+ return false;
+}
+
+int M3ULoader::Load( const wchar_t *p_filename, ifc_playlistloadercallback *playlist )
+{
+ // TODO: download temp file if it's a URL
+ // TODO - WDP2-198
+
+ FILE *fp = _wfopen( p_filename, L"rt,ccs=UNICODE" );
+ if ( !fp )
+ return IFC_PLAYLISTLOADER_FAILED;
+
+ fseek( fp, 0, SEEK_END );
+ int size = ftell( fp );
+ fseek( fp, 0, SEEK_SET );
+
+ if ( size == -1 )
+ {
+ fclose( fp );
+ fp = 0;
+
+ return IFC_PLAYLISTLOADER_FAILED;
+ }
+
+ if ( StringEnds( p_filename, L".m3u8" ) )
+ _utf8 = true;
+
+ int ext = 0;
+
+ wchar_t *p;
+
+ const int l_linebuf_size = 2048;
+ wchar_t linebuf[ l_linebuf_size ] = { 0 };
+
+ wchar_t ext_title[ MAX_PATH ] = { 0 };
+
+ wchar_t ext_mediahash[ 128 ] = { 0 };
+ wchar_t ext_metahash[ 128 ] = { 0 };
+
+ wchar_t ext_cloud_id[ 128 ] = { 0 };
+ wchar_t ext_cloud_status[ 16 ] = { 0 };
+ wchar_t ext_cloud_devices[ 128 ] = { 0 };
+
+ int ext_len = -1;
+
+ wchar_t rootPath[ MAX_PATH ] = { 0 };
+ const wchar_t *callbackPath = playlist->GetBasePath();
+ if ( callbackPath )
+ StringCchCopyW( rootPath, MAX_PATH, callbackPath );
+ else
+ {
+ StringCchCopyW( rootPath, MAX_PATH, p_filename );
+ PathRemoveFileSpecW( rootPath );
+ }
+
+ unsigned char BOM[ 3 ] = { 0, 0, 0 };
+ if ( fread( BOM, 3, 1, fp ) == 1 && BOM[ 0 ] == 0xEF && BOM[ 1 ] == 0xBB && BOM[ 2 ] == 0xBF )
+ _utf8 = true;
+ else
+ fseek( fp, 0, SEEK_SET );
+
+
+ std::wstring l_separator = L"\" ";
+ std::wstring l_key_separator = L"=";
+
+
+ const wchar_t _ASF[] = L"ASF ";
+ const wchar_t _DIRECTIVE_EXTINF[] = L"#EXTINF:";
+ const wchar_t _DIRECTIVE_EXTM3U[] = L"#EXTM3U";
+ const wchar_t _DIRECTIVE_EXT_X_NS_CLOUD[] = L"#EXT-X-NS-CLOUD:";
+ const wchar_t _DIRECTIVE_UTF8[] = L"#UTF8";
+ const wchar_t _END_LINE[] = L"\r\n";
+
+ const int l_move_size = sizeof( wchar_t );
+
+
+ wa::strings::wa_string l_key_value_pair = "";
+ wa::strings::wa_string l_key = "";
+ wa::strings::wa_string l_value = "";
+
+ std::map<std::wstring, std::wstring> l_extended_infos;
+
+ while ( 1 )
+ {
+ if ( feof( fp ) )
+ break;
+
+ linebuf[ 0 ] = 0;
+ fgetws( linebuf, l_linebuf_size - 1, fp );
+
+ linebuf[ wcscspn( linebuf, _END_LINE ) ] = 0;
+ if ( wcslen( linebuf ) == 0 )
+ continue;
+
+ if ( ext == 0 && wcsstr( linebuf, _DIRECTIVE_EXTM3U ) )
+ {
+ ext = 1;
+
+ continue;
+ }
+
+ if ( !wcsncmp( linebuf, _DIRECTIVE_UTF8, 5 ) )
+ {
+ _utf8 = true;
+
+ continue;
+ }
+
+ p = linebuf;
+
+ while ( p && *p == ' ' || *p == '\t' )
+ p = CharNextW( p );
+
+ if ( *p != '#' && *p != '\n' && *p != '\r' && *p )
+ {
+ wchar_t buf[ 4096 ] = { 0 };
+
+ wchar_t *p2 = CharPrevW( linebuf, linebuf + wcslen( linebuf ) ); //GetLastCharacter(linebuf);
+ if ( p2 && *p2 == '\n' )
+ *p2 = 0;
+
+ if ( !wcsncmp( p, _ASF, 4 ) && wcslen( p ) > 4 )
+ p += 4;
+
+ if ( wcsncmp( p, L"\\\\", 2 ) && wcsncmp( p + 1, L":\\", 2 ) && wcsncmp( p + 1, L":/", 2 ) && !wcsstr( p, L"://" ) )
+ {
+ if ( p[ 0 ] == '\\' )
+ {
+ buf[ 0 ] = rootPath[ 0 ];
+ buf[ 1 ] = rootPath[ 1 ];
+
+ StringCchCopyW( buf + 2, 4093, p );
+
+ //buf[ wcslen( buf ) - 1 ] = 0;
+ buf[ wcscspn( buf, _END_LINE ) ] = 0;
+ p = buf;
+ }
+ }
+
+ int ret;
+
+ // generate extra info from the cloud specific values (if present)
+ M3UInfo info( ext_mediahash, ext_metahash, ext_cloud_id, ext_cloud_status, ext_cloud_devices );
+
+
+ if ( !l_extended_infos.empty() )
+ {
+ for ( auto l_extended_infos_iterator = l_extended_infos.begin(); l_extended_infos_iterator != l_extended_infos.end(); ++l_extended_infos_iterator )
+ {
+ info.SetExtendedInfo( ( *l_extended_infos_iterator ).first.c_str(), ( *l_extended_infos_iterator ).second.c_str() );
+ }
+ }
+
+ l_extended_infos.clear();
+
+ if ( ext_title[ 0 ] )
+ {
+ wcsncpy( wideTitle, ext_title, FILETITLE_SIZE );
+ ret = OnFileHelper( playlist, p, wideTitle, ext_len * 1000, rootPath, &info );
+ }
+ else
+ {
+ ret = OnFileHelper( playlist, p, 0, -1, rootPath, &info );
+ }
+
+ if ( ret != ifc_playlistloadercallback::LOAD_CONTINUE )
+ break;
+
+ ext_len = -1;
+ ext_title[ 0 ] = 0;
+ }
+ else
+ {
+ if ( ext && !wcsncmp( p, _DIRECTIVE_EXTINF, 8 ) )
+ {
+ p += 8;
+ ext_len = _wtoi( p );
+
+ int l_track_length = ext_len;
+ int l_digits = ( l_track_length < 0 ? 1 : 0 );
+ while ( l_track_length )
+ {
+ l_track_length /= 10;
+ ++l_digits;
+ }
+
+ p += l_digits;
+
+
+ if ( p && *p )
+ {
+ wchar_t *p2 = CharPrevW( p, p + wcslen( p ) ); // GetLastCharacter(p);
+ if ( p2 && *p2 == '\n' )
+ *p2 = 0;
+
+ while ( p && *p == ' ' )
+ p = CharNextW( p );
+
+ std::wstring l_string( p );
+
+ int l_pos = l_string.find_first_of( L"," );
+
+ if ( l_pos > 0 )
+ {
+ int l_key_separator_pos = 0;
+
+ wa::strings::wa_string l_line_trail( l_string.substr( 0, l_pos ) );
+
+ while ( !l_line_trail.empty() )
+ {
+ int l_separator_pos = l_line_trail.find( l_separator );
+
+ if ( l_separator_pos > 0 )
+ l_key_value_pair = l_line_trail.mid( 0, l_separator_pos + 1 );
+ else
+ l_key_value_pair = l_line_trail;
+
+
+ l_key_separator_pos = l_key_value_pair.find( l_key_separator );
+
+ l_key = l_key_value_pair.mid( 0, l_key_separator_pos );
+ l_value = l_key_value_pair.mid( l_key_separator_pos + 1, l_key_value_pair.lengthS() - l_key_separator_pos + 1 );
+
+ l_value.replaceAll( "\"", "" );
+
+ l_extended_infos.emplace( l_key.GetW(), l_value.GetW() );
+
+ if ( l_separator_pos > 0 )
+ l_line_trail = l_line_trail.mid( l_separator_pos + l_move_size, l_line_trail.lengthS() - l_separator_pos + 1 );
+ else
+ l_line_trail.clear();
+ }
+
+
+ l_string = l_string.substr( l_pos + 1, l_string.size() - l_pos );
+
+ StringCchCopyW( ext_title, MAX_PATH, l_string.c_str() );
+ }
+ else
+ StringCchCopyW( ext_title, MAX_PATH, CharNextW( p ) );
+ }
+ else
+ {
+ ext_len = -1;
+ ext_title[ 0 ] = 0;
+ }
+ }
+ // cloud specific playlist line for holding information about the entry
+ else if ( ext && !wcsncmp( p, _DIRECTIVE_EXT_X_NS_CLOUD, 16 ) )
+ {
+ p += 16;
+ wchar_t *pt = wcstok( p, L"," );
+ while ( pt != NULL )
+ {
+ int end = (int)wcscspn( pt, L"=" );
+
+ if ( !wcsncmp( pt, _INFO_NAME_MEDIA_HASH, end ) )
+ {
+ if ( ( lstrcpynW( ext_mediahash, pt + end + 1, 128 ) ) == NULL )
+ return IFC_PLAYLISTLOADER_FAILED;
+ }
+ else if ( !wcsncmp( pt, _INFO_NAME_META_HASH, end ) )
+ {
+ if ( ( lstrcpynW( ext_metahash, pt + end + 1, 128 ) ) == NULL )
+ return IFC_PLAYLISTLOADER_FAILED;
+ }
+ else if ( !wcsncmp( pt, _INFO_NAME_CLOUD_ID, end ) )
+ {
+ if ( ( lstrcpynW( ext_cloud_id, pt + end + 1, 128 ) ) == NULL )
+ return IFC_PLAYLISTLOADER_FAILED;
+ }
+ else if ( !wcsncmp( pt, _INFO_NAME_CLOUD_STATUS, end ) )
+ {
+ if ( ( lstrcpynW( ext_cloud_status, pt + end + 1, 16 ) ) == NULL )
+ return IFC_PLAYLISTLOADER_FAILED;
+ }
+ else if ( !wcsncmp( pt, _INFO_NAME_CLOUD_DEVICES, end ) )
+ {
+ wchar_t *p2 = pt + end + 1;
+ while ( p2 && *p2 != '\n' )
+ p2 = CharNextW( p2 );
+
+ if ( p2 && *p2 == '\n' )
+ *p2 = 0;
+
+ if ( ( lstrcpynW( ext_cloud_devices, pt + end + 1, 128 ) ) == NULL )
+ return IFC_PLAYLISTLOADER_FAILED;
+ }
+
+ pt = wcstok( NULL, L"," );
+ }
+ }
+ else
+ {
+ ext_len = -1;
+ ext_title[ 0 ] = 0;
+ ext_mediahash[ 0 ] = 0;
+ ext_metahash[ 0 ] = 0;
+ ext_cloud_id[ 0 ] = 0;
+ ext_cloud_status[ 0 ] = 0;
+ ext_cloud_devices[ 0 ] = 0;
+ }
+ }
+ }
+
+ if ( fp )
+ fclose( fp );
+
+ return IFC_PLAYLISTLOADER_SUCCESS;
+}
+
+
+#ifdef CBCLASS
+#undef CBCLASS
+#endif
+
+#define CBCLASS M3ULoader
+
+START_DISPATCH;
+CB( IFC_PLAYLISTLOADER_LOAD, Load )
+#if 0
+VCB( IFC_PLAYLISTLOADER_CLOSE, Close )
+CB( IFC_PLAYLISTLOADER_GETITEM, GetItem )
+CB( IFC_PLAYLISTLOADER_GETITEMTITLE, GetItemTitle )
+CB( IFC_PLAYLISTLOADER_GETITEMLENGTHMILLISECONDS, GetItemLengthMilliseconds )
+CB( IFC_PLAYLISTLOADER_GETITEMEXTENDEDINFO, GetItemExtendedInfo )
+CB( IFC_PLAYLISTLOADER_NEXTITEM, NextItem )
+#endif
+END_DISPATCH; \ No newline at end of file