3

Я не могу решить, как возобновить прерванную загрузку в V3 API-интерфейса YouTube C#.YouTube C# API V3, как вы возобновите прерванную загрузку?

Мой существующий код использует V1 и работает нормально, но я переключаюсь на V3.

Если я вызываю UploadAsync(), ничего не меняя, он начинается с начала. Используя Fiddler, я вижу, что protocol given here не соблюдается, и загрузка перезапускается.

Я попытался установить положение в потоке согласно V1, но не существует метода ResumeAsync().

В примере на Python используется NextChunk, но метод SendNextChunk защищен и недоступен на C#.

В приведенном ниже коде как UploadVideo(), так и Resume() работают нормально, если я оставлю их до завершения, но все видео загружается вместо оставшихся частей.

Как возобновить прерванную загрузку с помощью google.apis.youtube.v3?

Вот код C#, который я пробовал до сих пор.

private ResumableUpload<Video> UploadVideo(
    YouTubeService youTubeService, Video video, Stream stream, UserCredential userCredentials) 
{ 
    var resumableUpload = youTubeService.Videos.Insert(video, 
     "snippet,status,contentDetails", stream, "video/*"); 
    resumableUpload.OauthToken = userCredentials.Token.AccessToken; 
    resumableUpload.ChunkSize = 256 * 1024; 
    resumableUpload.ProgressChanged += resumableUpload_ProgressChanged; 
    resumableUpload.ResponseReceived += resumableUpload_ResponseReceived;     
    resumableUpload.UploadAsync(); 
    return resumableUpload; 
} 

private void Resume(ResumableUpload<Video> resumableUpload) 
{ 
    //I tried seeking like V1 but it doesn't work 
    //if (resumableUpload.ContentStream.CanSeek) 
    // resumableUpload.ContentStream.Seek(resumableUpload.ContentStream.Position, SeekOrigin.Begin); 

    resumableUpload.UploadAsync(); // <----This restarts the upload        
} 

void resumableUpload_ResponseReceived(Video obj) 
{     
    Debug.WriteLine("Video status: {0}", obj.Status.UploadStatus);      
} 

void resumableUpload_ProgressChanged(IUploadProgress obj) 
{ 
    Debug.WriteLine("Position: {0}", (resumableUploadTest == null) ? 0 : resumableUploadTest.ContentStream.Position); 
    Debug.WriteLine("Status: {0}", obj.Status); 
    Debug.WriteLine("Bytes sent: {0}", obj.BytesSent); 
} 

private void button2_Click(object sender, EventArgs e) 
{ 
    Resume(resumableUploadTest); 
} 

Любое решение/предложение/демо или ссылку на «google.apis.youtube.v3» исходный код будет очень полезно.

Спасибо заранее!

EDIT: Новая информация

Я все еще работаю над этим, и я считаю, что API просто не закончена. Либо это, либо я пропустил что-то простое.

Я до сих пор не могу найти исходный код google.apis.youtube.v3, поэтому я загрузил исходный код «google-api-dotnet-client». Это содержит класс ResumableUpload, используемый API YouTube.

Мне удалось успешно продолжить загрузку, пропустив первые четыре строки кода в методе UploadAsync(). Я создал новый метод под названием ResumeAsync(), экземпляр UploadAsync() с удалением первых четырех строк кода инициализации. Все работало, и загрузка возобновилась с того места, где она была и была завершена.

Я бы предпочел не менять код в API, поэтому, если кто-нибудь знает, как я должен использовать это, дайте мне знать.

Я буду продолжать подключаться и посмотреть, смогу ли я это обработать.

Это оригинальный метод UploadAsync() и мой метод ResumeAsync().

public async Task<IUploadProgress> UploadAsync(CancellationToken cancellationToken) 
{ 
    try 
    { 
     BytesServerReceived = 0; 
     UpdateProgress(new ResumableUploadProgress(UploadStatus.Starting, 0)); 
     // Check if the stream length is known. 
     StreamLength = ContentStream.CanSeek ? ContentStream.Length : UnknownSize; 
     UploadUri = await InitializeUpload(cancellationToken).ConfigureAwait(false); 

     Logger.Debug("MediaUpload[{0}] - Start uploading...", UploadUri); 

     using (var callback = new ServerErrorCallback(this)) 
     { 
      while (!await SendNextChunkAsync(ContentStream, cancellationToken).ConfigureAwait(false)) 
      { 
       UpdateProgress(new ResumableUploadProgress(UploadStatus.Uploading, BytesServerReceived)); 
      } 
      UpdateProgress(new ResumableUploadProgress(UploadStatus.Completed, BytesServerReceived)); 
     } 
    } 
    catch (TaskCanceledException ex) 
    { 
     Logger.Error(ex, "MediaUpload[{0}] - Task was canceled", UploadUri); 
     UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceived)); 
     throw ex; 
    } 
    catch (Exception ex) 
    { 
     Logger.Error(ex, "MediaUpload[{0}] - Exception occurred while uploading media", UploadUri); 
     UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceived)); 
    } 

    return Progress; 
} 

