2017-04-03 41 views
0

我正在尋找一個示例,說明如何使用.Net客戶端庫爲YouTube報告API下載報告。我已通過身份驗證,創建了作業,並檢索了包含reportUrl值的報告列表。我無法弄清楚如何使用media.download方法實際下載報告並將其保存在本地。如何使用media.download下載YouTube報告

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

using System.IO; 
using System.Threading; //CancelationToken 
using Google.Apis; 
using Google.Apis.Auth.OAuth2; 
using Google.Apis.YouTubeReporting.v1; //Scope of auth 
using Google.Apis.YouTubeReporting.v1.Data; 
using Google.Apis.Services; 
using Google.Apis.Util.Store; //FileDataStore needs this 

using System.Data.SqlClient; 

namespace YouTubeReportingConsole 
{ 
class Program 
{ 
    static void Main(string[] args) 
    { 

     //Console.WriteLine("Number of command line parameters = {0}", args.Length); 
     //for (int i = 0; i < args.Length; i++) 
     //{ 
     // Console.WriteLine("Arg[{0}] = [{1}]", i, args[i]); 
     //} 

     if (args.Length > 0) 
     { 
      switch (args[0].ToLower()) 
      { 
       case "reporttypeslist": //ReportTypesList returns a list of report types that are available 
        ListReportTypesResponse reportTypesListResponse = ytReportingService.ReportTypes.List().Execute(); 
        foreach (ReportType reportType in reportTypesListResponse.ReportTypes) 
        { 
         WriteReportType(reportType, "Report Type List"); 
        } 
        break; 
       case "jobcreate": //JobCreate creates jobs of the specified types. If the second argument equals "All", then a job is created for all availale report types. 
        for (int i = 1; i < args.Length; i++) 
        { 
         if (args[i].ToLower() == "all") 
         { //Create a job for all available report types 
          ListReportTypesResponse reportTypesAvailableResponse = ytReportingService.ReportTypes.List().Execute(); 
          foreach (ReportType reportType in reportTypesAvailableResponse.ReportTypes) 
          { 
           Job createdjob = CreateJob(reportType.Id); 
           WriteJob(createdjob, "Job Created"); 
          } 
          break; 
         } 
         else 
         { //Create job with id equal to the argument value 
          Job createdjob = CreateJob(args[i].ToLower()); 
          WriteJob(createdjob, "Job Created"); 
         } 
        } 
        break; 
       case "jobdelete": 
        for (int i = 1; i < args.Length; i++) 
        { 
         if (args[i].ToLower() == "all") 
         { //Delete all jobs 
          ListJobsResponse jobDeleteList = ytReportingService.Jobs.List().Execute(); 
          foreach (Job Job in jobDeleteList.Jobs) 
          { 
           ytReportingService.Jobs.Delete(Job.Id).Execute(); 
           Console.WriteLine("Job Deleted: " + Job.Name); 
          } 

          break; 
         } 
         else 
         { //Delete each job with id equal to the argument value 
          Job Job = ytReportingService.Jobs.Get(args[i]).Execute(); 
          ytReportingService.Jobs.Delete(args[i]).Execute(); 
          Console.WriteLine("Job Deleted: " + Job.Name); 
         } 
        } 
        break; 
       //case "getjob": 
       // break; 
       case "joblist": 
        ListJobsResponse jobListResponse = ytReportingService.Jobs.List().Execute(); 
        if (jobListResponse.Jobs == null) 
        { 
         Console.WriteLine("No Jobs Returned"); 
        } 
        else 
        { 
         foreach (Job Job in jobListResponse.Jobs) 
         { 
          WriteJob(Job, "Job List"); 
         } 
        } 
        break; 
       //case "getreport": 
       // break; 
       case "reportlist": 
        //ReportList All [DataSource] 
        //ReportList [JobID] [DataSource] 
        //ReportList All [DataSource] [DownloadPath] 
        //ReportList [JobID] [DataSource] [DownloadPath] 

        SqlConnection conn = OpenConnection(args[2]); 
        VerifySchema(conn); 

        if (args[1].ToLower() == "all") 
        { //List reports for all jobs 

         ListJobsResponse jobListResponseReport = ytReportingService.Jobs.List().Execute(); 
         if (jobListResponseReport.Jobs == null) 
         { 
          Console.WriteLine("No Jobs Returned"); 
         } 
         else 
         { 
          foreach (Job Job in jobListResponseReport.Jobs) 
          { 
           Report(Job.Id, conn, args[3]); 
          } 
         } 
         break; 
        } 
        else 
        { //Single Job 
         Report(args[1], conn, args[3]); 
        } 
        break; 
       default: 
        break; 
      } 
     } 
    } 

