Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update the timestamp on a photo or add the time zone on Google+ Photos?

Using Picasa Web API I retrieve a photo from my Google+ photo album and attempt to change the timestamp (the time was wrong on my phone, so trying to fix it):

var service = new PicasaService("exampleCo-exampleApp-1");
service.setUserCredentials("uid", "pwd");
AlbumQuery query = new AlbumQuery(PicasaQuery.CreatePicasaUri("default"));

PicasaFeed feed = service.Query(query);
var entry = (PicasaEntry)feed.Entries.SingleOrDefault(f => f.Title.Text == "Trip to Italy - ALL");

var ac = new AlbumAccessor(entry);

var photoQuery = new PhotoQuery(PicasaQuery.CreatePicasaUri("default", ac.Id));
PicasaFeed photoFeed = service.Query(photoQuery);

PicasaEntry picasaEntry = photoFeed.Entries[0];

ulong timestamp = Convert.ToUInt64(picasaEntry.GetPhotoExtensionValue("timestamp"));

// deduct 9 hours
DateTime dt = FromUnixTime(pa.Timestamp).AddHours(-9);
picasaEntry.SetPhotoExtensionValue("timestamp", Convert.ToString(ToUnixTime(dt)));

var updatedEntry = (PicasaEntry) picasaEntry.Update();

Unfortunately, while the .Update method succeeds, the timestamp doesn't change. I've also tried to change the timezone of the photo (e.g. same thing user does manually like this http://i.imgur.com/pxYSi9S.png).

Am I missing something simple? Is there another way to accomplish the same thing? I would also settle for changing the timezone of the photo.

like image 798
AngryHacker Avatar asked Apr 24 '15 16:04

AngryHacker


People also ask

Can you change the timestamp on Google Photos?

Though you can view your photos on any mobile device – may it be an Android or an iOS mobile – editing the timestamp is still not possible. If you want to change the time and date of the photos, you need to use your laptop to access the Timestamp Editor App.

Can you edit the timestamp on a photo?

Select the photo you want to change, right-click the photo and choose Properties. Click the Details tab. Under Date Taken you can simply enter the date or click the calendar icon.

How do you change the time and place a picture?

Right-click (or Control+click) a photo in an entry and use the contextual menu to apply date/time/location information to the entry.


1 Answers

I had a go at your problem myself, so let me share my assessment of the situation here:

Google seems to take the displayed meta information from the Exif tags of the image. Although setting the Exif tags seems possible when looking at the .net API (I did even decompile the Google assemblies, to make sure I got everything right), Google simply does not reembed them into the picture when issuing an update.

So my approach is to download all images, change their Exif info and re-upload them (like nemesv suggested). Unfortunately Google does strip all downloaded files of their Exif tags (thx big G!) and replaces them by artificial ones (e.g. application name "Google") and null values (creation time = null). Generating Exif info from scratch in .net is hackish at best (one has to forcibly construct instances of System.Drawing.Imaging.PropertyItem [has internal constructor, can be done] and parameterize them correctly). But, as I have done something like this (taken Exif info from existing pic, regenerated pic and re-added Exif to new pic) in an imaging module of an application at my main job, I deem this approach feasible.

Here is some proof of concept code for you. It does not go all the way (a complete solution should read the dates of the existing picasa entries and cache them in a list to reapply them on the downloaded images), but it does cover the tricky parts.

private void button1_Click(object sender, EventArgs e)
{
    var service = new PicasaService("exampleCo-exampleApp-1");
    service.setUserCredentials("[email protected]", "-secret-");
    AlbumQuery query = new AlbumQuery(PicasaQuery.CreatePicasaUri("default"));


    PicasaFeed feed = service.Query(query);
    var entry = (PicasaEntry)feed.Entries.SingleOrDefault(f => f.Title.Text == "Testalbum");

    var ac = new AlbumAccessor(entry);

    var photoQuery = new PhotoQuery(PicasaQuery.CreatePicasaUri("default", ac.Id));
    PicasaFeed photoFeed = service.Query(photoQuery);

    DirectoryInfo srcdir = Directory.CreateDirectory("C:\\Temp\\Testalbum");
    DownloadAllPhotos("C:\\Temp\\Testalbum", photoFeed.Entries);

    foreach (PicasaEntry oldentry in photoFeed.Entries)
    {
        oldentry.Delete();
    }

    DirectoryInfo tgtdir = Directory.CreateDirectory("C:\\Temp\\Converted");
    foreach (FileInfo imagefile in srcdir.EnumerateFiles())
    {
        Image img = Image.FromFile(imagefile.FullName);

        PropertyItem PiDtOrig = null;
        try
        {
            PiDtOrig = img.GetPropertyItem(0x9003); // id 0x9003 is "DateTimeOriginal"
        }
        catch (System.ArgumentException ex) // this exception is thrown when PropertyItem does not exist 
        {
            PiDtOrig = NewPropertyItem();
            PiDtOrig.Id = 0x9003;
            PiDtOrig.Type = 7;
            PiDtOrig.Len = 4;
        }

        PiDtOrig.Value = BitConverter.GetBytes(DateTimeToInt(DateTime.Now));
        img.SetPropertyItem(PiDtOrig);
        string ConvImgName = tgtdir.FullName + "\\" + imagefile.Name;
        img.Save(ConvImgName);

        //ExifTagCollection exif = new ExifTagCollection(img);
        //Debug.WriteLine(exif);

        Uri postUri = new Uri(PicasaQuery.CreatePicasaUri("[email protected]", ac.Id));
        FileStream fileStream = imagefile.OpenRead();

        PicasaEntry newentry = (PicasaEntry)service.Insert(postUri, fileStream, "image/jpeg", ConvImgName);

        fileStream.Close();
        fileStream.Dispose();
    }
}

private PropertyItem NewPropertyItem()
{
    Type t = typeof (PropertyItem);
    ConstructorInfo ctor = t.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)[0];
    Object o = ctor.Invoke(new Object[] { });
    return (PropertyItem) o;
}

