summaryrefslogtreecommitdiff
path: root/game/client/youtubeapi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/youtubeapi.cpp')
-rw-r--r--game/client/youtubeapi.cpp931
1 files changed, 931 insertions, 0 deletions
diff --git a/game/client/youtubeapi.cpp b/game/client/youtubeapi.cpp
new file mode 100644
index 0000000..c104221
--- /dev/null
+++ b/game/client/youtubeapi.cpp
@@ -0,0 +1,931 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#if defined( WIN32 ) && !defined( _X360 )
+#include "winlite.h"
+#include <WinInet.h>
+#endif
+
+#include "youtubeapi.h"
+
+#include "platform.h"
+#include "convar.h"
+#include "fmtstr.h"
+#include "igamesystem.h"
+#include "strtools.h"
+#include "cdll_util.h"
+#include "utlmap.h"
+#include "utlstring.h"
+#include "vstdlib/jobthread.h"
+#include <steam/steam_api.h>
+#include <steam/isteamhttp.h>
+#include "cdll_client_int.h"
+#include "utlbuffer.h"
+#include "filesystem.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//=============================================================================
+
+static ConVar youtube_http_proxy( "youtube_http_proxy", "", FCVAR_ARCHIVE, "HTTP proxy. Specify if you have have problems uploading to YouTube." );
+
+//=============================================================================
+
+static bool GetStandardTagValue( const char *pTag, const char *pXML, CUtlString &strResult )
+{
+ const char *pStart = strstr( pXML, pTag );
+ if ( pStart != NULL )
+ {
+ pStart = strstr( pStart, ">" );
+ if ( pStart != NULL )
+ {
+ pStart += 1;
+ const char *pEnd = strstr( pStart, CFmtStr1024( "</%s", pTag ) );
+ if ( pEnd != NULL )
+ {
+ strResult.SetDirect( pStart, pEnd - pStart );
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+//=============================================================================
+
+class CYouTubeRetrieveInfoJob;
+
+namespace
+{
+ class CYouTubeSystem : public CAutoGameSystemPerFrame
+ {
+ public:
+
+ CYouTubeSystem();
+ ~CYouTubeSystem();
+
+ // CAutoGameSystem
+ virtual bool Init();
+ virtual void PostInit();
+ virtual void Shutdown();
+ virtual bool IsPerFrame() { return true; }
+ virtual void Update( float frametime );
+
+ void Login( const char *pUserName, const char *pPassword, const char *pSource );
+ void LoginCancel();
+ YouTubeUploadHandle_t Upload( const char* pFilePath, const char *pMimeType, const char *pTitle, const char *pDescription, const char *pCategory, const char *pKeywords, eYouTubeAccessControl access );
+
+ bool IsUploadFinished( YouTubeUploadHandle_t handle );
+ bool GetUploadProgress( YouTubeUploadHandle_t handle, double &ultotal, double &ulnow );
+ bool GetUploadResults( YouTubeUploadHandle_t handle, bool &bSuccess, CUtlString &strURLToVideo, CUtlString &strURLToVideoStats );
+ void ClearUploadResults( YouTubeUploadHandle_t handle );
+ void CancelUpload( YouTubeUploadHandle_t handle );
+ void SetUploadFinished( YouTubeUploadHandle_t handle, bool bSuccess, const char *pURLToVideo, const char *pURLToVideoStats );
+
+ void SetUserProfile( const char *pUserProfile );
+ bool GetProfileURL( CUtlString &strProfileURL ) const;
+
+ YouTubeInfoHandle_t GetInfo( const char *pURLToVideoStats, CYouTubeResponseHandler &responseHandler );
+ void CancelGetInfo( YouTubeInfoHandle_t handle );
+
+ const char *GetDeveloperKey() const;
+ const char *GetDeveloperTag() const;
+ void SetDeveloperSettings( const char *pDeveloperKey, const char *pDeveloperTag );
+
+ const char *GetLoginName() const;
+ eYouTubeLoginStatus GetLoginStatus() const;
+ void SetLoginStatus( eYouTubeLoginStatus status );
+
+ const char* GetAuthToken() const;
+ void SetAuthToken( const char *pAuthToken );
+
+ private:
+ struct uploadstatus_t
+ {
+ bool bFinished;
+ bool bSuccess;
+ CUtlString strURLToVideo;
+ CUtlString strURLToVideoStats;
+ };
+
+ uploadstatus_t *GetStatus( YouTubeUploadHandle_t handle );
+
+ eYouTubeLoginStatus m_eLoginStatus;
+ CUtlString m_strYouTubeUserName;
+ CUtlString m_strDeveloperKey;
+ CUtlString m_strDeveloperTag;
+ CUtlString m_strAuthToken;
+ CUtlString m_strUserProfile;
+ CThreadMutex m_Mutex;
+ IThreadPool* m_pThreadPool;
+ CUtlMap< YouTubeUploadHandle_t, uploadstatus_t > m_mapUploads;
+ CUtlVector< CYouTubeRetrieveInfoJob * > m_vecRetrieveInfoJobs;
+ };
+};
+
+static CYouTubeSystem gYouTube;
+
+static ISteamHTTP *GetISteamHTTP()
+{
+ if ( steamapicontext != NULL && steamapicontext->SteamHTTP() )
+ {
+ return steamapicontext->SteamHTTP();
+ }
+#ifndef CLIENT_DLL
+ if ( steamgameserverapicontext != NULL )
+ {
+ return steamgameserverapicontext->SteamHTTP();
+ }
+#endif
+ return NULL;
+}
+
+//=============================================================================
+
+// Base class for all YouTube jobs
+class CYouTubeJob : public CJob
+{
+public:
+ CYouTubeJob( CYouTubeSystem *pSystem )
+ {
+ SetFlags( JF_IO );
+ // store local ones so we don't have to go through a mutex
+ m_strDeveloperKey = pSystem->GetDeveloperKey();
+ m_strDeveloperTag = pSystem->GetDeveloperTag();
+ }
+
+ virtual ~CYouTubeJob()
+ {
+ }
+
+ void CancelUpload()
+ {
+ m_bCancelled = true;
+ }
+
+ bool IsCancelled() const
+ {
+ return m_bCancelled;
+ }
+
+
+protected:
+ void OnHTTPRequestCompleted( HTTPRequestCompleted_t *pParam, bool bIOFailure )
+ {
+ m_bHTTPRequestPending = false;
+ if ( (!m_bAllowRequestFailure && pParam->m_eStatusCode != k_EHTTPStatusCode200OK) || bIOFailure || !pParam->m_bRequestSuccessful )
+ {
+ Warning( "Failed to get youtube url: HTTP status %d fetching %s\n", pParam->m_eStatusCode, m_sURL.String() );
+ }
+ else
+ {
+ OnHTTPRequestCompleted( pParam );
+ }
+ }
+
+ virtual void OnHTTPRequestCompleted( HTTPRequestCompleted_t *pParam ) { Assert( false ); }
+
+ void DoRequest( HTTPRequestHandle hRequest, const char *pchRequestURL )
+ {
+ m_sURL = pchRequestURL;
+
+ SteamAPICall_t hSteamAPICall;
+ if ( GetISteamHTTP()->SendHTTPRequest( hRequest, &hSteamAPICall ) )
+ {
+ m_HTTPRequestCompleted.Set( hSteamAPICall, this, &CYouTubeJob::OnHTTPRequestCompleted );
+ }
+
+ // Wait for it to finish.
+ while ( m_bHTTPRequestPending && !m_bCancelled )
+ {
+ ThreadSleep( 100 );
+ }
+
+ GetISteamHTTP()->ReleaseHTTPRequest( hSteamAPICall );
+ }
+
+
+ CCallResult<CYouTubeJob, HTTPRequestCompleted_t> m_HTTPRequestCompleted;
+
+ CUtlString m_strDeveloperKey;
+ CUtlString m_strDeveloperTag;
+ CUtlString m_sURL;
+ CUtlString m_strResponse;
+ bool m_bHTTPRequestPending = true;
+ bool m_bAllowRequestFailure = false;
+ bool m_bCancelled = false;
+};
+
+//=============================================================================
+
+class CYouTubeRetrieveUserProfile : public CYouTubeJob
+{
+public:
+ CYouTubeRetrieveUserProfile( CYouTubeSystem *pSystem )
+ : CYouTubeJob( pSystem )
+ {
+ }
+
+private:
+ void OnHTTPRequestCompleted( HTTPRequestCompleted_t *pParam ) OVERRIDE
+ {
+ uint32 unBodySize;
+ if ( !GetISteamHTTP()->GetHTTPResponseBodySize( pParam->m_hRequest, &unBodySize ) )
+ {
+ Assert( false );
+ }
+ else
+ {
+ m_strResponse.SetLength( unBodySize );
+
+ if ( GetISteamHTTP()->GetHTTPResponseBodyData( pParam->m_hRequest, (uint8*)m_strResponse.String(), unBodySize ) )
+ {
+ gYouTube.SetUserProfile( m_strResponse.Get() );
+ }
+ }
+ }
+
+ virtual JobStatus_t DoExecute()
+ {
+ HTTPRequestHandle hRequest = GetISteamHTTP()->CreateHTTPRequest( k_EHTTPMethodGET, "http://gdata.youtube.com/feeds/api/users/default" );
+ GetISteamHTTP()->SetHTTPRequestNetworkActivityTimeout( hRequest, 30 );
+ GetISteamHTTP()->SetHTTPRequestHeaderValue( hRequest, "Authorization", CFmtStr1024( "GoogleLogin auth=%s", gYouTube.GetAuthToken() ) );
+
+ DoRequest( hRequest, "http://gdata.youtube.com/feeds/api/users/default" );
+
+ return JOB_OK;
+ }
+};
+
+//=============================================================================
+
+class CYouTubeRetrieveInfoJob : public CYouTubeJob
+{
+public:
+ CYouTubeRetrieveInfoJob( CYouTubeSystem *pSystem, const char *pVideoURL, CYouTubeResponseHandler &responseHandler )
+ : CYouTubeJob( pSystem )
+ , m_strURL( pVideoURL )
+ , m_responseHandler( responseHandler )
+ {
+ }
+
+ void NotifyResponseHandler()
+ {
+ m_responseHandler.HandleResponse( 200, m_strResponse.Get() );
+ }
+
+private:
+ void OnHTTPRequestCompleted( HTTPRequestCompleted_t *pParam ) OVERRIDE
+ {
+ uint32 unBodySize;
+ if ( !GetISteamHTTP()->GetHTTPResponseBodySize( pParam->m_hRequest, &unBodySize ) )
+ {
+ Assert( false );
+ }
+ else
+ {
+ m_strResponse.SetLength( unBodySize );
+ GetISteamHTTP()->GetHTTPResponseBodyData( pParam->m_hRequest, (uint8*)m_strResponse.String(), unBodySize );
+ }
+ }
+
+ virtual JobStatus_t DoExecute()
+ {
+ HTTPRequestHandle hRequest = GetISteamHTTP()->CreateHTTPRequest( k_EHTTPMethodGET, m_strURL.Get() );
+ GetISteamHTTP()->SetHTTPRequestNetworkActivityTimeout( hRequest, 30 );
+
+ DoRequest( hRequest, m_strURL.Get() );
+
+ return JOB_OK;
+ }
+
+ // data
+ CUtlString m_strURL;
+ CYouTubeResponseHandler &m_responseHandler;
+};
+
+class CYouTubeLoginJob : public CYouTubeJob
+{
+public:
+ CYouTubeLoginJob( CYouTubeSystem *pSystem, const char *pUserName, const char *pPassword, const char *pSource )
+ : CYouTubeJob( pSystem )
+ , m_strUserName( pUserName )
+ , m_strPassword( pPassword )
+ , m_strSource( pSource )
+ {
+ }
+
+private:
+
+ void SetLoginResults( const char *pLoginResults )
+ {
+ const char *pStart = strstr( pLoginResults, "Auth=" );
+ if ( pStart != NULL )
+ {
+ pStart += strlen( "Auth=" );
+ const char *pEnd = strstr( pStart, "\r\n" );
+ if ( pEnd == NULL )
+ {
+ pEnd = strstr( pStart, "\n" );
+ }
+ CUtlString strAuthToken;
+ if ( pEnd != NULL )
+ {
+ strAuthToken.SetDirect( pStart, pEnd - pStart );
+ }
+ else
+ {
+ strAuthToken.SetDirect( pStart, strlen( pStart ) );
+ }
+ gYouTube.SetAuthToken( strAuthToken.Get() );
+ }
+ }
+
+ void OnHTTPRequestCompleted( HTTPRequestCompleted_t *pParam ) OVERRIDE
+ {
+ eYouTubeLoginStatus loginStatus = kYouTubeLogin_LoggedIn;
+
+ if ( pParam->m_eStatusCode == 403 )
+ {
+ loginStatus = kYouTubeLogin_Forbidden;
+ }
+ else if ( pParam->m_eStatusCode != 200 )
+ {
+ loginStatus = kYouTubeLogin_GenericFailure;
+ }
+ else
+ {
+ uint32 unBodySize;
+ if ( !GetISteamHTTP()->GetHTTPResponseBodySize( pParam->m_hRequest, &unBodySize ) )
+ {
+ Assert( false );
+ }
+ else
+ {
+ m_strResponse.SetLength( unBodySize );
+ if ( GetISteamHTTP()->GetHTTPResponseBodyData( pParam->m_hRequest, (uint8*)m_strResponse.String(), unBodySize ) )
+ {
+ SetLoginResults( m_strResponse );
+ }
+ }
+ }
+
+ gYouTube.SetLoginStatus( loginStatus );
+
+ }
+
+ virtual JobStatus_t DoExecute()
+ {
+ m_bAllowRequestFailure = true;
+
+ HTTPRequestHandle hRequest = GetISteamHTTP()->CreateHTTPRequest( k_EHTTPMethodPOST, "https://www.google.com/accounts/ClientLogin" );
+ GetISteamHTTP()->SetHTTPRequestNetworkActivityTimeout( hRequest, 30 );
+
+ // GetISteamHTTP()->SetHTTPRequestHeaderValue( hRequest, "Content-Type", "application/x-www-form-urlencoded" );
+ GetISteamHTTP()->SetHTTPRequestHeaderValue( hRequest, "X-GData-Key", CFmtStr1024( "key=%s", m_strDeveloperKey.Get() ) );
+
+ char szUserNameEncoded[256];
+ char szPasswordEncoded[256];
+ char szSourceEncoded[256];
+ Q_URLEncode( szUserNameEncoded, sizeof( szUserNameEncoded ), m_strUserName.Get(), m_strUserName.Length() );
+ Q_URLEncode( szPasswordEncoded, sizeof( szPasswordEncoded ), m_strPassword.Get(), m_strPassword.Length() );
+ Q_URLEncode( szSourceEncoded, sizeof( szSourceEncoded ), m_strSource.Get(), m_strSource.Length() );
+
+ CFmtStr1024 data( "Email=%s&Passwd=%s&service=youtube&source=%s", szUserNameEncoded, szPasswordEncoded, szSourceEncoded );
+
+ GetISteamHTTP()->SetHTTPRequestRawPostBody( hRequest, "application/x-www-form-urlencoded", (uint8 *)data.Access(), data.Length() );
+
+ DoRequest( hRequest, "https://www.google.com/accounts/ClientLogin" );
+
+ return JOB_OK;
+ }
+
+ // data
+ CUtlString m_strUserName;
+ CUtlString m_strPassword;
+ CUtlString m_strSource;
+};
+
+// Job for uploading a file
+class CYouTubeUploadJob : public CYouTubeJob
+{
+public:
+ CYouTubeUploadJob( CYouTubeSystem *pSystem, const char* pFilePath, const char *pMimeType, const char *pTitle, const char *pDescription, const char *pCategory, const char *pKeywords, eYouTubeAccessControl access )
+ : CYouTubeJob( pSystem )
+ , m_strFilePath( pFilePath )
+ , m_strMimeType( pMimeType )
+ , m_strTitle( pTitle )
+ , m_strDesc( pDescription )
+ , m_strCategory( pCategory )
+ , m_strKeywords( pKeywords )
+ , m_eAccess( access )
+ {
+ }
+
+ void GetProgress( double &ultotal, double &ulnow )
+ {
+ ultotal = 0;
+ ulnow = 0;
+ }
+
+private:
+
+ virtual JobStatus_t DoExecute()
+ {
+ m_bAllowRequestFailure = true;
+
+ HTTPRequestHandle hRequest = GetISteamHTTP()->CreateHTTPRequest( k_EHTTPMethodPUT, "http://uploads.gdata.youtube.com/feeds/api/users/default/uploads" );
+ GetISteamHTTP()->SetHTTPRequestNetworkActivityTimeout( hRequest, 30 );
+
+ const char *pFileName = Q_UnqualifiedFileName( m_strFilePath.Get() );
+
+ GetISteamHTTP()->SetHTTPRequestHeaderValue( hRequest, "Authorization", CFmtStr1024( "GoogleLogin auth=%s", gYouTube.GetAuthToken() ) );
+ GetISteamHTTP()->SetHTTPRequestHeaderValue( hRequest, "X-GData-Key", CFmtStr1024( "key=%s", m_strDeveloperKey.Get() ) );
+ GetISteamHTTP()->SetHTTPRequestHeaderValue( hRequest, "GData-Version", "2" );
+ //GetISteamHTTP()->SetHTTPRequestHeaderValue( hRequest, "Content-Type", "multipart/form-data;boundary=-x" );
+ GetISteamHTTP()->SetHTTPRequestHeaderValue( hRequest, "Slug", CFmtStr1024( "%s", pFileName ) );
+
+ const char *pPrivateString = "";
+ const char *pAccessControlString = "";
+
+ switch ( m_eAccess )
+ {
+ case kYouTubeAccessControl_Public:
+ break;
+ case kYouTubeAccessControl_Private:
+ pPrivateString = "<yt:private/>";
+ break;
+ case kYouTubeAccessControl_Unlisted:
+ pAccessControlString = "<yt:accessControl action=\"list\" permission=\"denied\"/>";
+ break;
+ }
+
+ CFmtStr1024 strAPIRequest( "<?xml version=\"1.0\"?>"
+ "<entry "
+ "xmlns=\"http://www.w3.org/2005/Atom\" "
+ "xmlns:media=\"http://search.yahoo.com/mrss/\" "
+ "xmlns:yt=\"http://gdata.youtube.com/schemas/2007\">"
+ "<media:group>"
+ "<media:title type=\"plain\">%s</media:title>"
+ "<media:description type=\"plain\">%s</media:description>"
+ "<media:category scheme=\"http://gdata.youtube.com/schemas/2007/categories.cat\">"
+ "%s"
+ "</media:category>"
+ "%s"
+ "<media:keywords>%s</media:keywords>"
+ "<media:category scheme=\"http://gdata.youtube.com/schemas/2007/developertags.cat\">"
+ "%s"
+ "</media:category>"
+ "</media:group>"
+ "%s"
+ "</entry>",
+ m_strTitle.Get(),
+ m_strDesc.Get(),
+ m_strCategory.Get(),
+ pPrivateString,
+ m_strKeywords.Get(),
+ m_strDeveloperTag.Get(),
+ pAccessControlString );
+
+ CFmtStr1024 fmtstrBody(
+ "\r\nContent-Type: application/atom+xml; charset=UTF-8\r\n"
+ "\r\n---x\r\nContent-Disposition: form-data; name=\"apirequest\"\r\n\r\n%s"
+ "\r\n---x\r\nContent-Disposition: form-data; name=\"video\"\r\nContent-Type: %s\r\nContent-Transfer-Encoding: binary\r\n\r\n",
+ strAPIRequest.Access(), m_strMimeType.Get()
+ );
+
+ CUtlBuffer postDataRaw( 0, 0, 0 );
+
+ postDataRaw.Put( fmtstrBody.Access(), fmtstrBody.Length() );
+
+ CUtlBuffer fileData( 0, 0, 0 );
+ bool bReadFileOK = g_pFullFileSystem->ReadFile( m_strFilePath, nullptr, fileData );
+
+ if ( bReadFileOK )
+ {
+ postDataRaw.Put( fileData.Base(), fileData.TellPut() );
+
+ fileData.Clear();
+
+ static const char rgchFooter[] = "\r\n---x--\r\n";
+
+ postDataRaw.Put( rgchFooter, V_strlen( rgchFooter ) );
+
+ GetISteamHTTP()->SetHTTPRequestRawPostBody( hRequest, "multipart/form-data;boundary=-x", (uint8 *)postDataRaw.Base(), postDataRaw.TellPut() );
+
+ // BUGBUG: use SendHTTPRequestAndStreamResponse
+ DoRequest( hRequest, "http://uploads.gdata.youtube.com/feeds/api/users/default/uploads" );
+ }
+
+ return JOB_OK;
+ }
+
+ void OnHTTPRequestCompleted( HTTPRequestCompleted_t *pParam ) OVERRIDE
+ {
+ bool bSuccess = false;
+
+ CUtlString strURLToVideoStats;
+ CUtlString strURLToVideo;
+
+ if ( pParam->m_eStatusCode == 200 )
+ {
+ bSuccess = true;
+
+ uint32 unBodySize;
+ if ( !GetISteamHTTP()->GetHTTPResponseBodySize( pParam->m_hRequest, &unBodySize ) )
+ {
+ Assert( false );
+ }
+ else
+ {
+ m_strResponse.SetLength( unBodySize );
+ if ( GetISteamHTTP()->GetHTTPResponseBodyData( pParam->m_hRequest, (uint8*)m_strResponse.String(), unBodySize ) )
+ {
+ // @note Tom Bui: wish I had an xml parser...
+ {
+ strURLToVideo = "";
+ // "<link rel='alternate' type='text/html' href='http://www.youtube.com/watch?v=7D4pb3irM_0&feature=youtube_gdata' /> ";
+ const char *kLinkStartTag = "<link rel='alternate' type='text/html' href='";
+ const char *kLinkTagEnd = "'/>";
+ const char *pStart = strstr( m_strResponse.Get(), kLinkStartTag );
+ if ( pStart != NULL )
+ {
+ pStart += strlen( kLinkStartTag );
+ const char *pEnd = strstr( pStart, kLinkTagEnd );
+ if ( pEnd != NULL )
+ {
+ strURLToVideo.SetDirect( pStart, pEnd - pStart );
+ }
+ }
+ }
+ {
+ strURLToVideoStats = "";
+ // "<link rel='self' type='text/html' href='http://www.youtube.com/watch?v=7D4pb3irM_0&feature=youtube_gdata' /> ";
+ const char *kLinkStartTag = "<link rel='self' type='application/atom+xml' href='";
+ const char *kLinkTagEnd = "'/>";
+ const char *pStart = strstr( m_strResponse.Get(), kLinkStartTag );
+ if ( pStart != NULL )
+ {
+ pStart += strlen( kLinkStartTag );
+ const char *pEnd = strstr( pStart, kLinkTagEnd );
+ if ( pEnd != NULL )
+ {
+ strURLToVideoStats.SetDirect( pStart, pEnd - pStart );
+ // @note Tom Bui: we want at least version 2
+ if ( V_strstr( strURLToVideoStats.Get(), "?v=" ) == NULL )
+ {
+ strURLToVideoStats += "?v=2";
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ gYouTube.SetUploadFinished( (YouTubeUploadHandle_t)this, bSuccess, strURLToVideo.Get(), strURLToVideoStats.Get() );
+ }
+
+ // data
+ CUtlString m_strFilePath;
+ CUtlString m_strMimeType;
+ CUtlString m_strTitle;
+ CUtlString m_strDesc;
+ CUtlString m_strCategory;
+ CUtlString m_strKeywords;
+ eYouTubeAccessControl m_eAccess;
+};
+
+//=============================================================================
+
+CYouTubeSystem::CYouTubeSystem()
+ : m_eLoginStatus( kYouTubeLogin_NotLoggedIn )
+ , m_pThreadPool( NULL )
+ , m_mapUploads( DefLessFunc( YouTubeUploadHandle_t ) )
+{
+}
+
+CYouTubeSystem::~CYouTubeSystem()
+{
+}
+
+bool CYouTubeSystem::Init()
+{
+ m_pThreadPool = CreateThreadPool();
+ m_pThreadPool->Start( ThreadPoolStartParams_t( false, 4 ), "YouTubeSystem" );
+ return true;
+}
+
+void CYouTubeSystem::PostInit()
+{
+}
+
+void CYouTubeSystem::Shutdown()
+{
+ DestroyThreadPool( m_pThreadPool );
+ m_pThreadPool = NULL;
+}
+
+void CYouTubeSystem::Update( float frametime )
+{
+ AUTO_LOCK( m_Mutex );
+ FOR_EACH_VEC( m_vecRetrieveInfoJobs, i )
+ {
+ CYouTubeRetrieveInfoJob *pJob = m_vecRetrieveInfoJobs[i];
+ if ( pJob->IsFinished() )
+ {
+ pJob->NotifyResponseHandler();
+ // cleanup the job
+ pJob->Release();
+ // and remove
+ m_vecRetrieveInfoJobs.FastRemove( i );
+ }
+ else
+ {
+ ++i;
+ }
+ }
+}
+
+void CYouTubeSystem::Login( const char *pUserName, const char *pPassword, const char *pSource )
+{
+ m_eLoginStatus = kYouTubeLogin_NotLoggedIn;
+ CYouTubeLoginJob *pJob = new CYouTubeLoginJob( this, pUserName, pPassword, pSource );
+ m_pThreadPool->AddJob( pJob );
+ pJob->Release();
+}
+
+void CYouTubeSystem::LoginCancel()
+{
+ m_eLoginStatus = kYouTubeLogin_Cancelled;
+}
+
+YouTubeUploadHandle_t CYouTubeSystem::Upload( const char* pFilePath, const char *pMimeType, const char *pTitle, const char *pDescription, const char *pCategory, const char *pKeywords, eYouTubeAccessControl access )
+{
+ if ( m_eLoginStatus != kYouTubeLogin_LoggedIn )
+ {
+ return NULL;
+ }
+ CYouTubeUploadJob *pJob = new CYouTubeUploadJob( this, pFilePath, pMimeType, pTitle, pDescription, pCategory, pKeywords, access );
+ m_pThreadPool->AddJob( pJob );
+ uploadstatus_t status = { false, false, "", "" };
+ m_mapUploads.Insert( (YouTubeUploadHandle_t)pJob, status );
+ return (YouTubeUploadHandle_t)pJob;
+}
+
+CYouTubeSystem::uploadstatus_t *CYouTubeSystem::GetStatus( YouTubeUploadHandle_t handle )
+{
+ int idx = m_mapUploads.Find( handle );
+ if ( m_mapUploads.IsValidIndex( idx ) )
+ {
+ return &m_mapUploads[idx];
+ }
+ return NULL;
+}
+
+bool CYouTubeSystem::IsUploadFinished( YouTubeUploadHandle_t handle )
+{
+ AUTO_LOCK( m_Mutex );
+ uploadstatus_t *pStatus = GetStatus( handle );
+ if ( pStatus != NULL )
+ {
+ return pStatus->bFinished;
+ }
+ return true;
+}
+
+bool CYouTubeSystem::GetUploadProgress( YouTubeUploadHandle_t handle, double &ultotal, double &ulnow )
+{
+ AUTO_LOCK( m_Mutex );
+ uploadstatus_t *pStatus = GetStatus( handle );
+ if ( pStatus != NULL )
+ {
+ CYouTubeUploadJob *pJob = (CYouTubeUploadJob*)handle;
+ pJob->GetProgress( ultotal, ulnow );
+ return true;
+ }
+ return false;
+}
+
+bool CYouTubeSystem::GetUploadResults( YouTubeUploadHandle_t handle, bool &bSuccess, CUtlString &strURLToVideo, CUtlString &strURLToVideoStats )
+{
+ AUTO_LOCK( m_Mutex );
+ uploadstatus_t *pStatus = GetStatus( handle );
+ if ( pStatus != NULL )
+ {
+ bSuccess = pStatus->bSuccess;
+ strURLToVideo = pStatus->strURLToVideo;
+ strURLToVideoStats = pStatus->strURLToVideoStats;
+ return true;
+ }
+ return false;
+}
+
+void CYouTubeSystem::ClearUploadResults( YouTubeUploadHandle_t handle )
+{
+ AUTO_LOCK( m_Mutex );
+ if ( m_mapUploads.Remove( handle ) )
+ {
+ CYouTubeUploadJob *pJob = (CYouTubeUploadJob*)handle;
+ pJob->Release();
+ }
+}
+
+void CYouTubeSystem::CancelUpload( YouTubeUploadHandle_t handle )
+{
+ AUTO_LOCK( m_Mutex );
+ if ( m_mapUploads.Remove( handle ) )
+ {
+ CYouTubeUploadJob *pJob = (CYouTubeUploadJob*)handle;
+ pJob->CancelUpload();
+ pJob->Release();
+ }
+}
+
+void CYouTubeSystem::SetUploadFinished( YouTubeUploadHandle_t handle, bool bSuccess, const char *pURLToVideo, const char *pURLToVideoStats )
+{
+ AUTO_LOCK( m_Mutex );
+ uploadstatus_t *pStatus = GetStatus( handle );
+ if ( pStatus )
+ {
+ pStatus->bFinished = true;
+ pStatus->bSuccess = bSuccess;
+ pStatus->strURLToVideo = pURLToVideo;
+ pStatus->strURLToVideoStats = pURLToVideoStats;
+ }
+}
+
+void CYouTubeSystem::SetUserProfile( const char *pUserProfile )
+{
+ AUTO_LOCK( m_Mutex );
+ m_strUserProfile = pUserProfile;
+ CUtlString author;
+ GetStandardTagValue( "author", m_strUserProfile.Get(), author );
+ GetStandardTagValue( "name", author.Get(), m_strYouTubeUserName );
+}
+
+YouTubeInfoHandle_t CYouTubeSystem::GetInfo( const char *pURLToVideoStats, CYouTubeResponseHandler &responseHandler )
+{
+ AUTO_LOCK( m_Mutex );
+ CYouTubeRetrieveInfoJob *pJob = new CYouTubeRetrieveInfoJob( this, pURLToVideoStats, responseHandler );
+ m_pThreadPool->AddJob( pJob );
+ m_vecRetrieveInfoJobs.AddToTail( pJob );
+ return (YouTubeInfoHandle_t)pJob;
+}
+
+void CYouTubeSystem::CancelGetInfo( YouTubeInfoHandle_t handle )
+{
+ AUTO_LOCK( m_Mutex );
+ int idx = m_vecRetrieveInfoJobs.Find( (CYouTubeRetrieveInfoJob*)handle );
+ if ( idx >= 0 && idx < m_vecRetrieveInfoJobs.Count() )
+ {
+ ((CYouTubeRetrieveInfoJob*)handle)->Release();
+ m_vecRetrieveInfoJobs.FastRemove( idx );
+ }
+}
+
+void CYouTubeSystem::SetDeveloperSettings( const char *pDeveloperKey, const char *pDeveloperTag )
+{
+ AUTO_LOCK( m_Mutex );
+ m_strDeveloperKey = pDeveloperKey;
+ m_strDeveloperTag = pDeveloperTag;
+}
+
+const char *CYouTubeSystem::GetDeveloperKey() const
+{
+ AUTO_LOCK( m_Mutex );
+ return m_strDeveloperKey.Get();
+}
+
+const char *CYouTubeSystem::GetDeveloperTag() const
+{
+ AUTO_LOCK( m_Mutex );
+ return m_strDeveloperTag.Get();
+}
+
+const char *CYouTubeSystem::GetLoginName() const
+{
+ return m_strYouTubeUserName.Get();
+}
+
+eYouTubeLoginStatus CYouTubeSystem::GetLoginStatus() const
+{
+ return m_eLoginStatus;
+}
+
+bool CYouTubeSystem::GetProfileURL( CUtlString &strProfileURL ) const
+{
+ if ( m_eLoginStatus == kYouTubeLogin_LoggedIn )
+ {
+ strProfileURL = CFmtStr1024( "http://www.youtube.com/profile?user=%s", m_strYouTubeUserName.Get() );
+ return true;
+ }
+ return false;
+}
+
+void CYouTubeSystem::SetLoginStatus( eYouTubeLoginStatus status )
+{
+ AUTO_LOCK( m_Mutex );
+ m_eLoginStatus = status;
+ if ( m_eLoginStatus == kYouTubeLogin_LoggedIn )
+ {
+ CYouTubeRetrieveUserProfile *pJob = new CYouTubeRetrieveUserProfile( this );
+ m_pThreadPool->AddJob( pJob );
+ pJob->Release();
+ }
+}
+
+void CYouTubeSystem::SetAuthToken( const char *pAuthToken )
+{
+ AUTO_LOCK( m_Mutex );
+ m_strAuthToken = pAuthToken;
+}
+
+const char* CYouTubeSystem::GetAuthToken() const
+{
+ AUTO_LOCK( m_Mutex );
+ return m_strAuthToken.Get();
+}
+
+//=============================================================================
+// Public API
+
+void YouTube_SetDeveloperSettings( const char *pDeveloperKey, const char *pDeveloperTag )
+{
+ gYouTube.SetDeveloperSettings( pDeveloperKey, pDeveloperTag );
+}
+
+void YouTube_Login( const char *pUserName, const char *pPassword, const char *pSource )
+{
+ if ( gYouTube.GetLoginStatus() == kYouTubeLogin_LoggedIn )
+ {
+ return;
+ }
+ gYouTube.Login( pUserName, pPassword, pSource );
+}
+
+void YouTube_LoginCancel()
+{
+ gYouTube.LoginCancel();
+}
+
+const char *YouTube_GetLoginName()
+{
+ return gYouTube.GetLoginName();
+}
+
+eYouTubeLoginStatus YouTube_GetLoginStatus()
+{
+ return gYouTube.GetLoginStatus();
+}
+
+bool YouTube_GetProfileURL( CUtlString &strProfileURL )
+{
+ return gYouTube.GetProfileURL( strProfileURL );
+}
+
+YouTubeUploadHandle_t YouTube_Upload( const char* pFilePath, const char *pMimeType, const char *pTitle, const char *pDescription, const char *pCategory, const char *pKeywords, eYouTubeAccessControl access )
+{
+ return gYouTube.Upload( pFilePath, pMimeType, pTitle, pDescription, pCategory, pKeywords, access );
+}
+
+bool YouTube_IsUploadFinished( YouTubeUploadHandle_t handle )
+{
+ return gYouTube.IsUploadFinished( handle );
+}
+
+bool YouTube_GetUploadProgress( YouTubeUploadHandle_t handle, double &ultotal, double &ulnow )
+{
+ return gYouTube.GetUploadProgress( handle, ultotal, ulnow );
+}
+
+bool YouTube_GetUploadResults( YouTubeUploadHandle_t handle, bool &bSuccess, CUtlString &strURLToVideo, CUtlString &strURLToVideoStats )
+{
+ return gYouTube.GetUploadResults( handle, bSuccess, strURLToVideo, strURLToVideoStats );
+}
+
+void YouTube_ClearUploadResults( YouTubeUploadHandle_t handle )
+{
+ gYouTube.ClearUploadResults( handle );
+}
+
+void YouTube_CancelUpload( YouTubeUploadHandle_t handle )
+{
+ gYouTube.CancelUpload( handle );
+}
+
+YouTubeInfoHandle_t YouTube_GetVideoInfo( const char *pURLToVideoStats, CYouTubeResponseHandler &responseHandler )
+{
+ return gYouTube.GetInfo( pURLToVideoStats, responseHandler );
+}
+
+void YouTube_CancelGetVideoInfo( YouTubeInfoHandle_t handle )
+{
+ gYouTube.CancelGetInfo( handle );
+}