    private static void Report(string JobId, SqlConnection conn, string path) 
    { 
     //Get the last created time. If empty then use 1900. 
     var reportList = ytReportingService.Jobs.Reports.List(JobId); 
     try 
     { 
      reportList.CreatedAfter = LastCreatedTime(conn, JobId); 
     } 
     catch 
     { 
      reportList.CreatedAfter = "1900-01-01T12:00:00.000000Z"; 
     } 
     ListReportsResponse reportListResponse = reportList.Execute(); 

     if (reportListResponse.Reports == null) 
     { 
      Console.WriteLine("No Reports for JobID: " + JobId); 
     } 
     else 
     { 
      foreach (Report report in reportListResponse.Reports) 
      { 

       //Download the report 
       var request = ytReportingService.Media.Download(""); 

//Insert report into history 
string reportInsertSql = "INSERT INTO [dbo].[YouTubeReportHistory]" + "\n" + 
       "([reportId]" + "\n" + 
       ",[jobId]" + "\n" + 
       ",[startTime]" + "\n" + 
       ",[endTime]" + "\n" + 
       ",[createTime]" + "\n" + 
       ",[jobExpireTime]" + "\n" + 
       ",[downloadUrl])" + "\n" + 
       "VALUES" + "\n" + 
       "('" + report.Id + "'\n" + 
       ",'" + JobId + "'\n" + 
       ",'" + report.StartTime + "'\n" + 
       ",'" + report.EndTime + "'\n" + 
       ",'" + report.CreateTime + "'\n" + 
       ",'" + report.JobExpireTime + "'\n" + 
       ",'" + report.DownloadUrl + "')"; 

       SqlCommand reportInsert = new SqlCommand(reportInsertSql, conn); 
       reportInsert.ExecuteNonQuery(); 
       reportInsert.Dispose(); 
       WriteReport(report, "Report"); 
      } 
     } 
    } 

    private static string LastCreatedTime(SqlConnection conn, string JobId) 
    { 
     //***************************************************************************************************************** 
     //Library is not returning milliseconds. Adding 1 seconds to prevent collecting same report multiple times. 
     //Risk of missing report. Minimal chance that the same job will return more than one report in one second. 
     //***************************************************************************************************************** 
     SqlCommand comm = new SqlCommand("SELECT isnull(convert(varchar(50), dateadd(second, 1, max(cast([createTime] as datetimeoffset))), 127), '1900-01-01T12:00:00.000000Z') createTimeZulu FROM [dbo].[YouTubeReportHistory] where jobId = '" + JobId + "'", conn); 
     SqlDataReader reader = null; 
     reader = comm.ExecuteReader(); 
     string LastCreatedTime = ""; 
     if (reader.HasRows) 
     { 
      reader.Read(); 
      LastCreatedTime = reader["createTimeZulu"].ToString(); 
      reader.Close(); 
      comm.Dispose(); 
     } 
     else 
     { 
      LastCreatedTime = "1900-01-01T12:00:00.000000Z"; 
     } 

     return LastCreatedTime; 
    } 

    //Feedback information only 
    private static void WriteReport(Report report, String Event) 
    { 


     // DateTime convertedDate = DateTime.SpecifyKind(DateTime.Parse(report.CreateTime.ToString()), DateTimeKind.Utc); 

     Console.WriteLine("================== Job Report =================="); 
     Console.WriteLine("Event: " + Event); 
     Console.WriteLine("ID: " + report.Id); 
     Console.WriteLine("DownloadUrl: " + report.DownloadUrl); 
     Console.WriteLine("CreateTime: " + report.CreateTime.ToString()); 
     Console.WriteLine("StartTime: " + report.StartTime); 
     Console.WriteLine("EndTime: " + report.EndTime); 
     Console.WriteLine("JobID: " + report.JobId); 
     Console.WriteLine("JobExpireTime: " + report.JobExpireTime); 
    } 