public async Task<IUploadProgress> ResumeAsync(CancellationToken cancellationToken) 
{ 
    try 
    { 
     using (var callback = new ServerErrorCallback(this)) 
     { 
      while (!await SendNextChunkAsync(ContentStream, cancellationToken).ConfigureAwait(false)) 
      { 
       UpdateProgress(new ResumableUploadProgress(UploadStatus.Uploading, BytesServerReceived)); 
      } 
      UpdateProgress(new ResumableUploadProgress(UploadStatus.Completed, BytesServerReceived)); 
     } 
    } 
    catch (TaskCanceledException ex) 
    {      
     UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceived)); 
     throw ex; 
    } 
    catch (Exception ex) 
    {      
     UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceived)); 
    } 

    return Progress; 
} 

Таковы Fiddler records показывая RESUMING загрузки.

ответ

2

После небольшого обсуждения я решил изменить код API. Мое решение поддерживает обратную совместимость.

Я зарегистрировал свои изменения ниже, но я не рекомендую их использовать.

В методе UploadAsync() в классе ResumableUpload в «Google.Apis.Upload» я заменил этот код.

BytesServerReceived = 0; 
UpdateProgress(new ResumableUploadProgress(UploadStatus.Starting, 0)); 
// Check if the stream length is known. 
StreamLength = ContentStream.CanSeek ? ContentStream.Length : UnknownSize; 
UploadUri = await InitializeUpload(cancellationToken).ConfigureAwait(false); 

с этим кодом

UpdateProgress(new ResumableUploadProgress(
    BytesServerReceived == 0 ? UploadStatus.Starting : UploadStatus.Resuming, BytesServerReceived)); 
StreamLength = ContentStream.CanSeek ? ContentStream.Length : UnknownSize; 
if (UploadUri == null) UploadUri = await InitializeUpload(cancellationToken).ConfigureAwait(false); 

Я также сделал UploadUri и BytesServerReceived свойства общественности. Это позволяет продолжить загрузку после того, как объект ResumableUpload был уничтожен или после перезапуска приложения.

Вы просто воссоздаете ResumableUpload как обычно, установите эти два поля и вызовите UploadAsync(), чтобы возобновить загрузку. Оба поля должны быть сохранены во время первоначальной загрузки.

public Uri UploadUri { get; set; } 
public long BytesServerReceived { get; set; } 

Я также добавил «Возобновление» к перечислению UploadStatus в классе IUploadProgress.

public enum UploadStatus 
{ 
    /// <summary> 
    /// The upload has not started. 
    /// </summary> 
    NotStarted, 

    /// <summary> 
    /// The upload is initializing. 
    /// </summary> 
    Starting, 

    /// <summary> 
    /// Data is being uploaded. 
    /// </summary> 
    Uploading, 

    /// <summary> 
    /// Upload is being resumed. 
    /// </summary> 
    Resuming, 

    /// <summary> 
    /// The upload completed successfully. 
    /// </summary> 
    Completed, 

    /// <summary> 
    /// The upload failed. 
    /// </summary> 
    Failed 
}; 

Ничего не изменилось при отправке.

При условии, что ResumableUpload Oject и потоки не были уничтожены, снова вызовите UploadAsync(), чтобы возобновить прерывание загрузки.

Если они были уничтожены, создайте новые объекты и установите свойства UploadUri и BytesServerReceived. Эти два свойства можно сохранить во время первоначальной загрузки. Детали видео и поток контента могут быть настроены в соответствии с нормальным.

Эти несколько изменений позволяют возобновить загрузку даже после перезапуска приложения или перезагрузки. Я не уверен, что задолго до истечения срока действия загрузки, но я отчитаю, когда еще раз проведу тестирование с помощью своего реального приложения.

