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;
}

2015年7月8日 星期三

[Linux] Execute other cmd from cmd 'find'

將目錄下所有size=0之檔案,調整其權限為777 "rwxrwxrwx"

find . -type f -size 0k -exec chmod 777 {} ;

將目錄下所有權限非777 "rwxrwxrwx"之檔案都列出

find . -type f ! -perm 777;

2015年5月25日 星期一

[C] File mappings and domain of NHibernate for SQLite DB

C#中針對SQLite使用NHibernate來存取DB資料

1. 在C#專案裡需安裝的Packages

NHibernate
Iesi.Collections

2. 額外使用到的軟體 (產出cs與xml檔案)
NHibernateMappingGenerator

設定參考

Entity不修改
Namespace (Domain)、Namespace (Map)、Assembly Name都設為專案名
 Connection選擇SQLite,並修改Data Source的路徑指向DB file

最後按下Generate All即可產出cs與xml檔案


3. 在C#專案中建立Domain與Mapping資料夾,分別放入cs檔案與xml檔案

4. 在C#專案中建立hibernate.cfg.xml
 
    NHibernate.Driver.SQLite20Driver
    Data Source=[DB檔案路徑];Version=3
    NHibernate.Dialect.SQLiteDialect
    true=1;false=0
    true
   
 

5. 在C#專案中新增一class 'NHibernateHelper'

namespace [專案名]
{
    public class NHibernateHelper
    {
        private static ISessionFactory _sessionFactory;
        private static ISessionFactory SessionFactory
        {
            get
            {
                if (_sessionFactory == null)
                {
                    var configuration = new Configuration();
                    configuration.Configure();
                    _sessionFactory = configuration.BuildSessionFactory();
                }
                return _sessionFactory;
            }
        }
        public static ISession OpenSession()
        {
            return SessionFactory.OpenSession();
        }
    }
}
完成以上設置即可開始實作相關DB存取程式

2015年5月15日 星期五

[C#] Auto load x86/x64 SQLite DLL for Platform

  How to Reference Platform Specific (X86/x64) Assemblies in Visual Studio Projects


Unload對想要有此功能之Project,Edit xxx.csproj,新增下方內容


<!--Start Code-->
<choose></choose>
<when condition="$(Platform) == 'x64'"></when>
<itemgroup></itemgroup><br />
<reference include="System.Data.SQLite, Version=1.0.65.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=AMD64"></reference>
<specificversion>False</specificversion>
<hintpath>..SQLiteX64System.Data.SQLite.DLL</hintpath>
<otherwise></otherwise>
<itemgroup></itemgroup>
<reference include="System.Data.SQLite, Version=1.0.65.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86"></reference>
<specificversion>False</specificversion>
<hintpath>..SQLiteX86System.Data.SQLite.DLL</hintpath>
<!--End Code-->

存檔後關閉,然後Reload此Project,選擇Platform (此時Reference的DLL路徑已經修正成你所選定的平台對應的DLL),Rebuild即可。

2015年4月21日 星期二

[C#] Post-Build event command line

Copy file(s) from one project to another using post build event

Visual Studio provides customized cmd script functions after Building solution.


Project->Property->Build Events->Post-Build event command line:


copy "$(SolutionDir)xxx.xml" "$(TargetDir)xxx.xml"


如上設置,可指定將Solotion Item的檔案一同複製一份至Project out folder

[C#] Set version for all projects in one solution

C# version for all projects in one solution


  1. Create an new "VersionInfo.cs" in solution item
  2. Clear its content
  3. Copy content of  one of your "AssemblyInfo.cs" in your project to "VersionInfo.cs" (Modify the content if you want)
  4. Clear content of all "AssemblyInfo.cs" in all projects
  5. Add Link to the "VersionInfo.cs" in each of your projects

2015年4月15日 星期三

VIM Settings for Visual Studio 2008

Vi Tool

ViEmu (Install-ViEmuVS-3.0.6.exe)

Key Binding
Tool->Option->General->KeyBoard 設定

View.NavigateBackward (Ctrl + T)
Edit.GoToDefinition (Ctrl+])

Theme

Tool->Import and Export Settings 設定

coding-instinct-theme_vs2008.vssettings (網路上找尋自己想要的)

2015年3月4日 星期三

使用PowerShell設定系統變數與ps1範例

以下ps1範例包含系統時間格式、程式(標準/錯誤)輸出重導、外部程式呼叫與ps1變數使用

$datestr = Get-Date -format yyyMMdd
$reporttype = "COMP"
$folder = "D:\reportFolder\"
$toolpath = "D:\scripts\Report.php"

$log = '{0}logs\{1}_{2}.log' -f $folder, $reporttype, $datestr
$err = '{0}logs\{1}_{2}.err' -f $folder, $reporttype, $datestr
php.exe $toolpath type=$reporttype folder=$folder >>$log 2>>$err

PowerShell ps1之語法遠優於舊有的bat,使用上更為便利與靈活、更為易讀。


直接透過PowerShell永久設定系統環境變數之指令
# example of adding a path to PATH
[System.Environment]::SetEnvironmentVariable("PATH", $Env:Path + ";C:\Program Files (x86)\PHP", "Machine")

# creates “myY” of category “User”, and set the value to “"la la"”
[Environment]::SetEnvironmentVariable("myY", "la la", "User")

# removing a env var from registry
[Environment]::SetEnvironmentVariable("myY", $null, "User")

# show value of “path”
$env:path

搜尋此網誌