    //Feedback information only 
    private static void WriteJob (Job job, String Event) { 
     Console.WriteLine("================== Reporting Job =================="); 
     Console.WriteLine("Event: " + Event); 
     Console.WriteLine("ID: " + job.Id); 
     Console.WriteLine("Name: " + job.Name); 
     Console.WriteLine("ReportTypeID: " + job.ReportTypeId); 
     Console.WriteLine("CreateTime: " + job.CreateTime); 
     Console.WriteLine("ExpireTime: " + job.ExpireTime); 
    } 

    //Feedback information only 
    private static void WriteReportType(ReportType reportType, String Event) 
    { 
     Console.WriteLine("================== Report Type =================="); 
     Console.WriteLine("Event: " + Event); 
     Console.WriteLine("ID: " + reportType.Id); 
     Console.WriteLine("Name: " + reportType.Name); 
     Console.WriteLine("SystemManaged: " + reportType.SystemManaged); 
    } 

    //Create a job 
    private static Job CreateJob(String ReportType) 
    { 
     Job job = new Job(); 
     job.ReportTypeId = ReportType; 
     job.Name = ReportType; //Use the type as the name 
     Job createdjob = ytReportingService.Jobs.Create(job).Execute(); 

     return createdjob; 
    } 

    private static String DeleteJob(String JobID) 
    { 
     ytReportingService.Jobs.Delete(JobID); 
     return JobID; 
    } 

    //Establish connection 
    private static SqlConnection OpenConnection(string connectionString) 
    { 
     //Data Source = WIN-IDD5KG7V0RT\SS2016;Integrated Security=true; Initial Catalog=Control; MultipleActiveResultSets=True; 
     //Database must exists 
     //Must have MARS = True 
     SqlConnection conn = new SqlConnection(connectionString); 
     try 
     { 
      conn.Open(); 
     } 
     catch(Exception e) 
     { 
      Console.WriteLine(e.ToString()); 
     } 
     return conn; 
    } 

    //Close connection 
    private static void CloseConnection(SqlConnection conn) 
    { 
     try 
     { 
      conn.Close(); 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine(e.ToString()); 
     } 
    } 

    //Ensure schema is in place 
    private static void VerifySchema(SqlConnection conn) 
    { 
     //Ensure table exists 
     //Ensure db exists 
     String tblExists = "if not exists (Select * From sys.tables where name = 'YouTubeReportHistory')" + "\n" + 
     "Begin" + "\n" + 
     "CREATE TABLE[dbo].[YouTubeReportHistory](" + "\n" + 
     "[id][int] IDENTITY(1, 1) NOT NULL," + "\n" + 
     "[loadTime] [datetime] NOT NULL," + "\n" + 
     "[reportId] [varchar](50) NOT NULL," + "\n" + 
     "[jobId] [varchar](50) NOT NULL," + "\n" + 
     "[startTime] [datetime] NOT NULL," + "\n" + 
     "[endTime] [datetime] NOT NULL," + "\n" + 
     "[createTime] [datetime] NOT NULL," + "\n" + 
     "[jobExpireTime] [datetime] NOT NULL," + "\n" + 
     "[downloadUrl] [varchar](1000) NOT NULL," + "\n" + 
     "CONSTRAINT[PK_YouTubeReportHistory] PRIMARY KEY CLUSTERED([id] ASC)" + "\n" + 
     ")" + "\n" + 
     "ALTER TABLE[dbo].[YouTubeReportHistory] ADD CONSTRAINT[DF_YouTubeReportHistory_loadTime] DEFAULT(sysdatetime()) FOR[loadTime]" + "\n" + 
     "End"; 
     SqlCommand comm = new SqlCommand(tblExists, conn); 
     comm.ExecuteNonQuery(); 
     comm.Dispose(); 
    } 

    private static YouTubeReportingService ytReportingService = Auth(); 

    private static YouTubeReportingService Auth() 
    { 
     UserCredential creds; 
     using(var stream = new FileStream("client_secret.json", FileMode.Open, FileAccess.Read)) 
     { 
      creds = GoogleWebAuthorizationBroker.AuthorizeAsync(
       GoogleClientSecrets.Load(stream).Secrets, 
       new[] { YouTubeReportingService.Scope.YtAnalyticsMonetaryReadonly, YouTubeReportingService.Scope.YtAnalyticsReadonly }, 
       "user", 
       CancellationToken.None, 
       new FileDataStore("YouTubeAPI") 
       ).Result; 
     } 

     var service = new YouTubeReportingService(new BaseClientService.Initializer() 
     { 
      HttpClientInitializer = creds, 
      ApplicationName = "LeapFrogBIDataCollector" 
     }); 

     return service; 
    } 

} 

}