private int DateTimeToInt(DateTime theDate)
{
    return (int)(theDate.Date - new DateTime(1900, 1, 1)).TotalDays + 2;
}

// taken from https://codethis.wordpress.com/2008/11/ and modified for this example
static void DownloadAllPhotos(string DirectoryName, AtomEntryCollection photoList)
{
    DirectoryInfo dirInfo = Directory.CreateDirectory(DirectoryName);

    int photoNum = 1;
    foreach (AtomEntry photo in photoList)
    {
        HttpWebRequest photoRequest = WebRequest.Create(photo.Content.AbsoluteUri) as HttpWebRequest;
        HttpWebResponse photoResponse = photoRequest.GetResponse() as
           HttpWebResponse;

        BufferedStream bufferedStream = new BufferedStream(
           photoResponse.GetResponseStream(), 1024);
        BinaryReader reader = new BinaryReader(bufferedStream);

        FileStream imgOut = File.Create(dirInfo.FullName + "\\image" +
           photoNum++ + ".jpg");
        BinaryWriter writer = new BinaryWriter(imgOut);

        int bytesRead = 1;
        byte[] buffer = new byte[1024];
        while (bytesRead > 0)
        {
            bytesRead = reader.Read(buffer, 0, buffer.Length);
            writer.Write(buffer, 0, bytesRead);
        }
        reader.Close();
        reader.Dispose();
        writer.Flush();
        writer.Close();
        writer.Dispose();
    }
}

Please bear with me for posting somewhat dirty code, but I have whipped this together really quick.

You will find two commented out lines in th code above, that uses some Exif classes I have found somwhere on the net some time ago. As they where too much code do disclose here directly, I have uploaded them to Pastebin:

http://pastebin.com/pkZMVZ9i

Although they only allow for Exif reading, they nonetheless could be of use to you, when trying to find the proper PropertyItem conents for other Exif tags.

As I do not know where this code came from any more, please anyone write a comment if he/she does, so I can add this information here as well.

like image 59
Peter Brennan Avatar answered Sep 21 '22 18:09

Peter Brennan