public Place TransformTextPlaceToPlace(GoogleTextSearchPlace gtp) { IPhotosService photoService = new GooglePhotosService(); Place place = new Place() { Id = gtp.PlaceId, Name = gtp.Name, Categories = gtp.Categories, Rating = gtp.Rating, Location = gtp.Geometry.Location.Latitude.ToString() + " " + gtp.Geometry.Location.Longitude.ToString(), Address = gtp.Address ?? "", Icon = gtp.Icon ?? "", OpenNow = gtp.OpenHours?.OpenNow ?? false }; if (gtp.GooglePhotos == null) { place.PhotoReference = place.Icon; place.Photo = place.Icon; } else { place.PhotoReference = gtp.GooglePhotos[0].PhotoReference; place.Photo = photoService.GetImageByReference(place.PhotoReference, "100", "80"); } return(place); }
public TestBase(ITestOutputHelper output) { var configuration = new ConfigurationBuilder() .AddJsonFile($"appsettings.Test.json", optional: false, reloadOnChange: true) .AddUserSecrets <TestBase>() //for local testing .AddEnvironmentVariables() //for CI testing .Build(); //initiate ServiceCollection w/logging var services = new ServiceCollection() .AddSingleton <IConfiguration>(configuration) .AddXUnitLogging(output); _logger = ApplicationLogging.LoggerFactory.CreateLogger <TestBase>(); //add services services.AddGooglePhotos(); services.AddSingleton <DiskCacheService>(); //retrieve services var serviceProvider = services.BuildServiceProvider(); _googlePhotosSvc = serviceProvider.GetRequiredService <GooglePhotosService>(); _diskCacheSvc = serviceProvider.GetRequiredService <DiskCacheService>(); }
public DetailedPlace TransformDetailedPlaceToPlace(DetailedGooglePlace dgp, Place place) { IPhotosService photo = new GooglePhotosService(); DetailedPlace detPlace = new DetailedPlace() { Id = dgp.PlaceId, Name = dgp.Name, Categories = dgp.Categories, Rating = dgp.Rating, Location = dgp.Geometry.Location.Latitude.ToString() + " " + dgp.Geometry.Location.Longitude.ToString(), Address = place.Address, Icon = place.Icon, OpenNow = dgp.OpenHours?.OpenNow ?? false, PriceLevel = place.PriceLevel, PriceString = place.PriceString, Reviews = dgp.Reviews, PhoneNumber = dgp.PhoneNumber, WebSite = dgp.WebSite, Photo = photo.GetImageByReference(place.PhotoReference, "184", "400") }; if (detPlace.Reviews != null) { detPlace.Author = detPlace.Reviews.FirstOrDefault().ReviewAuthor; detPlace.Comment = detPlace.Reviews.FirstOrDefault().ReviewText; } return(detPlace); }
const string _testFolder = "c:/temp/GooglePhotos/";//local folder of test media files public MyBackgroundService(ILogger <MyBackgroundService> logger, IHostApplicationLifetime appLifetime, GooglePhotosService googlePhotosSvc) { _logger = logger; _appLifetime = appLifetime; _googlePhotosSvc = googlePhotosSvc; }
public GoogleFile File(GooglePhotosService service, DiscretePicasaAlbum picasaAlbum, Picasa.Photo picasaPhoto) { return(new GoogleFile(new Path(mainPrefix, new string[] { PicasaExtensions.VirtualDirectoryName(service), PicasaExtensions.VirtualDirectoryName(picasaAlbum), PicasaExtensions.VirtualFileName(picasaPhoto) }, this))); }
public AlbumsQuery(GooglePhotosService service) { this.service = service; AlbumQuery albumsQuery = new AlbumQuery(PicasaQuery.CreatePicasaUri("default")); albumsQuery.ExtraParameters = "imgmax=d"; PicasaFeed albumsFeed = service.PicasaService.Query(albumsQuery); picasaAlbums = albumsFeed.Entries.OfType <PicasaEntry> ().ToArray(); }
public ContentQuery(GooglePhotosService service, DiscretePicasaAlbum album) { this.service = service; this.album = album; Picasa.Album picasaAlbum = new Picasa.Album(); picasaAlbum.AtomEntry = album.Entry; PhotoQuery picturesQuery = new PhotoQuery(PicasaQuery.CreatePicasaUri("default", picasaAlbum.Id)); picturesQuery.ExtraParameters = "imgmax=d"; PicasaFeed picturesFeed = service.PicasaService.Query(picturesQuery); picasaPictures = picturesFeed.Entries.OfType <PicasaEntry> ().ToArray(); }
public Duplicates(IConsole console, DiskCacheService diskCacheSvc, GooglePhotosService googlePhotosSvc) : base(console, diskCacheSvc, googlePhotosSvc) { }
public Download(IConsole console, DiskCacheService diskCacheSvc, GooglePhotosService googlePhotosSvc) : base(console, diskCacheSvc, googlePhotosSvc) { }
public async override Task <int> OnExecuteAsync(CommandLineApplication app) { await base.OnExecuteAsync(app); var rootPath = Path.GetFullPath(path); _console.Write($"Checking for file(s)... "); var allFileInfos = GetFiles(path, searchPattern); var items = new List <MyMediaFileItem>(allFileInfos.Count); if (allFileInfos.IsNullOrEmpty()) { _console.WriteLine($" 0 files found at {rootPath}"); } else { var checkForUploadableFileTypes = allFileInfos.GroupBy(p => Path.GetExtension(p.Name), StringComparer.InvariantCultureIgnoreCase) .Select(g => new { Extension = g.Key, MimeType = MimeTypeMap.GetMimeType(g.Key), Count = g.Count(), TotalBytes = g.Sum(p => p.Length) }) .ToList(); _console.WriteLine($"located {allFileInfos.Count} file(s), breakdown of file types;"); _console.WriteLine(); //todo: do we also analyse the files with ImageSharp/Exif? var headers = new[] { new ColumnHeader("File Extension"), new ColumnHeader("Mime Type"), new ColumnHeader("Count", Alignment.Right), new ColumnHeader("Size (MB)", Alignment.Right), new ColumnHeader("Status") }; var table = new Table(headers) { Config = TableConfiguration.Markdown() }; foreach (var f in checkForUploadableFileTypes.OrderBy(p => p.Extension)) { var status = string.Empty; if (!GooglePhotosService.IsFileUploadableByExtension(f.Extension)) { status = "Unsupported file extension, will not be uploaded."; } table.AddRow(f.Extension, f.MimeType, f.Count, f.TotalBytes.GetSizeInMB().ToString("0.0"), status); } //below summary row breaks the progress bar somehow //table.AddRow(string.Empty, string.Empty, allFileInfos.Count, allFileInfos.Sum(p => p.Length.GetSizeInMB()).ToString("0.0"), string.Empty); Console.Write(table.ToString()); _console.WriteLine(); //add all uploadable files into a new collection foreach (var fileInfo in allFileInfos) { if (GooglePhotosService.IsFileUploadable(fileInfo.FullName)) { items.Add(new MyMediaFileItem { fileInfo = fileInfo }); } } } if (items.IsNullOrEmpty()) { _console.WriteLine($"{items.Count} uploadable file(s)"); return(0); } { //extract album information from the folder structure (if requested) _console.WriteLine($"{items.Count} file(s) to be uploaded;"); _console.WriteLine(); var headers = new[] { new ColumnHeader("Relative Path"), new ColumnHeader("Size (KB)", Alignment.Right), new ColumnHeader("Album(s)") }; var table = new Table(headers) { Config = TableConfiguration.Markdown() }; foreach (var item in items) { item.relPath = GetRelPath(rootPath, item.fileInfo); item.albums = GetAlbums(item); table.AddRow(item.relPath, item.fileInfo.Length.GetSizeInKB().ToString("#,###,###"), string.Join(", ", item.albums)); } Console.Write(table.ToString()); _console.WriteLine(); } string[] GetAlbums(MyMediaFileItem item) { var albums = item.relPath.Substring(0, item.relPath.LastIndexOf(item.fileInfo.Name)); if (albums.StartsWith(Path.DirectorySeparatorChar)) { albums = albums.Substring(1); } if (albums.EndsWith(Path.DirectorySeparatorChar)) { albums = albums.Substring(0, albums.Length - 1); } var myAlbums = albums.Split(Path.DirectorySeparatorChar); return(myAlbums); } //note: if we are uploading a crazy amount of data ProgressBar only supports int for ticks, so may break :/ var totalBytes = items.Sum(p => p.fileInfo.Length); if (totalBytes > int.MaxValue) { throw new Exception($"Unable to upload more than {((long)int.MaxValue).GetSizeInMB()} in one session!"); } var totalKBytes = totalBytes.GetSizeInKB(); if (!AutoConfirm && !Prompt.GetYesNo($"Hit (Y)es to upload {items.Count} files, {totalBytes.GetSizeInMB():###,###} MB...", false, ConsoleColor.Cyan)) { return(0); } else { _console.WriteLine($"Now uploading {items.Count} files, {totalBytes.GetSizeInMB():###,###} MB..."); } var dtStart = DateTime.UtcNow; var estimatedDuration = TimeSpan.FromMilliseconds(items.Count * 2_000);//set gu-estimatedDuration pbar = new ProgressBar((int)totalBytes, $"Uploading {items.Count} media item(s)...", pbarOptions) { EstimatedDuration = estimatedDuration }; //do we upload an assign to library(and albums) as we progress? //or //do we upload all and get the uploadTokens, then assign to the library(and albums) in a second step? //...which gives the user to bomb out if a file isn't successfully uploaded? var uploadedFileCount = 0; var uploadedTotalBytes = 0; foreach (var item in items) { var str = $"{item.fileInfo.Name} : 0 of {(int)item.fileInfo.Length.GetSizeInKB()} Kb"; childPBar = pbar.Spawn((int)item.fileInfo.Length, str, childPbarOptions); //todo: pass Action or Func for a callback instead of raising an event? var uploadToken = await _googlePhotosSvc.UploadMediaAsync(item.fileInfo.FullName /*, callback: child.Tick()*/); if (!string.IsNullOrWhiteSpace(uploadToken)) { item.uploadToken = uploadToken; } else { Debugger.Break(); //todo: how to handle upload failure here? } childPBar.Dispose(); uploadedFileCount++; uploadedTotalBytes += (int)item.fileInfo.Length; pbar.Tick(uploadedTotalBytes, $"Uploaded {uploadedFileCount} of {items.Count}"); //if (Interlocked.Read(ref iteration) % 25 == 0) { var tsTaken = DateTime.UtcNow.Subtract(dtStart).TotalMilliseconds; var timePerCombination = tsTaken / uploadedFileCount; pbar.EstimatedDuration = TimeSpan.FromMilliseconds((items.Count - uploadedFileCount) * timePerCombination); } } pbar.Dispose(); //album duplicate checking needs to happen first var requiredAlbumTitles = items.SelectMany(p => p.albums).Distinct(StringComparer.OrdinalIgnoreCase).Where(p => !string.IsNullOrWhiteSpace(p)).ToList(); //allAlbums = await _diskCacheSvc.GetAsync($"albums.json", () => _googlePhotosSvc.GetAlbumsAsync()); allAlbums = await _googlePhotosSvc.GetAlbumsAsync(); if (!requiredAlbumTitles.IsNullOrEmpty()) { DoDuplicateAlbumsExist(); } var dAlbums = await GetOrCreateAlbums(); if (dAlbums is null) { return(1); } _console.Write($"Adding {items.Count} media item(s) to your library..."); var uploadItems = items.Select(p => (p.uploadToken, p.fileInfo.Name)).ToList(); var res = await _googlePhotosSvc.AddMediaItemsAsync(uploadItems); if (res is object) { _console.WriteLine($" done! :)"); //iterate over results and assign the MediaItem object to our collection foreach (var newMediaItem in res.newMediaItemResults) { var item = items.FirstOrDefault(p => p.uploadToken == newMediaItem.uploadToken); if (item is null) { throw new Exception("could this happen?"); } if (newMediaItem.status is object && newMediaItem.status.message == "Success") { item.mediaItem = newMediaItem.mediaItem; } else { Debugger.Break(); //todo: handle error? } } //todo: delete local files (if required) //todo: delete empty folders? if (deleteLocal) { foreach (var item in items.Where(p => p.mediaItem is object)) { _console.Write($"Deleting '{item.fileInfo.FullName}'..."); File.Delete(item.fileInfo.FullName);//todo: try...catch here? _console.WriteLine($" deleted!"); } } if (dAlbums.Count > 0) { _console.WriteLine($"Adding media item(s) to albums..."); //todo: put progress bar here? var table = new Table("Album Name", "Status") { Config = TableConfiguration.Markdown() }; foreach (var kvp in dAlbums) { var ids = items.Where(p => p.albums.Contains(kvp.Value.title, StringComparer.OrdinalIgnoreCase)).Select(p => p.mediaItem.id).ToList(); if (await _googlePhotosSvc.AddMediaItemsToAlbumAsync(kvp.Value.id, ids)) { table.AddRow(kvp.Value.title, $"{ids.Count} media item(s) added"); } else { Debugger.Break(); } } Console.Write(table.ToString()); _console.WriteLine(); } _console.WriteLine($"Upload completed, exiting."); } else { _console.WriteLine($" failed :("); } //todo: now handle albums //todo: do we add media items to local cache here? return(0); bool DoDuplicateAlbumsExist() { var duplicateAlbumsByTitle = GetAlbumDuplicates(allAlbums); //album titles in google photos don't need to be unique, but we can't assign photos to an existing album //if duplicate titles exist that match one of our required album titles... if (duplicateAlbumsByTitle.Count > 0 && duplicateAlbumsByTitle.Any(p => requiredAlbumTitles.Contains(p.title, StringComparer.OrdinalIgnoreCase))) { _console.WriteLine($"Duplicate album titles present, unable to assign media item(s) to albums."); foreach (var album in duplicateAlbumsByTitle) { _console.WriteLine($"{album.title}"); } _console.WriteLine($"Please rename or merge the above albums to continue."); return(false); } return(true); } async Task <Dictionary <string, Album> > GetOrCreateAlbums() { //great there are no duplicate titles, lets get/create the missing albums var d = new Dictionary <string, Album>(); foreach (var title in requiredAlbumTitles) { var album = allAlbums.FirstOrDefault(p => p.title.Equals(title, StringComparison.OrdinalIgnoreCase)); if (album is null) { album = await _googlePhotosSvc.CreateAlbumAsync(title); } d.Add(title, album); } return(d); } }
public Upload(IConsole console, DiskCacheService diskCacheSvc, GooglePhotosService googlePhotosSvc) : base(console, diskCacheSvc, googlePhotosSvc) { _googlePhotosSvc.UploadProgressEvent += _googlePhotosSvc_UploadProgressEvent; }
public static void AddService(GooglePhotosService service) { Instance.Services [PicasaExtensions.VirtualDirectoryName(service)] = service; }
static async Task Main(string[] args) { if (new[] { _user, _clientId, _clientSecret }.Any(p => string.IsNullOrWhiteSpace(p))) { Console.WriteLine("Please populate authentication details to continue..."); Debugger.Break(); return; } if (!Directory.Exists(_testFolder)) { Console.WriteLine($"Cannot find folder '{_testFolder}'"); Debugger.Break(); return; } //1) new-up some basic logging (if using appsettings.json you could load logging configuration from there) //var configuration = new ConfigurationBuilder().Build(); var loggerFactory = LoggerFactory.Create(builder => { //builder.AddConfiguration(configuration.GetSection("Logging")).AddDebug().AddConsole(); }); var logger = loggerFactory.CreateLogger <GooglePhotosService>(); //2) create a configuration object var options = new GooglePhotosOptions { User = _user, ClientId = _clientId, ClientSecret = _clientSecret, //FileDataStoreFullPathOverride = _testFolder, Scopes = new[] { GooglePhotosScope.Access, GooglePhotosScope.Sharing },//Access+Sharing == full access }; //3) (Optional) display local OAuth 2.0 JSON file(s); var path = options.FileDataStoreFullPathOverride is null ? options.FileDataStoreFullPathDefault : options.FileDataStoreFullPathOverride; Console.WriteLine($"{nameof(options.FileDataStoreFullPathOverride)}:\t{path}"); var files = Directory.GetFiles(path); if (files.Length == 0) { Console.WriteLine($"\t- n/a this is probably the first time we have authenticated..."); } else { Console.WriteLine($"Files;"); foreach (var file in files) { Console.WriteLine($"\t- {Path.GetFileName(file)}"); } } //4) create a single HttpClient, this will be efficiently re-used by GooglePhotosService var handler = new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate }; var client = new HttpClient(handler) { BaseAddress = new Uri(options.BaseAddress) }; //5) new-up the GooglePhotosService passing in the previous references (in lieu of dependency injection) _googlePhotosSvc = new GooglePhotosService(logger, Options.Create(options), client); //6) log-in if (!await _googlePhotosSvc.LoginAsync()) { throw new Exception($"login failed!"); } //get existing/create new album var albumTitle = $"{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss}-{Guid.NewGuid()}";//make-up a random title var album = await _googlePhotosSvc.GetOrCreateAlbumAsync(albumTitle); if (album is null) { throw new Exception("album creation failed!"); } Console.WriteLine($"{nameof(album)} '{album.title}' id is '{album.id}'"); //upload single media item and assign to album var mediaItem = await _googlePhotosSvc.UploadSingle($"{_testFolder}test1.jpg", album.id); if (mediaItem is null) { throw new Exception("media item upload failed!"); } Console.WriteLine($"{nameof(mediaItem)} '{mediaItem.mediaItem.filename}' id is '{mediaItem.mediaItem.id}'"); //retrieve all media items in the album var albumMediaItems = await _googlePhotosSvc.GetMediaItemsByAlbumAsync(album.id); if (albumMediaItems is null) { throw new Exception("retrieve media items by album id failed!"); } var i = 1; foreach (var item in albumMediaItems) { Console.WriteLine($"{i}\t{item.filename}\t{item.mediaMetadata.width}x{item.mediaMetadata.height}"); i++; } }
public Logout(IConsole console, DiskCacheService diskCacheSvc, GooglePhotosService googlePhotosSvc) : base(console, diskCacheSvc, googlePhotosSvc) { }
public Place TransformAPINerabyPlaceToPlace(GooglePlace gp) { IPhotosService photoService = new GooglePhotosService(); var gplace = gp; Place place = new Place() { Id = gp.PlaceId, Name = gp.Name, Categories = gp.Categories ?? new List <string>(), Rating = gp.Rating, Location = gp.Geometry.Location.Latitude.ToString() + " " + gp.Geometry.Location.Longitude.ToString(), Address = gp.Address ?? "", Icon = gp.Icon, OpenNow = gp.OpenHours?.OpenNow ?? false }; if (gp.GooglePhotos == null) { place.PhotoReference = place.Icon; place.Photo = place.Icon; } else { place.PhotoReference = gp.GooglePhotos[0].PhotoReference; place.Photo = photoService.GetImageByReference(place.PhotoReference, "100", "80"); } if (gp.PriceLevel.ToString() != "") { place.PriceLevel = gp.PriceLevel.ToString(); } else { place.PriceLevel = "5"; } switch (place.PriceLevel) { case "0": place.PriceString = "free"; break; case "1": place.PriceString = "cheap"; break; case "2": place.PriceString = "moderate"; break; case "3": place.PriceString = "expensive"; break; case "4": place.PriceString = "very expensive"; break; case "5": place.PriceString = "unknown"; break; } return(place); }
public Program(IConsole console, GooglePhotosService googlePhotosSvc) { _console = console; _googlePhotosSvc = googlePhotosSvc; }
public MediaItems(IConsole console, DiskCacheService diskCacheSvc, GooglePhotosService googlePhotosSvc) : base(console, diskCacheSvc, googlePhotosSvc) { }