更新20170405

我相信我看到的問題有事情做與正在提供的下載URL的方式。經過大量的頭部劃傷和審查我可以找到什麼小的文檔,我結束了下面的代碼,這似乎是一個改進,但這仍然只返回標題。

   MediaResource.DownloadRequest getRequest = ytReportingService.Media.Download(""); 
       using (var fileStream = new System.IO.FileStream(@filePath + reportTypeId + "_" + report.Id + ".csv", System.IO.FileMode.Create, System.IO.FileAccess.Write)) 
       { 
        // Add a handler which will be notified on progress changes. 
        // It will notify on each chunk download and when the download is completed or failed. 
        getRequest.MediaDownloader.ProgressChanged += Download_ProgressChanged; 
        getRequest.MediaDownloader.Download(report.DownloadUrl, fileStream); 
       } 

20170406更新 我發現一些代碼例子在其他語言害得我在做以下修改。至少下載URL是直接傳入的,而不是我解析它並將其用作資源名。可悲的是我得到了同樣的結果;只有標題。沒有數據。

   //Download the report  
       MediaResource.DownloadRequest getRequest = ytReportingService.Media.Download(""); 

       using (var fileStream = new System.IO.FileStream(@filePath + reportTypeId + "_" + report.Id + ".csv", System.IO.FileMode.Create, System.IO.FileAccess.Write)) 
       { 
        // Add a handler which will be notified on progress changes. It will notify on each chunk download and when the download is completed or failed. 
        getRequest.MediaDownloader.ProgressChanged += Download_ProgressChanged; 
        getRequest.MediaDownloader.Download(report.DownloadUrl, fileStream); 
       } 
+0

是否有可能只獲取列標題,因爲沒有爲報告選擇數據? –

+0

我不確定你的意思,邁克。請詳細說明。 Reporting API有一組預定義的數據,其中包括24小時的活動時間段。我們只能創建一個工作,要求生成報告,然後下載生成的csv。我誤解了嗎? – DataMe

+0

我不是Reporting API的專家。我在[本頁](https://developers.google.com/youtube/reporting/v1/reports/#step-6-download-the-report)上閱讀了以下文檔,並想知道是否適用於您的問題。 「YouTube確實會在沒有數據可用的日子生成可下載的報告,這些報告將包含標題行但不包含其他數據。」 –

回答

1

後期示例中media.download的最終版本是正確的。

我在YouTube上有一個「品牌帳戶」。這意味着我登錄Google的用戶擁有2個「帳戶」。一個帳戶與品牌帳戶相關聯,另一個與我的Google用戶相關聯。

我通過刪除訪問&刷新令牌來重置Oath2進程。這促使我再次允許應用程序訪問所請求的範圍。首先,我會看到一個典型的YouTube登錄屏幕。然後,我會看到一個屏幕,要求我在兩個帳戶之間進行選擇; [email protected]或LeapFrogBI。品牌帳戶是名爲LeapFrogBI的品牌帳戶。

它是有道理的,API將需要知道哪個帳戶鏈接到應用程序。雖然這很令人困惑。目前爲止,我沒有發現任何文件。坦率地說,我發現這一點的唯一原因是因爲我開始質疑我是否需要爲OnBehalfOfContentOwner傳遞值。雖然我仍然沒有100%清楚的命名,但我想我明白髮生了什麼事情。

有了這些信息,我回去跑了幾次測試。雖然Oath2被設置爲使用LeapFrogBI帳戶,但我沒有收到任何工作!這是一個很大的線索。所以,我將Oath2切換到[email protected]帳戶,並獲得了我的預期工作清單。當然,所有的視頻都在LeapFrogBI帳戶上,所以這可以解釋爲什麼我沒有在下載的報告中獲取數據。

我在LeapFrogBI帳戶上創建了報告,但報告將需要2天才能發佈。手指交叉。我希望這個問題得到解決,但我一定會讓你知道發生了什麼。

確認。我現在正在下載csv中獲取數據。