private void UpdateProgressToast(NicoVideoCacheRequest req, DownloadOperation op) { if (!Helpers.ApiContractHelper.IsCreatorsUpdateAvailable) { return; } // Construct a NotificationData object; string toastTag = $"{req.RawVideoId}_{req.Quality}"; string toastGroup = "hohoema_cache_dl"; var progress = op.Progress.BytesReceived / (double)op.Progress.TotalBytesToReceive; var progressText = (progress * 100).ToString("F0"); // Create NotificationData with new values; // Make sure that sequence number is incremented since last update, or assign with value 0 for updating regardless of order; var data = new NotificationData { SequenceNumber = 0 }; data.Values["progressValue"] = progress.ToString("F1"); // 固定小数点、整数部と小数一桁までを表示 data.Values["progressString"] = $"{progressText}%"; data.Values["progressStatus"] = "donwloading"; // Updating a previously sent toast with tag, group, and new data; NotificationUpdateResult updateResult = ToastNotificationManager.CreateToastNotifier().Update(data, toastTag, toastGroup); }
// 次のキャッシュダウンロードを試行 // ダウンロード用の本数 public async Task TryNextCacheRequestedVideoDownload() { if (!IsInitialized) { return; } if (!HohoemaApp.IsLoggedIn) { return; } NicoVideoCacheRequest nextDownloadItem = null; using (var releaser = await _DownloadOperationsLock.LockAsync()) using (var pendingVideoLockReleaser = await _CacheDownloadPendingVideosLock.LockAsync()) { if (CanAddDownloadLine) { nextDownloadItem = _CacheDownloadPendingVideos .SkipWhile(x => _DownloadOperations.ContainsKey(x)) .FirstOrDefault(); } } if (nextDownloadItem != null) { var op = await DonwloadVideoInBackgroundTask(nextDownloadItem); } }
public async Task RequestCache(NicoVideoCacheRequest req) { var requests = await GetCacheRequest(req.RawVideoId); var already = requests.FirstOrDefault(x => x.RawVideoId == req.RawVideoId && x.Quality == req.Quality); if (already != null) { req.RequestAt = already.RequestAt; } else { using (var releaser2 = await _CacheRequestProcessingLock.LockAsync()) { // 画質指定が無い場合はデフォルトのキャッシュ画質を選択 if (req.Quality == NicoVideoQuality.Unknown) { req.Quality = _HohoemaApp.UserSettings.CacheSettings.DefaultCacheQuality; } _CacheDownloadPendingVideos.Add(req); Requested?.Invoke(this, req); VideoCacheStateChanged?.Invoke(this, new VideoCacheStateChangedEventArgs() { CacheState = NicoVideoCacheState.Pending, Request = req }); } } await SaveDownloadRequestItems(); }
public async Task <bool> RemoveCacheRequest(string rawVideoId, NicoVideoQuality quality) { NicoVideoCacheRequest removeTarget = null; using (var pendingVideoLockReleaser = await _CacheDownloadPendingVideosLock.LockAsync()) { removeTarget = _CacheDownloadPendingVideos.SingleOrDefault(x => x.RawVideoId == rawVideoId && x.Quality == quality); } // ダウンロード中タスクを削除(DLのキャンセル) bool removed = false; if (removeTarget != null) { await RemoveDownloadOperation(removeTarget); using (var pendingVideoLockReleaser = await _CacheDownloadPendingVideosLock.LockAsync()) { _CacheDownloadPendingVideos.Remove(removeTarget); } await SaveDownloadRequestItems(); RequestCanceled?.Invoke(this, removeTarget); removed = true; } await TryNextCacheRequestedVideoDownload(); return(removed); }
internal async Task <bool> _AddCacheRequest_Internal(NicoVideoCacheRequest req) { var nicoVideo = await MediaManager.GetNicoVideoAsync(req.RawVideoId); var div = nicoVideo.GetDividedQualityNicoVideo(req.Quality); if (div.IsCached) { if (req.IsRequireForceUpdate) { Debug.WriteLine($"{req.RawVideoId}<{req.Quality}> is already cached, but enable force update .(re-download)"); await RemoveCacheRequest(req.RawVideoId, req.Quality); } else { Debug.WriteLine($"{req.RawVideoId}<{req.Quality}> is already cached. (skip download)"); Requested?.Invoke(this, req); DownloadCompleted?.Invoke(this, req, div.CacheFilePath); return(false); } } using (var pendingVideoLockReleaser = await _CacheDownloadPendingVideosLock.LockAsync()) { _CacheDownloadPendingVideos.Add(req); } Requested?.Invoke(this, req); return(true); }
public NicoVideoCacheInfo(NicoVideoCacheRequest req, string filePath) { RawVideoId = req.RawVideoId; Quality = req.Quality; IsRequireForceUpdate = req.IsRequireForceUpdate; RequestAt = req.RequestAt; FilePath = filePath; }
private async Task <DividedQualityNicoVideo> GetNicoVideo(NicoVideoCacheRequest cacheRequest) { var nicoVideo = await MediaManager.GetNicoVideoAsync(cacheRequest.RawVideoId); var div = nicoVideo.GetDividedQualityNicoVideo(cacheRequest.Quality); return(div); }
public NicoVideoCacheProgress(NicoVideoCacheRequest req, DownloadOperation op, IVideoStreamingSession session) { RawVideoId = req.RawVideoId; Quality = session.Quality; IsRequireForceUpdate = req.IsRequireForceUpdate; RequestAt = req.RequestAt; DownloadOperation = op; Session = session; }
private async void VideoDownloadManager_DownloadCompleted(object sender, NicoVideoCacheRequest request, string filePath) { var nicoVideo = await GetNicoVideoAsync(request.RawVideoId); var div = nicoVideo.GetDividedQualityNicoVideo(request.Quality); await div.RestoreCache(filePath); VideoCacheStateChanged?.Invoke(this, request, NicoVideoCacheState.Cached); }
/// <summary> /// キャッシュリクエストをキューの最後尾に積みます /// 通常のダウンロードリクエストではこちらを利用します /// </summary> /// <param name="req"></param> /// <returns></returns> public Task RequestCache(string rawVideoId, NicoVideoQuality quality = NicoVideoQuality.Unknown, bool forceUpdate = false) { var req = new NicoVideoCacheRequest() { RawVideoId = rawVideoId, Quality = quality, RequestAt = DateTime.Now }; return(RequestCache(req)); }
/// <summary> /// キャッシュリクエストをキューの最後尾に積みます /// 通常のダウンロードリクエストではこちらを利用します /// </summary> /// <param name="req"></param> /// <returns></returns> public Task AddCacheRequest(string rawVideoId, NicoVideoQuality quality, bool forceUpdate = false) { var req = new NicoVideoCacheRequest() { RawVideoId = rawVideoId, Quality = quality, RequestAt = DateTime.Now }; return(AddCacheRequest(req, forceUpdate)); }
private async Task AddDownloadOperation(NicoVideoCacheRequest req, DownloadOperation op) { using (var releaser = await _DownloadOperationsLock.LockAsync()) { _DownloadOperations.Add(req, op); ++CurrentDownloadTaskCount; } // ダウンロード開始イベントをトリガー DownloadStarted?.Invoke(this, req, op); }
private async Task CacheRequestCanceled(NicoVideoCacheRequest req) { var nicoVideo = await GetNicoVideoAsync(req.RawVideoId); if (nicoVideo.GetAllQuality().All(x => !x.IsCacheRequested)) { using (var releaser = await _CacheVideosLock.LockAsync()) { _CacheVideos.Remove(nicoVideo); } } }
private async void VideoDownloadManager_DownloadProgress(object sender, NicoVideoCacheRequest request, DownloadOperation op) { if (request.RawVideoId == this.RawVideoId && request.Quality == this.Quality) { using (var releaser = await _Lock.LockAsync()) { CacheState = NicoVideoCacheState.Downloading; CacheTotalSize = (uint)op.Progress.TotalBytesToReceive; CacheProgressSize = (uint)op.Progress.BytesReceived; } } }
private async void VideoDownloadManager_DownloadCompleted(object sender, NicoVideoCacheRequest request, string filePath) { if (request.RawVideoId == this.RawVideoId && request.Quality == this.Quality) { await RestoreCache(filePath).ConfigureAwait(false); using (var releaser = await _Lock.LockAsync()) { VideoDownloadManager.DownloadProgress -= VideoDownloadManager_DownloadProgress; VideoDownloadManager.DownloadCanceled -= VideoDownloadManager_DownloadCanceled; VideoDownloadManager.DownloadCompleted -= VideoDownloadManager_DownloadCompleted; } } }
private async Task CacheRequested(NicoVideoCacheRequest req) { var nicoVideo = await GetNicoVideoAsync(req.RawVideoId, false); using (var releaser = await _CacheVideosLock.LockAsync()) { if (_CacheVideos.Any(x => x.RawVideoId == nicoVideo.RawVideoId)) { _CacheVideos.Remove(nicoVideo); } // 新しく追加されるアイテムを先頭に配置 _CacheVideos.Insert(0, nicoVideo); } }
public static NicoVideoCacheState ToCacheState(this NicoVideoCacheRequest req) { if (req is NicoVideoCacheInfo) { return(NicoVideoCacheState.Cached); } else if (req is NicoVideoCacheProgress) { return(NicoVideoCacheState.Downloading); } else { return(NicoVideoCacheState.Pending); } }
private async Task RestoreDonloadOperation(NicoVideoCacheRequest _info, DownloadOperation operation) { var div = await GetNicoVideo(_info); await AddDownloadOperation(_info, operation); await div.RestoreDownload(operation); var action = operation.AttachAsync(); action.Progress = OnDownloadProgress; var task = action.AsTask() .ContinueWith(OnDownloadCompleted) .ConfigureAwait(false); }
private async void VideoDownloadManager_DownloadCanceled(object sender, NicoVideoCacheRequest request) { if (request.RawVideoId == this.RawVideoId && request.Quality == this.Quality) { using (var releaser = await _Lock.LockAsync()) { if (CacheState == NicoVideoCacheState.Downloading) { CacheState = NicoVideoCacheState.Pending; } VideoDownloadManager.DownloadProgress -= VideoDownloadManager_DownloadProgress; VideoDownloadManager.DownloadCanceled -= VideoDownloadManager_DownloadCanceled; VideoDownloadManager.DownloadCompleted -= VideoDownloadManager_DownloadCompleted; } } }
private async Task RetrieveCacheCompletedVideos() { var videoFolder = await _HohoemaApp.GetVideoCacheFolder(); if (videoFolder != null) { var files = await videoFolder.GetFilesAsync(); foreach (var file in files) { if (file.FileType != ".mp4") { continue; } // ファイル名の最後方にある[]の中身の文字列を取得 // (動画タイトルに[]が含まれる可能性に配慮) var match = NicoVideoIdRegex.Match(file.Name); var id = match.Value; var quality = NicoVideoQualityFileNameHelper.NicoVideoQualityFromFileNameExtention(file.Name); var info = new NicoVideoCacheRequest() { RawVideoId = id, Quality = quality, }; if (string.IsNullOrEmpty(id)) { continue; } var nicoVideo = await GetNicoVideoAsync(info.RawVideoId, false); var div = nicoVideo.GetDividedQualityNicoVideo(quality); await nicoVideo.RestoreCache(quality, file.Path); await CacheRequested(info); VideoCacheStateChanged?.Invoke(this, info, NicoVideoCacheState.Cached); Debug.Write("."); } } }
public async Task <DownloadOperation> GetDownloadingVideoOperation(string rawVideoId, NicoVideoQuality quality) { NicoVideoCacheRequest req = new NicoVideoCacheRequest() { RawVideoId = rawVideoId, Quality = quality }; using (var releaser = await _DownloadOperationsLock.LockAsync()) { if (_DownloadOperations.ContainsKey(req)) { return(_DownloadOperations[req]); } else { return(null); } } }
private async Task RemoveDownloadOperation(NicoVideoCacheRequest req) { DownloadOperation op = null; using (var releaser = await _DownloadOperationsLock.LockAsync()) { if (_DownloadOperations.ContainsKey(req)) { op = _DownloadOperations[req]; if (op.Progress.BytesReceived != op.Progress.TotalBytesToReceive) { op.AttachAsync().Cancel(); await op.ResultFile.DeleteAsync(); } _DownloadOperations.Remove(req); --CurrentDownloadTaskCount; } else { return; } } }
public async Task <bool> AddCacheRequest(NicoVideoCacheRequest req, bool forceUpdate = false) { NicoVideoCacheRequest already = null; using (var pendingVideoLockReleaser = await _CacheDownloadPendingVideosLock.LockAsync()) { already = _CacheDownloadPendingVideos.FirstOrDefault(x => x.RawVideoId == req.RawVideoId && x.Quality == req.Quality); } if (already != null) { if (forceUpdate) { using (var pendingVideoLockReleaser = await _CacheDownloadPendingVideosLock.LockAsync()) { req.RequestAt = already.RequestAt; _CacheDownloadPendingVideos.Remove(already); } } else { await TryNextCacheRequestedVideoDownload(); return(true); } } var result = await _AddCacheRequest_Internal(req); if (result) { await SaveDownloadRequestItems(); await TryNextCacheRequestedVideoDownload(); } return(result); }
/// <summary> /// ダウンロードを停止します。 /// 現在ダウンロード中のアイテムはキャンセルしてPendingに積み直します /// </summary> /// <returns></returns> public async Task SuspendCacheDownload() { if (State != CacheManagerState.Running) { return; } List <NicoVideoCacheProgress> operations; using (var releaser2 = await _CacheRequestProcessingLock.LockAsync()) { operations = _DownloadOperations.ToList(); foreach (var progress in operations) { await RemoveDownloadOperation(progress); _CacheDownloadPendingVideos.Remove(progress); } State = CacheManagerState.SuspendDownload; } operations.Reverse(); foreach (var progress in operations) { var cacheRequest = new NicoVideoCacheRequest() { RawVideoId = progress.RawVideoId, RequestAt = progress.RequestAt, Quality = progress.Quality, IsRequireForceUpdate = progress.IsRequireForceUpdate }; await RequestCache(cacheRequest); } }
private void VideoDownloadManager_DownloadCanceled(object sender, NicoVideoCacheRequest request) { VideoCacheStateChanged?.Invoke(this, request, NicoVideoCacheState.Pending); }
// バックグラウンドで動画キャッシュのダウンロードを行うタスクを作成 private async Task <DownloadOperation> DonwloadVideoInBackgroundTask(NicoVideoCacheRequest req) { using (var bgTaskLock = await _RegistrationBackgroundTaskLock.LockAsync()) { using (var releaser = await _DownloadOperationsLock.LockAsync()) { if (_DownloadOperations.Keys.Any(x => x.RawVideoId == req.RawVideoId && x.Quality == req.Quality)) { return(null); } } Debug.WriteLine($"キャッシュ準備を開始: {req.RawVideoId} {req.Quality}"); // 動画ダウンロードURLを取得 var nicoVideo = await MediaManager.GetNicoVideoAsync(req.RawVideoId); var div = nicoVideo.GetDividedQualityNicoVideo(req.Quality); if (div.IsCached) { if (!req.IsRequireForceUpdate) { using (var pendingVideoLockReleaser = await _CacheDownloadPendingVideosLock.LockAsync()) { _CacheDownloadPendingVideos.Remove(req); } } else { // キャッシュ済みのためダウンロードを行わない // TODO: Completedイベントを発行 return(null); } } Uri uri = null; try { uri = await nicoVideo.SetupWatchPageVisit(req.Quality); if (uri == null) { throw new Exception($"can't download {req.Quality} quality Video, in {req.RawVideoId}."); } } catch { return(null); } var downloader = await ResetDownloader(); // 認証情報付きクッキーをダウンローダーのHttpヘッダにコピー // 動画ページアクセス後のクッキーが必須になるため、サインイン時ではなく // ダウンロード開始直前のこのタイミングでクッキーをコピーしています var httpclinet = HohoemaApp.NiconicoContext.HttpClient; foreach (var header in httpclinet.DefaultRequestHeaders) { downloader.SetRequestHeader(header.Key, header.Value); } // 保存先ファイルの確保 var filename = div.VideoFileName; var videoFolder = await HohoemaApp.GetVideoCacheFolder(); var videoFile = await videoFolder.CreateFileAsync(filename, CreationCollisionOption.OpenIfExists); // ダウンロード操作を作成 var operation = downloader.CreateDownload(uri, videoFile); downloader.CompletionGroup?.Enable(); await AddDownloadOperation(req, operation); Debug.WriteLine($"キャッシュ準備完了: {req.RawVideoId} {req.Quality}"); // ダウンロードを開始 var action = operation.StartAsync(); action.Progress = OnDownloadProgress; var task = action.AsTask().ContinueWith(OnDownloadCompleted).ConfigureAwait(false); Debug.WriteLine($"キャッシュ開始: {req.RawVideoId} {req.Quality}"); return(operation); } }
private void SendUpdatableToastWithProgress(string title, NicoVideoCacheRequest req) { if (!Helpers.ApiContractHelper.IsCreatorsUpdateAvailable) { return; } // Define a tag value and a group value to uniquely identify a notification, in order to target it to apply the update later; string toastTag = $"{req.RawVideoId}_{req.Quality}"; string toastGroup = "hohoema_cache_dl"; // Construct the toast content with updatable data fields inside; var content = new ToastContent() { Visual = new ToastVisual() { BindingGeneric = new ToastBindingGeneric() { Children = { new AdaptiveText() { Text = title, HintStyle = AdaptiveTextStyle.Header }, new AdaptiveProgressBar() { Value = new BindableProgressBarValue("progressValue"), ValueStringOverride = new BindableString("progressString"), Status = new BindableString("progressStatus") } } } }, Actions = new ToastActionsCustom() { Buttons = { new ToastButton("キャンセル", $"cache_cancel?id={req.RawVideoId}&quality={req.Quality}") { } } }, }; // Generate the toast notification; var toast = new ToastNotification(content.GetXml()); // Assign the tag and group properties; toast.Tag = toastTag; toast.Group = toastGroup; // Define NotificationData property and add it to the toast notification to bind the initial data; // Data.Values are assigned with string values; toast.Data = new NotificationData(); toast.Data.Values["progressValue"] = "0"; toast.Data.Values["progressString"] = $""; toast.Data.Values["progressStatus"] = "download started"; // Provide sequence number to prevent updating out-of-order or assign it with value 0 to indicate "always update"; toast.Data.SequenceNumber = 1; toast.SuppressPopup = true; // Show the toast notification to the user; ToastNotificationManager.CreateToastNotifier().Show(toast); _ProgressToast = toast; }
// 次のキャッシュダウンロードを試行 // ダウンロード用の本数 private async Task TryNextCacheRequestedVideoDownload() { if (State != CacheManagerState.Running) { return; } NicoVideoCacheRequest nextDownloadItem = null; using (var releaser = await _CacheRequestProcessingLock.LockAsync()) { while (CanAddDownloadLine) { if (_DownloadOperations.Count == 0) { nextDownloadItem = _CacheDownloadPendingVideos .FirstOrDefault(); } if (nextDownloadItem == null) { break; } if (_DownloadOperations.Any(x => x.RawVideoId == nextDownloadItem.RawVideoId && x.Quality == nextDownloadItem.Quality)) { _CacheDownloadPendingVideos.Remove(nextDownloadItem); break; } Debug.WriteLine($"キャッシュ準備を開始: {nextDownloadItem.RawVideoId} {nextDownloadItem.Quality}"); // 動画ダウンロードURLを取得 var nicoVideo = new NicoVideo(nextDownloadItem.RawVideoId, _HohoemaApp.ContentProvider, _HohoemaApp.NiconicoContext, _HohoemaApp.CacheManager); var videoInfo = await _HohoemaApp.ContentProvider.GetNicoVideoInfo(nextDownloadItem.RawVideoId); // DownloadSessionを保持して、再生完了時にDisposeさせる必要がある var downloadSession = await nicoVideo.CreateVideoStreamingSession(nextDownloadItem.Quality, forceDownload : true); var uri = await downloadSession.GetDownloadUrlAndSetupDonwloadSession(); var downloader = new BackgroundDownloader() { TransferGroup = _NicoCacheVideoBGTransferGroup }; downloader.SuccessToastNotification = MakeSuccessToastNotification(videoInfo); downloader.FailureToastNotification = MakeFailureToastNotification(videoInfo); // 保存先ファイルの確保 var filename = VideoCacheManager.MakeCacheVideoFileName( videoInfo.Title, nextDownloadItem.RawVideoId, videoInfo.MovieType, downloadSession.Quality ); var videoFolder = await _HohoemaApp.GetVideoCacheFolder(); var videoFile = await videoFolder.CreateFileAsync(filename, CreationCollisionOption.OpenIfExists); // ダウンロード操作を作成 var operation = downloader.CreateDownload(uri, videoFile); var progress = new NicoVideoCacheProgress(nextDownloadItem, operation, downloadSession); await AddDownloadOperation(progress); Debug.WriteLine($"キャッシュ準備完了: {nextDownloadItem.RawVideoId} {nextDownloadItem.Quality}"); // ダウンロードを開始 /* * if (Helpers.ApiContractHelper.IsFallCreatorsUpdateAvailable) * { * operation.IsRandomAccessRequired = true; * } */ var action = operation.StartAsync(); action.Progress = OnDownloadProgress; var task = action.AsTask(); TaskIdToCacheProgress.Add(task.Id, progress); var _ = task.ContinueWith(OnDownloadCompleted); VideoCacheStateChanged?.Invoke(this, new VideoCacheStateChangedEventArgs() { CacheState = NicoVideoCacheState.Downloading, Request = progress }); Debug.WriteLine($"キャッシュ開始: {nextDownloadItem.RawVideoId} {nextDownloadItem.Quality}"); SendUpdatableToastWithProgress(videoInfo.Title, nextDownloadItem); // DL作業を作成できたらDL待ちリストから削除 _CacheDownloadPendingVideos.Remove(nextDownloadItem); await SaveDownloadRequestItems(); } } }
public async Task RestoreRequestCache(NicoVideoCacheRequest req) { VideoFileCreatedAt = req.RequestAt; await RequestCache(); }
private async void _NiconicoMediaManager_VideoCacheStateChanged(object sender, NicoVideoCacheRequest request, NicoVideoCacheState state) { if (this.RawVideoId == request.RawVideoId) { var divided = GetDividedQualityNicoVideo(request.Quality); // divided.CacheState = state; Debug.WriteLine($"{request.RawVideoId}<{request.Quality}>: {state.ToString()}"); // update Cached time await divided.GetCacheFile(); if (state != NicoVideoCacheState.NotCacheRequested) { var requestAt = request.RequestAt; foreach (var div in GetAllQuality()) { if (div.VideoFileCreatedAt > requestAt) { requestAt = div.VideoFileCreatedAt; } } await HohoemaApp.UIDispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { CachedAt = requestAt; }); } } }