diff --git a/YouTubeScraper/YouTube.API.Test/DashManifestTest.cs b/YouTubeScraper/YouTube.API.Test/DashManifestTest.cs index 3ace573..02d1c99 100644 --- a/YouTubeScraper/YouTube.API.Test/DashManifestTest.cs +++ b/YouTubeScraper/YouTube.API.Test/DashManifestTest.cs @@ -17,7 +17,7 @@ namespace YouTube.API.Test public void ValidManifestTest() { YouTubeService service = new YouTubeService(); - IReadOnlyList manifests = service.DashManifests.List("VC5-YkjMHuw").Execute(); + IReadOnlyList manifests = service.DashManifests.List("NkGbcQwWxqk").Execute(); foreach (var i in manifests) Console.WriteLine(i.Label); Assert.IsNotNull(manifests); diff --git a/YouTubeScraper/YouTube.API.Test/WatchLaterTest.cs b/YouTubeScraper/YouTube.API.Test/WatchLaterTest.cs new file mode 100644 index 0000000..1dc2c89 --- /dev/null +++ b/YouTubeScraper/YouTube.API.Test/WatchLaterTest.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Google.Apis.Auth.OAuth2; +using Google.Apis.Services; +using Google.Apis.YouTube.v3.Data; +using Newtonsoft.Json; +using NUnit.Framework; +using YouTube.Authorization; +using YouTube.Resources; + +namespace YouTube.API.Test +{ + public class WatchLaterTest + { + const string testVideoId = "NkGbcQwWxqk"; + YouTubeService service; + + [SetUp] + public void Setup() + { + var task = AuthorizationHelpers.ExchangeToken(new ClientSecrets + { + ClientId = "1096685398208-u95rcpkqb4e1ijfmb8jdq3jsg37l8igv.apps.googleusercontent.com", + ClientSecret = "IU5bbdjwvmx8ttJoXQ7e6JWd" + }, "4/twFMhT4xSaAxls-rEayp8MxFI2Oy0knUdDbAXKnfyMkbDHaNyqhV6uM"); + task.Wait(); + + UserCredential credential = task.Result; + service = new YouTubeService(new BaseClientService.Initializer + { + HttpClientInitializer = credential, + ApplicationName = "FoxTube" + }); + } + + [Test] + public void AddVideoTest() + { + WatchLaterResource.InsertRequest request = service.WatchLater.Insert(testVideoId, "snippet"); + PlaylistItem item = request.Execute(); + Console.WriteLine(JsonConvert.SerializeObject(item)); + Assert.IsNotNull(item); + } + + [Test] + public void DeleteVideoTest() + { + WatchLaterResource.DeleteRequest request = service.WatchLater.Delete(testVideoId); + request.Execute(); + Assert.Pass(); + } + } +} diff --git a/YouTubeScraper/YouTubeScraper/Authorization/AuthorizationHelpers.cs b/YouTubeScraper/YouTubeScraper/Authorization/AuthorizationHelpers.cs index f7a82f1..398132a 100644 --- a/YouTubeScraper/YouTubeScraper/Authorization/AuthorizationHelpers.cs +++ b/YouTubeScraper/YouTubeScraper/Authorization/AuthorizationHelpers.cs @@ -62,7 +62,7 @@ namespace YouTube.Authorization AuthorizationCodeFlow authorizationCodeFlow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer() { ClientSecrets = clientSecrets, - Scopes = responseData.scope.Split(' ') + Scopes = responseData.scope.ToString().Split(' ') }); return new UserCredential(authorizationCodeFlow, "user", tokenResponse); diff --git a/YouTubeScraper/YouTubeScraper/Resources/WatchLaterResource.cs b/YouTubeScraper/YouTubeScraper/Resources/WatchLaterResource.cs index 6d3aa17..b96009a 100644 --- a/YouTubeScraper/YouTubeScraper/Resources/WatchLaterResource.cs +++ b/YouTubeScraper/YouTubeScraper/Resources/WatchLaterResource.cs @@ -1,28 +1,109 @@ -using System; +using AngleSharp.Html.Dom; +using AngleSharp.Html.Parser; +using Google.Apis.Http; +using Google.Apis.Services; +using Google.Apis.YouTube.v3; +using Google.Apis.YouTube.v3.Data; +using System; using System.Collections.Generic; -using System.Text; +using System.Linq; +using System.Net.Http; +using System.Text.RegularExpressions; +using System.Threading.Tasks; namespace YouTube.Resources { public class WatchLaterResource { - public class ListRequest { } - public class InsertRequest { } - public class DeleteRequest { } + IClientService Service { get; } + + public WatchLaterResource(IClientService service) => + Service = service; public ListRequest List() { return new ListRequest(); } - public InsertRequest Insert(string videoId) - { - return new InsertRequest(); - } + public InsertRequest Insert(string videoId, string part) => + new InsertRequest(Service, videoId, part); - public DeleteRequest Delete(string videoId) + public DeleteRequest Delete(string videoId) => + new DeleteRequest(Service, videoId); + + public class ListRequest { } + public class InsertRequest { - return new DeleteRequest(); + IClientService Service { get; set; } + public string Id { get; set; } + public string Part { get; set; } + + public InsertRequest(IClientService service, string videoId, string part) + { + Service = service; + Id = videoId; + Part = part; + } + + public async Task ExecuteAsync() + { + PlaylistItem playlist = new PlaylistItem + { + Snippet = new PlaylistItemSnippet + { + PlaylistId = "WL", + ResourceId = new ResourceId + { + VideoId = Id, + Kind = "youtube#video" + } + } + }; + PlaylistItemsResource.InsertRequest request = (Service as YouTubeService).PlaylistItems.Insert(playlist, Part); + + return await request.ExecuteAsync(); + } + + public PlaylistItem Execute() + { + Task task = ExecuteAsync(); + task.Wait(); + return task.Result; + } + } + public class DeleteRequest + { + IClientService Service { get; set; } + public string Id { get; set; } + + public DeleteRequest(IClientService service, string videoId) + { + Service = service; + Id = videoId; + } + + public async Task ExecuteAsync() + { + ConfigurableHttpClient client = Service.HttpClient; + string data = await client.GetStringAsync($"https://youtube.com/watch?v={Id}&disable_polymer=true&bpctr=9999999999&hl=en"); + string plid = Regex.Match(data, @"(?<=plid=).?\w+").Value; + IHtmlDocument html = await new HtmlParser().ParseDocumentAsync(data); + string sessionToken = html.GetElementsByTagName("input").FirstOrDefault(i => i.GetAttribute("name") == "session_token")?.GetAttribute("value"); + Dictionary body = new Dictionary + { + { "video_ids", Id }, + { "full_list_id", "WL" }, + { "plid", plid }, + { "session_token", sessionToken } + }; + HttpResponseMessage response = await client.PostAsync("https://www.youtube.com/playlist_video_ajax?action_delete_from_playlist=1", new FormUrlEncodedContent(body)); + string responseStr = await response.Content.ReadAsStringAsync(); + if (!responseStr.Contains("SUCCESS")) + throw new Exception(responseStr); + } + + public void Execute() => + ExecuteAsync().Wait(); } } } diff --git a/YouTubeScraper/YouTubeScraper/YouTubeService.cs b/YouTubeScraper/YouTubeScraper/YouTubeService.cs index 27b4808..c8df870 100644 --- a/YouTubeScraper/YouTubeScraper/YouTubeService.cs +++ b/YouTubeScraper/YouTubeScraper/YouTubeService.cs @@ -8,13 +8,10 @@ namespace YouTube public VideoPlaybackResource VideoPlayback => new VideoPlaybackResource(this); public new CaptionsResource Captions => new CaptionsResource(this); public HistoryResource History { get; } - public WatchLaterResource WatchLater { get; } - // TODO: Add Activities override for recomendations and subscriptions and implementation of cc retrieval + public WatchLaterResource WatchLater => new WatchLaterResource(this); + // TODO: Add Activities override for recomendations and subscriptions - public YouTubeService() : base() - { - - } + public YouTubeService() : base() { } public YouTubeService(Initializer initializer) : base(initializer) { } }