You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
931 lines
26 KiB
931 lines
26 KiB
//========= 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 ); |
|
}
|
|
|