Только для полноты, это тестовый код, который я использовал, который с удовольствием возобновляет прерванную загрузку после перезапуска приложения несколько раз во время загрузки. Единственное различие между возобновлением и перезапуском - настройка свойств UploadUri и BytesServerReceived.

resumableUploadTest = youTubeService.Videos.Insert(video, "snippet,status,contentDetails", fileStream, "video/*"); 
if (resume) 
{ 
    resumableUploadTest.UploadUri = Settings.Default.UploadUri; 
    resumableUploadTest.BytesServerReceived = Settings.Default.BytesServerReceived;     
}            
resumableUploadTest.ChunkSize = ResumableUpload<Video>.MinimumChunkSize; 
resumableUploadTest.ProgressChanged += resumableUpload_ProgressChanged; 
resumableUploadTest.UploadAsync(); 

Я надеюсь, что это кому-то поможет. Мне потребовалось намного больше времени, чем ожидалось, и я все еще надеюсь, что пропущу что-то простое. Я долгое время мучился, пытаясь добавить свои собственные обработчики ошибок, но API делает все это для вас. API действительно восстанавливается из небольших коротких икота, но не из перезапуска приложения, перезагрузки или длительного простоя.

Cheers. Мик.

1

Эта проблема разрешена в версии «1.8.0.960-rc» Google.Apis.YouTube.v3 Client Library.

Они добавили новый метод, называемый ResumeAsync, и он отлично работает. Хотел бы я знать, что они работали над этим.

Одной из незначительных проблем, которые мне необходимо было решить, было возобновление загрузки после перезапуска приложения или перезагрузки. Текущий api не позволяет этого, но две незначительные изменения разрешили проблему.

Я добавил новую подпись для метода ResumeAsync, который принимает и устанавливает оригинал UploadUri. Свойство StreamLength необходимо инициализировать, чтобы избежать ошибки переполнения.

public Task<IUploadProgress> ResumeAsync(Uri uploadUri, CancellationToken cancellationToken) 
{ 
    UploadUri = uploadUri; 
    StreamLength = ContentStream.CanSeek ? ContentStream.Length : UnknownSize; 
    return ResumeAsync(cancellationToken); 
} 

Я также выставил геттер для UploadUri, чтобы его можно было сохранить из вызывающего приложения.

public Uri UploadUri { get; private set; } 
+0

В API v3 библиотеки .NET Клиент Google теперь содержит функцию для сохранения UploadUri во время ResumableUpload и позже использовать что UploadUri возобновить загрузку в случае перезапуска программы. Есть два примера ResumableUpload в https://github.com/google/google-api-dotnet-client-samples –

2

Мне удалось заставить это работать с использованием отражения и избегать необходимости изменять API вообще. Для полноты я документирую процесс, но он не рекомендуется. Настройка частной собственности в возобновляемом загружаемом объекте - отличная идея.

Когда ваш загружаемый объект загрузки был уничтожен после перезагрузки или перезагрузки приложения, вы можете продолжить загрузку с использованием версии «1.8.0.960-rc» из Google.Apis.YouTube.v3 Client Library.

private static void SetPrivateProperty<T>(Object obj, string propertyName, object value) 
{ 
    var propertyInfo = typeof(T).GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance); 
    if (propertyInfo == null) return; 
    propertyInfo.SetValue(obj, value, null); 
} 

private static object GetPrivateProperty<T>(Object obj, string propertyName) 
{ 
    if (obj == null) return null; 
    var propertyInfo = typeof(T).GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance); 
    return propertyInfo == null ? null : propertyInfo.GetValue(obj, null); 
} 

Необходимо сохранить UploadUri во время события ProgressChanged.

Upload.ResumeUri = GetPrivateProperty<ResumableUpload<Video>>(InsertMediaUpload, "UploadUri") as Uri; 

Перед вызовом ResumeAsync необходимо установить UploadUri и StreamLength.

private const long UnknownSize = -1; 
SetPrivateProperty<ResumableUpload<Video>>(InsertMediaUpload, "UploadUri", Upload.ResumeUri); 
SetPrivateProperty<ResumableUpload<Video>>(InsertMediaUpload, "StreamLength", fileStream.CanSeek ? fileStream.Length : Constants.UnknownSize); 
Task = InsertMediaUpload.ResumeAsync(CancellationTokenSource.Token);