2016年6月15日 星期三

[C#] Upload/Update/Check/Delete/List Video with YouTube API

找了很多關於C#上使用YouTube API的資料後,拼拼湊湊出簡易的YouTube Video處理的程式

以下不說明如何匯入YouTube API Lib與其使用申請,網路上很多,可自行參考。

基本的建立UserCredential,並宣告YouTubeService,每個動作都是需要此步驟

UserCredential credential;
using( var stream = new FileStream(System.IO.Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "client_secrets.json"), FileMode.Open, FileAccess.Read) )
{
    credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
        GoogleClientSecrets.Load(stream).Secrets,
        new[] { YouTubeService.Scope.Youtube, YouTubeService.Scope.YoutubeUpload },
        "user",
        CancellationToken.None,
        new FileDataStore(System.IO.Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "YoutubeCredential"))
    );
}
youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
    HttpClientInitializer = credential,
    ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
});
     

Upload
上傳單一影片檔案
var video = new Video();
video.Snippet = new VideoSnippet();
video.Snippet.Title = videoName;
video.Snippet.Description = videoName;
video.Snippet.Tags = new string[] { "LowRes", processingTag };
video.Snippet.CategoryId = conf.configMapper.ey_sec.ey_youtube_category;//"22"; See https://developers.google.com/youtube/v3/docs/videoCategories/list
video.Status = new VideoStatus();
video.Status.PrivacyStatus = conf.configMapper.ey_sec.ey_youtube_privacy; //"unlisted" or "private" or "public"
var filePath = target; // Replace with path to actual movie file.
try
{
    using( var fileStream = new FileStream(filePath, FileMode.Open) )
    {
        var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet,status", fileStream, "video/*");
        const int KB = 0x400;
        var minimumChunkSize = 256 * KB;
        videosInsertRequest.ProgressChanged += videosInsertRequest_ProgressChanged;
        videosInsertRequest.ResponseReceived += videosInsertRequest_ResponseReceived;
        videosInsertRequest.ChunkSize = minimumChunkSize * 32;
        await videosInsertRequest.UploadAsync();      
    }
}
catch(Exception ex)
{
    logtext.Out("[YouTube Exception] " + ex.Message + ex.StackTrace);
}

Check&Update
檢查VideoID是否有效並檢查是否為Duplicate video,並更新Tag資訊
var my_video_request = youtubeService.Videos.List("snippet, status");
my_video_request.Id = oldVideoID; // the Youtube video id of the video you want to update
my_video_request.MaxResults = 1;
var my_video_response = await my_video_request.ExecuteAsync();
if( my_video_response.Items.Count == 0 )
{
    return;
}
var video = my_video_response.Items[0];
video.Snippet.Tags.Add(oldVersionTag);
if( video.Status.UploadStatus == "rejected" )
{
    logtext.Out("[YouTube Info] Video upload status : " + video.Status.UploadStatus);
    logtext.Out("[YouTube Info] Video upload reject reason : " + video.Status.RejectionReason);
}
// and tell the changes we want to youtube
var my_update_request = youtubeService.Videos.Update(video, "snippet, status");
my_update_request.Execute();


Delete
刪除特定VideoID之影片
try
{
    var my_video_request = youtubeService.Videos.List("snippet, status");
    my_video_request.Id = oldVideoID; // the Youtube video id of the video you want to update
    my_video_request.MaxResults = 1;
    var my_video_response = await my_video_request.ExecuteAsync();
    if( my_video_response.Items.Count != 0 )
    {
        logtext.Out("[Info ] Old VideoID [" + oldVideoID + "] is valid, then delete it.");
        var my_update_request = youtubeService.Videos.Delete(oldVideoID);
        my_update_request.Execute();
    }
}
catch(Exception ex)
{
    logtext.Out("[YouTube Exception] " + ex.Message + ex.StackTrace);
}

List
列出所有YouTube上的所有Video ID
try
{
    var channelsListRequest = youtubeService.Channels.List("contentDetails");
    channelsListRequest.Mine = true;
    var channelsListResponse = channelsListRequest.Execute();
    foreach( var channel in channelsListResponse.Items )
    {
        var uploadsListId = channel.ContentDetails.RelatedPlaylists.Uploads;
        var nextPageToken = "";
        while( nextPageToken != null )
        {
            var playlistItemsListRequest = youtubeService.PlaylistItems.List("snippet");
            playlistItemsListRequest.PlaylistId = uploadsListId;
            playlistItemsListRequest.MaxResults = 50;
            playlistItemsListRequest.PageToken = nextPageToken;
            var playlistItemsListResponse = playlistItemsListRequest.Execute();
            foreach( var playlistItem in playlistItemsListResponse.Items )
            {
                string id = playlistItem.Snippet.ResourceId.VideoId;
                videoIDList.Add(id);
            }
            nextPageToken = playlistItemsListResponse.NextPageToken;
        }
    }
}
catch( Exception ex )
{
    logtext.Out("[Exception] " + ex.Message + ex.StackTrace);
}

[C#] File access issue

為了確保檔案已無任何程式存取,個人使用了下列方式

檢查檔案的A C M time,確保其時間與當下時間差大於指定的秒數

public static bool CheckFileInUse(Logs logtext, string filePath, int timeout)
{
    if( !EFCS.FileExist(logtext, filePath, Conf.RUNNING_NO_CMD_MESSAGE) )
        return false;
    DateTime lastAT = System.IO.File.GetLastAccessTime(filePath);
    DateTime createT = System.IO.File.GetCreationTime(filePath);
    DateTime lastWT = System.IO.File.GetLastWriteTime(filePath);
    DateTime nowT = DateTime.Now;
    if( (nowT - lastAT).TotalSeconds < 0 ||
        (nowT - createT).TotalSeconds < 0 ||
        (nowT - lastWT).TotalSeconds < 0 )
    {
        System.IO.File.SetCreationTime(filePath, DateTime.Now);
        System.IO.File.SetLastAccessTime(filePath, DateTime.Now);
        System.IO.File.SetLastWriteTime(filePath, DateTime.Now);
    }
    lastAT = System.IO.File.GetLastAccessTime(filePath);
    createT = System.IO.File.GetCreationTime(filePath);
    lastWT = System.IO.File.GetLastWriteTime(filePath);
    nowT = DateTime.Now;
    if( (nowT - lastAT).TotalSeconds < timeout ||
        (nowT - createT).TotalSeconds < timeout ||
        (nowT - lastWT).TotalSeconds < timeout )
    {
        logtext.Out("[Info ] [" + filePath + "] creation/modification time interval < " + timeout + " secs");
        return true;
    }
    return false;
}
此外,另增加了File Lock檢查

public static bool CheckLock(Logs logtext, string _strSourceFileName)
{
    int i = 0;
    bool bResult = true;
    while( i < 20 )//Retry 20次
    {
        try
        {
            using( Stream stream = System.IO.File.Open(_strSourceFileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite) )
            {
                if( stream != null )
                {
                    logtext.Out("[Info ] [" + _strSourceFileName + "] is ready.");
                    bResult = false;
                    break;
                }
            }
        }
        catch( FileNotFoundException ex )
        {
            logtext.Out("[Info ] [" + _strSourceFileName + "] is not ready. (" + ex.Message + ")");
            bResult = true;
        }
        catch( IOException ex )
        {
            logtext.Out("[Info ] [" + _strSourceFileName + "] is not ready. (" + ex.Message + ")");
            bResult = true;
        }
        catch( UnauthorizedAccessException ex )
        {
            logtext.Out("[Info ] [" + _strSourceFileName + "] is not ready. (" + ex.Message + ")");
            bResult = true;
        }
        finally
        {
            i++;
            System.Threading.Thread.Sleep(500);
        }
    }
    return bResult;
}

搜尋此網誌