diff --git a/VOffline/Models/Storage/AlbumWithPhoto.cs b/VOffline/Models/Storage/AlbumWithPhoto.cs new file mode 100644 index 0000000..5429b34 --- /dev/null +++ b/VOffline/Models/Storage/AlbumWithPhoto.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using VkNet.Model; +using VkNet.Model.Attachments; + +namespace VOffline.Models.Storage +{ + public class AlbumWithPhoto + { + public PhotoAlbum Album { get; } + public IReadOnlyList Photo { get; } + + public AlbumWithPhoto(PhotoAlbum album, IReadOnlyList photo) + { + Album = album; + Photo = photo + .OrderBy(a => a.CreateTime) + .ToList(); + } + + public AlbumWithPhoto(Album album, IReadOnlyList photo) + { + this.Album = new PhotoAlbum + { + Id = album.Id.Value, + Title = album.Title, + Description = album.Description, + Created = album.CreateTime, + ThumbId = album.Thumb?.Id, + OwnerId = album.OwnerId, + Updated = album.UpdateTime + }; + this.Photo = photo + .OrderBy(a => a.CreateTime) + .ToList(); + } + + public AlbumWithPhoto(IReadOnlyList photo) + { + this.Album = new PhotoAlbum + { + Id = long.MinValue, + Title = "__default", + Description = "Photos without album", + Created = DateTime.MinValue + }; + this.Photo = photo + .OrderBy(a => a.CreateTime) + .ToList(); + } + + + } +} \ No newline at end of file diff --git a/VOffline/Models/Storage/Download.cs b/VOffline/Models/Storage/Download.cs index 2a21601..5828a4c 100644 --- a/VOffline/Models/Storage/Download.cs +++ b/VOffline/Models/Storage/Download.cs @@ -5,7 +5,6 @@ using System.Threading; using System.Threading.Tasks; using RestSharp; using VOffline.Services; -using VOffline.Services.Handlers; namespace VOffline.Models.Storage { diff --git a/VOffline/Models/Storage/NetworkException.cs b/VOffline/Models/Storage/NetworkException.cs index f7011ca..57dddce 100644 --- a/VOffline/Models/Storage/NetworkException.cs +++ b/VOffline/Models/Storage/NetworkException.cs @@ -1,6 +1,6 @@ using System; -namespace VOffline.Services +namespace VOffline.Models.Storage { public class NetworkException : ApplicationException { diff --git a/VOffline/Program.cs b/VOffline/Program.cs index 5473d5a..476e1ee 100644 --- a/VOffline/Program.cs +++ b/VOffline/Program.cs @@ -69,6 +69,8 @@ namespace VOffline serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddTransient(provider => LogManager.GetLogger(Assembly.GetEntryAssembly(), typeof(Program))); diff --git a/VOffline/Models/Storage/Extensions.cs b/VOffline/Services/Extensions.cs similarity index 61% rename from VOffline/Models/Storage/Extensions.cs rename to VOffline/Services/Extensions.cs index 1504a5e..369f4aa 100644 --- a/VOffline/Models/Storage/Extensions.cs +++ b/VOffline/Services/Extensions.cs @@ -1,5 +1,8 @@ -using System.Net; +using System.Collections.Generic; +using System.Net; +using log4net; using RestSharp; +using VOffline.Models.Storage; namespace VOffline.Services { @@ -22,5 +25,16 @@ namespace VOffline.Services throw new NetworkException($"Null response bytes"); } } + + public static void AddUnique(this HashSet result, IEnumerable items, ILog log) + { + foreach (var item in items) + { + if (!result.Add(item)) + { + log.Warn($"Duplicate item [{item}]"); + } + } + } } } \ No newline at end of file diff --git a/VOffline/Services/Handlers/AlbumHandler.cs b/VOffline/Services/Handlers/AlbumHandler.cs new file mode 100644 index 0000000..ae70813 --- /dev/null +++ b/VOffline/Services/Handlers/AlbumHandler.cs @@ -0,0 +1,43 @@ +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using log4net; +using VOffline.Models.Storage; +using VOffline.Services.Storage; + +namespace VOffline.Services.Handlers +{ + public class AlbumHandler : HandlerBase + { + private readonly AttachmentProcessor attachmentProcessor; + + public AlbumHandler(FilesystemTools filesystemTools, AttachmentProcessor attachmentProcessor) : base(filesystemTools) + { + this.attachmentProcessor = attachmentProcessor; + } + + public override async Task ProcessInternal(AlbumWithPhoto albumWithPhoto, DirectoryInfo workDir, CancellationToken token, ILog log) + { + if (!string.IsNullOrWhiteSpace(albumWithPhoto.Album.Description)) + { + var text = filesystemTools.CreateFile(workDir, $"__description.txt", CreateMode.MergeWithExisting); + File.WriteAllText(text.FullName, albumWithPhoto.Album.Description); + } + + if (albumWithPhoto.Album.ThumbId != null) + { + var cover = albumWithPhoto.Photo.FirstOrDefault(x => x.Id == albumWithPhoto.Album.ThumbId); + if (cover != null) + { + await attachmentProcessor.ProcessAttachment(cover, -1, workDir, token, log); + } + } + + var attachmentTasks = albumWithPhoto.Photo.Select((a, i) => attachmentProcessor.ProcessAttachment(a, i, workDir, token, log)); + await Task.WhenAll(attachmentTasks); + } + + public override DirectoryInfo GetWorkingDirectory(AlbumWithPhoto albumWithPhoto, DirectoryInfo parentDir) => filesystemTools.CreateSubdir(parentDir, $"{albumWithPhoto.Album.Title}", CreateMode.MergeWithExisting); + } +} \ No newline at end of file diff --git a/VOffline/Services/Handlers/AudioHandler.cs b/VOffline/Services/Handlers/AudioHandler.cs index 680f017..4bd96e7 100644 --- a/VOffline/Services/Handlers/AudioHandler.cs +++ b/VOffline/Services/Handlers/AudioHandler.cs @@ -3,34 +3,32 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using log4net; -using VkNet; using VOffline.Models.Storage; using VOffline.Services.Storage; using VOffline.Services.Vk; -using VOffline.Services.VkNetHacks; namespace VOffline.Services.Handlers { public class AudioHandler : HandlerBase { - private readonly VkApi vkApi; + private readonly VkApiUtils vkApiUtils; private readonly PlaylistHandler playlistHandler; - public AudioHandler(VkApi vkApi, FilesystemTools filesystemTools, PlaylistHandler playlistHandler) : base(filesystemTools) + public AudioHandler(VkApiUtils vkApiUtils, FilesystemTools filesystemTools, PlaylistHandler playlistHandler) : base(filesystemTools) { - this.vkApi = vkApi; + this.vkApiUtils = vkApiUtils; this.playlistHandler = playlistHandler; } public override async Task ProcessInternal(long id, DirectoryInfo workDir, CancellationToken token, ILog log) { - var vkPlaylists = await vkApi.Audio.GetAllPlaylistsAsync(id, token, log); + var vkPlaylists = await vkApiUtils.GetAllPagesAsync(vkApiUtils.Playlists(id), 200, token, log); log.Debug($"Audio: {vkPlaylists.Count} playlists"); - var expandTasks = vkPlaylists.Select(p => vkApi.Audio.ExpandPlaylist(p, token, log)); + var expandTasks = vkPlaylists.Select(p => vkApiUtils.ExpandPlaylist(p, token, log)); var playlists = await Task.WhenAll(expandTasks); log.Debug($"Audio: {playlists.Sum(p => p.Audio.Count)} in {playlists.Length} playlists"); - var allAudios = await vkApi.Audio.GetAllAudios(id, token, log); + var allAudios = await vkApiUtils.GetAllPagesAsync(vkApiUtils.Audios(id), long.MaxValue, token, log); var audioInPlaylists = playlists .SelectMany(p => p.Audio.Select(t => t.Id)) .ToHashSet(); diff --git a/VOffline/Services/Handlers/CommentsHandler.cs b/VOffline/Services/Handlers/CommentsHandler.cs index bfa0707..8923783 100644 --- a/VOffline/Services/Handlers/CommentsHandler.cs +++ b/VOffline/Services/Handlers/CommentsHandler.cs @@ -3,29 +3,26 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using log4net; -using VkNet; using VkNet.Model.Attachments; -using VOffline.Models.Storage; using VOffline.Services.Storage; using VOffline.Services.Vk; -using VOffline.Services.VkNetHacks; namespace VOffline.Services.Handlers { public class CommentsHandler : HandlerBase { - private readonly VkApi vkApi; + private readonly VkApiUtils vkApiUtils; private readonly CommentHandler commentHandler; - public CommentsHandler(VkApi vkApi, FilesystemTools filesystemTools, CommentHandler commentHandler) : base(filesystemTools) + public CommentsHandler(VkApiUtils vkApiUtils, FilesystemTools filesystemTools, CommentHandler commentHandler) : base(filesystemTools) { - this.vkApi = vkApi; + this.vkApiUtils = vkApiUtils; this.commentHandler = commentHandler; } public override async Task ProcessInternal(Post post, DirectoryInfo workDir, CancellationToken token, ILog log) { - var allComments = await vkApi.Wall.GetAllCommentsAsync(post.OwnerId.Value, post.Id.Value, token, log); + var allComments = await vkApiUtils.GetAllPagesAsync(vkApiUtils.Comments(post), 100, token, log); log.Debug($"Post {post.Id} has {allComments.Count} comments"); var byDate = allComments .OrderBy(c => c.Date) @@ -37,6 +34,6 @@ namespace VOffline.Services.Handlers await Task.WhenAll(commentTasks); } - public override DirectoryInfo GetWorkingDirectory(Post post, DirectoryInfo parentDir) => filesystemTools.CreateSubdir(parentDir, "comments", CreateMode.MergeWithExisting); + public override DirectoryInfo GetWorkingDirectory(Post post, DirectoryInfo parentDir) => filesystemTools.CreateSubdir(parentDir, "Comments", CreateMode.MergeWithExisting); } } \ No newline at end of file diff --git a/VOffline/Services/Handlers/PhotoHandler.cs b/VOffline/Services/Handlers/PhotoHandler.cs new file mode 100644 index 0000000..308f15a --- /dev/null +++ b/VOffline/Services/Handlers/PhotoHandler.cs @@ -0,0 +1,52 @@ +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using log4net; +using VOffline.Models.Storage; +using VOffline.Services.Storage; +using VOffline.Services.Vk; + +namespace VOffline.Services.Handlers +{ + public class PhotoHandler : HandlerBase + { + private readonly VkApiUtils vkApiUtils; + private readonly AlbumHandler albumHandler; + + public PhotoHandler(VkApiUtils vkApiUtils, AlbumHandler albumHandler, FilesystemTools filesystemTools) : base(filesystemTools) + { + this.vkApiUtils = vkApiUtils; + this.albumHandler = albumHandler; + } + + public override async Task ProcessInternal(long id, DirectoryInfo workDir, CancellationToken token, ILog log) + { + var vkAlbums = await vkApiUtils.GetAllPagesAsync(vkApiUtils.PhotoAlbums(id), int.MaxValue, token, log); + var expandTasks = vkAlbums.Select(album => vkApiUtils.ExpandAlbum(album, token, log)); + var albums = await Task.WhenAll(expandTasks); + + var allPhotos = await vkApiUtils.GetAllPagesAsync(vkApiUtils.Photos(id), 100, token, log); + var photosInAlbums = albums + .SelectMany(awp => awp.Photo.Select(photo => photo.Id.Value)) + .ToHashSet(); + var uniquePhotos = allPhotos + .Where(photo => !photosInAlbums.Contains(photo.Id.Value)) + .ToList(); + var defaultAlbum = new AlbumWithPhoto(uniquePhotos); + + var allAlbums = albums.ToList(); + if (defaultAlbum.Photo.Any()) + { + allAlbums.Add(defaultAlbum); + } + + var allTasks = allAlbums + .OrderBy(p => p.Album.Created) + .Select(p => albumHandler.Process(p, workDir, token, log)); + await Task.WhenAll(allTasks); + } + + public override DirectoryInfo GetWorkingDirectory(long id, DirectoryInfo parentDir) => filesystemTools.CreateSubdir(parentDir, "Photo", CreateMode.MergeWithExisting); + } +} \ No newline at end of file diff --git a/VOffline/Services/Handlers/WallHandler.cs b/VOffline/Services/Handlers/WallHandler.cs index 66f5512..2f742a3 100644 --- a/VOffline/Services/Handlers/WallHandler.cs +++ b/VOffline/Services/Handlers/WallHandler.cs @@ -3,27 +3,25 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using log4net; -using VkNet; using VOffline.Services.Storage; using VOffline.Services.Vk; -using VOffline.Services.VkNetHacks; namespace VOffline.Services.Handlers { public class WallHandler : HandlerBase { - private readonly VkApi vkApi; + private readonly VkApiUtils vkApiUtils; private readonly PostHandler postHandler; - public WallHandler(VkApi vkApi, FilesystemTools filesystemTools, PostHandler postHandler):base(filesystemTools) + public WallHandler(VkApiUtils vkApiUtils, FilesystemTools filesystemTools, PostHandler postHandler):base(filesystemTools) { - this.vkApi = vkApi; + this.vkApiUtils = vkApiUtils; this.postHandler = postHandler; } public override async Task ProcessInternal(long id, DirectoryInfo workDir, CancellationToken token, ILog log) { - var allPosts = await vkApi.Wall.GetAllPostsAsync(id, token, log); + var allPosts = await vkApiUtils.GetAllPagesAsync(vkApiUtils.Posts(id), 100, token, log); log.Debug($"Wall has {allPosts.Count} posts"); var allTasks = allPosts .OrderBy(x => x.Date) diff --git a/VOffline/Services/Logic.cs b/VOffline/Services/Logic.cs index 2e4a78a..7899bf8 100644 --- a/VOffline/Services/Logic.cs +++ b/VOffline/Services/Logic.cs @@ -28,8 +28,9 @@ namespace VOffline.Services private readonly DownloadQueueProvider queueProvider; private readonly WallHandler wallHandler; private readonly AudioHandler audioHandler; + private readonly PhotoHandler photoHandler; - public Logic(TokenMagic tokenMagic, VkApi vkApi, VkApiUtils vkApiUtils, BackgroundDownloader downloader, FilesystemTools filesystemTools, DownloadQueueProvider queueProvider, WallHandler wallHandler, AudioHandler audioHandler, IOptionsSnapshot settings) + public Logic(TokenMagic tokenMagic, VkApi vkApi, VkApiUtils vkApiUtils, BackgroundDownloader downloader, FilesystemTools filesystemTools, DownloadQueueProvider queueProvider, WallHandler wallHandler, AudioHandler audioHandler, PhotoHandler photoHandler, IOptionsSnapshot settings) { this.settings = settings.Value; this.tokenMagic = tokenMagic; @@ -40,6 +41,7 @@ namespace VOffline.Services this.queueProvider = queueProvider; this.wallHandler = wallHandler; this.audioHandler = audioHandler; + this.photoHandler = photoHandler; } public async Task Run(CancellationToken token, ILog log) @@ -101,6 +103,9 @@ namespace VOffline.Services case Mode.Audio: await audioHandler.Process(id, dir, token, log); break; + case Mode.Photos: + await photoHandler.Process(id, dir, token, log); + break; case Mode.All: throw new ArgumentOutOfRangeException(nameof(mode), mode, "This mode should have been replaced before processing"); default: diff --git a/VOffline/Services/Storage/AttachmentProcessor.cs b/VOffline/Services/Storage/AttachmentProcessor.cs index 2be9a1f..c715448 100644 --- a/VOffline/Services/Storage/AttachmentProcessor.cs +++ b/VOffline/Services/Storage/AttachmentProcessor.cs @@ -1,5 +1,4 @@ -using System; -using System.IO; +using System.IO; using System.Threading; using System.Threading.Tasks; using log4net; @@ -7,20 +6,21 @@ using Newtonsoft.Json; using VkNet; using VkNet.Model; using VkNet.Model.Attachments; -using VOffline.Services.Storage; using VOffline.Services.Vk; -namespace VOffline.Services +namespace VOffline.Services.Storage { public class AttachmentProcessor { private readonly VkApi vkApi; + private readonly VkApiUtils vkApiUtils; private readonly FilesystemTools filesystemTools; private readonly DownloadQueueProvider downloadQueueProvider; - public AttachmentProcessor(VkApi vkApi, FilesystemTools filesystemTools, DownloadQueueProvider downloadQueueProvider) + public AttachmentProcessor(VkApi vkApi, VkApiUtils vkApiUtils, FilesystemTools filesystemTools, DownloadQueueProvider downloadQueueProvider) { this.vkApi = vkApi; + this.vkApiUtils = vkApiUtils; this.filesystemTools = filesystemTools; this.downloadQueueProvider = downloadQueueProvider; } @@ -53,16 +53,25 @@ namespace VOffline.Services await downloadQueueProvider.EnqueueAll(link.ToDownloads(number, filesystemTools, workDir, log), token); await link.SaveHumanReadableText(number, filesystemTools, workDir, token, log); break; - - case VkNet.Model.Attachments.Video video: // vlc же как-то получает MP4 поток. а что делать с видосами на хостингах? + case VkNet.Model.Attachments.AudioPlaylist audioPlaylist: + var playlistWithAudio = await vkApiUtils.ExpandPlaylist(audioPlaylist, token, log); + //await playlistHandler.Process(p, workDir, token, log) + log.Warn($"TODO: playlist attachment"); + break; + case VkNet.Model.Attachments.Album album: + var albumWithPhoto = await vkApiUtils.ExpandAlbum(album, token, log); + //await albumHandler.Process(p, workDir, token, log) + log.Warn($"TODO: photoalbum attachment"); + break; + case VkNet.Model.Attachments.Video video: + await downloadQueueProvider.EnqueueAll(video.ToDownloads(number, filesystemTools, workDir, log), token); + break; case VkNet.Model.Attachments.Note note: // note и page похожи case VkNet.Model.Attachments.Page page: - case VkNet.Model.Attachments.Album album: // это к фотографиям - - case VkNet.Model.Attachments.AudioPlaylist audioPlaylist: // это к аудиозаписям. так вообще бывает? + // остальное похоже на хлам case VkNet.Model.Attachments.ApplicationContent applicationContent: case VkNet.Model.Attachments.AudioMessage audioMessage: diff --git a/VOffline/Services/Storage/BackgroundDownloader.cs b/VOffline/Services/Storage/BackgroundDownloader.cs index 59be652..f5abeae 100644 --- a/VOffline/Services/Storage/BackgroundDownloader.cs +++ b/VOffline/Services/Storage/BackgroundDownloader.cs @@ -27,7 +27,7 @@ namespace VOffline.Services.Storage IDownload item; int success; - for(success=0; (item = await GetItem(token)) != null; success++) + for (success = 0; (item = await GetItem(token)) != null;) { // TODO: add second queue, semaphore and support for retries try diff --git a/VOffline/Services/Storage/DownloadQueueProvider.cs b/VOffline/Services/Storage/DownloadQueueProvider.cs index ac8fdf0..a5bf6ae 100644 --- a/VOffline/Services/Storage/DownloadQueueProvider.cs +++ b/VOffline/Services/Storage/DownloadQueueProvider.cs @@ -6,7 +6,6 @@ using Microsoft.Extensions.Options; using Nito.AsyncEx; using VOffline.Models; using VOffline.Models.Storage; -using VOffline.Services.Vk; namespace VOffline.Services.Storage { diff --git a/VOffline/Services/Vk/AttachmentExtensions.cs b/VOffline/Services/Vk/AttachmentExtensions.cs index 7957cf6..0752cf4 100644 --- a/VOffline/Services/Vk/AttachmentExtensions.cs +++ b/VOffline/Services/Vk/AttachmentExtensions.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using log4net; using Newtonsoft.Json; using VkNet; +using VkNet.Enums.SafetyEnums; using VkNet.Model; using VkNet.Model.Attachments; using VOffline.Models.Storage; @@ -32,7 +33,7 @@ namespace VOffline.Services.Vk public static IEnumerable ToDownloads(this Photo photo, int i, FilesystemTools filesystemTools, DirectoryInfo dir, ILog log) { - // TODO: not sure about order + // TODO: not sure about ANYTHING here var url = photo.BigPhotoSrc ?? photo.Photo2560 ?? photo.PhotoSrc @@ -45,7 +46,22 @@ namespace VOffline.Services.Vk ?? photo.Photo100 ?? photo.Photo75 ?? photo.Photo50 - ?? photo.Sizes.Select(s => (square:s.Width*s.Height, size:s)).OrderByDescending(x => x.square).FirstOrDefault().size.Url; + ?? photo.Sizes + .Select(s => (square:s.Width*s.Height, size:s)) + .OrderByDescending(x => x.square) + .FirstOrDefault(s => s.square > 0) // width/height can be null + .size?.Url + ?? photo.Sizes.FirstOrDefault(s => s.Type == PhotoSizeType.W)?.Url + ?? photo.Sizes.FirstOrDefault(s => s.Type == PhotoSizeType.Z)?.Url + ?? photo.Sizes.FirstOrDefault(s => s.Type == PhotoSizeType.Y)?.Url + ?? photo.Sizes.FirstOrDefault(s => s.Type == PhotoSizeType.X)?.Url + ?? photo.Sizes.FirstOrDefault(s => s.Type == PhotoSizeType.R)?.Url + ?? photo.Sizes.FirstOrDefault(s => s.Type == PhotoSizeType.Q)?.Url + ?? photo.Sizes.FirstOrDefault(s => s.Type == PhotoSizeType.P)?.Url + ?? photo.Sizes.FirstOrDefault(s => s.Type == PhotoSizeType.O)?.Url + ?? photo.Sizes.FirstOrDefault(s => s.Type == PhotoSizeType.M)?.Url + ?? photo.Sizes.FirstOrDefault(s => s.Type == PhotoSizeType.S)?.Url + ; // TODO: i guess it's always jpeg? var ext = Path.HasExtension(url?.AbsoluteUri) ? Path.GetExtension(url?.AbsoluteUri) : ".jpg"; @@ -60,6 +76,28 @@ namespace VOffline.Services.Vk } } + public static IEnumerable ToDownloads(this Video video, int i, FilesystemTools filesystemTools, DirectoryInfo dir, ILog log) + { + var vkUrl = video.Files?.Mp4_1080 + ?? video.Files?.Mp4_720 + ?? video.Files?.Mp4_480 + ?? video.Files?.Mp4_360 + ?? video.Files?.Mp4_240; + if (vkUrl != null) + { + yield return new Download(vkUrl, dir, video.Title); + } + else if(video.Files?.External != null) + { + log.Warn($"Video {video.Id} [{video.Title}] is external"); + yield return new Download(video.Files.External, dir, video.Title); + } + else + { + log.Warn($"Video {video.Id} [{video.Title}] has no links"); + } + } + public static IEnumerable ToDownloads(this Document document, int i, FilesystemTools filesystemTools, DirectoryInfo dir, ILog log) { // TODO: looks like Title already has Extension diff --git a/VOffline/Services/Vk/VkApiUtils.cs b/VOffline/Services/Vk/VkApiUtils.cs index a9a8e68..6724c7a 100644 --- a/VOffline/Services/Vk/VkApiUtils.cs +++ b/VOffline/Services/Vk/VkApiUtils.cs @@ -1,9 +1,19 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Text.RegularExpressions; +using System.Threading; using System.Threading.Tasks; +using log4net; using VkNet; using VkNet.Enums; using VkNet.Enums.Filters; +using VkNet.Enums.SafetyEnums; +using VkNet.Model; +using VkNet.Model.Attachments; +using VkNet.Model.RequestParams; +using VkNet.Utils; +using VOffline.Models.Storage; namespace VOffline.Services.Vk { @@ -64,7 +74,169 @@ namespace VOffline.Services.Vk var group = groups[0]; return String.Join(" ", group.Name, GroupOrEmpty(" - ", group.Id.ToString(), group.ScreenName, group.Type.ToString())); } - + + public PageGetter PhotoAlbums(long id) => async (count, offset) => await vkApi.Photo.GetAlbumsAsync(new PhotoGetAlbumsParams() + { + OwnerId = id, + NeedCovers = true, + NeedSystem = true, + Count = (uint)count, + Offset = (uint)offset + }); + + public PageGetter Photos(long id) => async (count, offset) => await vkApi.Photo.GetAllAsync(new PhotoGetAllParams() + { + OwnerId = id, + Extended = true, + SkipHidden = false, + Count = (ulong)count, + Offset = (ulong)offset + }); + + public PageGetter PhotosInAlbum(long id, PhotoAlbumType albumIdType) => async (count, offset) => await vkApi.Photo.GetAsync(new PhotoGetParams() + { + OwnerId = id, + AlbumId = albumIdType, + Extended = true, + PhotoSizes = true, + Count = (ulong)count, + Offset = (ulong)offset + }); + + public PageGetter Posts(long id) => async (count, offset) => + { + var response = await vkApi.Wall.GetAsync(new WallGetParams + { + OwnerId = id, + Count = (ulong)count, + Offset = (ulong)offset + }); + return new VkCollection(response.TotalCount, response.WallPosts); + }; + + public PageGetter Playlists(long id) => async (count, offset) => await vkApi.Audio.GetPlaylistsAsync(id, (uint)count, (uint)offset); + + public PageGetter