Previously described bash script allows for uploading a file to PicasaWeb only. With the following (simplified) Perl script one can upload pictures as well as upload with metadata (title description and tags) or create/list albums:
#!/usr/bin/perl use strict; use LWP::UserAgent; use Getopt::Long; use File::MimeInfo; use XML::LibXML; my $AlbumId ="6170354040646469009"; my $profileID="default"; my $Action = 'u'; ## x| u | c | l (default action is Upload) my $entryTitle = ''; my $entryDescription = ''; my $entryKeywords = ''; my $ActionUpload =''; my $ActionList = ''; my $ActionCreate = ''; my $ActionXload = ''; my $ImageFile = ''; my $dummyReq=''; GetOptions("xload" => \$ActionXload, "upload" => \$ActionUpload, "list" => \$ActionList, "create" => \$ActionCreate, "title=s" => \$entryTitle, "description=s" => \$entryDescription, "keywords=s" => \$entryKeywords, "file=s" => \$ImageFile, "album=s" => \$AlbumId, ## UploadFile to Album ) ; ## Determine action: if ( $ActionUpload ) {$Action = 'u'} elsif ( $ActionList ) { $Action = 'l'} elsif ( $ActionCreate ) { $Action = 'c'} elsif ( $ActionXload ) { $Action = 'x'}
OAuth 2.0 authorization is handled with Python script
oauth2picasa.py
. The script is an adapted/copy-pasted fragment
of code borrowd from
picasawebsync
:
### Authenticate with external script (oauth2picasa.py): my $ACCESS_TOKEN=`oauth2picasa.py`; chomp($ACCESS_TOKEN); print STDERR "*** AccessToken: $ACCESS_TOKEN [AlbumId: $AlbumId]\n"; my $req ; my $blog_entry ; my $picasawebURL = "https://picasaweb.google.com/data/feed/api/user/$profileID"; if ( $Action eq 'c' ) {## Action: create album $req = HTTP::Request->new(POST => $picasawebURL ); $req->header( 'Content-Type' => 'application/atom+xml' ); $blog_entry = "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:media='http://search.yahoo.com/mrss/' xmlns:gphoto='http://schemas.google.com/photos/2007'>" . "<title type='text'>$entryTitle</title>" . "<summary type='text'>$entryDescription</summary>" . "<media:group><media:keywords>$entryKeywords</media:keywords></media:group>" . "<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/photos/2007#album'></category></entry>"; $req->content($blog_entry); } elsif ( $Action eq 'l' ) {## Action: list albums $req = HTTP::Request->new(GET => $picasawebURL ); } elsif ( $Action eq 'u' ) {## Action: Upload 1 photo w/o metadata my $mimeType = mimetype($ImageFile); ## https://developers.google.com/picasa-web/docs/2.0/developers_guide_protocol $req = HTTP::Request->new(POST => "$picasawebURL/albumid/$AlbumId" ); $req->header( 'Content-Type' => "$mimeType" ); $req->header( 'Slug' => "$ImageFile" ); ## http://www.perlmonks.org/?node_id=131584 open(FILE, $ImageFile); $req->content(join('',<FILE>)); close(FILE); }
To upload the binary image data along with its metadata, use MIME content
type "multipart/related"; send photo metadata in one part of the POST
body (Content-Type: application/atom+xml
),
and binary-encoded image data in another part.
This is the preferred approach according to
Picasa Web Albums Data API
Picasa Web Albums Data API
elsif ( $Action eq 'x' ) {## Action: Upload 1 photo with metadata # https://groups.google.com/forum/#!topic/google-picasa-data-api/2qRfP0EIFhk my $mimeType = mimetype($ImageFile); $req = HTTP::Request->new(POST => "$picasawebURL/albumid/$AlbumId" ); $req->header( 'Content-Type' => "multipart/related" ); open(FILE, $ImageFile); my $add_photo_metadata = "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:media='http://search.yahoo.com/mrss/'>" . "<title type='text'>$entryTitle</title>" . "<summary type='text'>$entryDescription</summary>" . "<media:group><media:keywords>$entryKeywords</media:keywords></media:group>" . "<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/photos/2007#photo'></category></entry>"; my $add_photo_data = join('',<FILE>); close(FILE); ## http://www.perlmonks.org/?node_id=131584 $req->add_part(HTTP::Message->new(['Content-Type' => 'application/atom+xml'], $add_photo_metadata)); $req->add_part(HTTP::Message->new(['Content-Type' => "$mimeType"], $add_photo_data)); } $req->header( 'Authorization' => "Bearer $ACCESS_TOKEN" ); $req->header( 'GData-Version' => '2' ); ## ### ### my $res ; my $ua = LWP::UserAgent->new; $res = $ua->request($req); if ($res->is_success) { my $decoded_response = $res->decoded_content; print "*** OK *** $decoded_response\n"; }
Usage:
Upload with metadata
picasaweb.pl -xload -title PICTURE-TITLE -descr DESCRIPTION \ -keywords 'TAG1,TAG2' -file FILE.jpg -album ALBUMID
Upload w/o metadata:
picasaweb.pl -upload -file FILE.jpg -album 12345
Create album:
picasaweb.pl -create -title ALBUM-TITLE -descr DESCRIPTION \ -keywords 'TAG1,TAG2'
List album:
picasaweb.pl -list
Source code:
picasaweb.pl
GoogleCL przestało działać, bo Google przestało obsługiwać wersję OAuth 1.0. Ponadto, wygląda na to, że dostosowanie tego użytecznego narzędzia do wersji OAuth 2.0 bynajmniej nie jest trywialne na co wskazują liczne (ale do tej pory bezskuteczne) prośby i wołania o aktualizację GoogleCL, które można znaleźć w Internecie.
Ponieważ poszukiwania
w miarę podobnego zamiennika zakończyły
się niepowodzeniem, nie pozostało nic innego zmajstrować coś samodzielnie.
Autoryzację OAuth 2.0 mam już opanową -- obsługuje ją Pythonowy skrypt oauth2picasa.py
.
(Skrypt jest (zapożyczonym) fragmentem z projektu
picasawebsync
).
Wystarczyło
dorobić następujący prosty skrypt Perlowy (por. także:
Publishing
a blog post):
#!/usr/bin/perl # *** Wyslanie posta na blogger.com *** use strict; use LWP::UserAgent; use XML::LibXML; use Getopt::Long; my $profileID="default"; my $blogID = '1928418645181504144'; # Identyfikator bloga my $blog_entry ; ## Na wypadek gdy ktoś ma kilka blogów moża podać na któr ## ma być wysłany post używając opcji -blog GetOptions( "blog=s" => \$blogID, "post=s" => \$blog_entry) ; if ( $blog_entry eq '' ) { print STDERR "*** USAGE: $0 -b blog -p message (-b is optional) ***\n" } ## sprawdź czy post jest well formed: my $parser = XML::LibXML->new(); eval {my $res_ = $parser->parse_string($blog_entry) }; if ($@) { die "*** Error parsing post message! \n"; } my $ACCESS_TOKEN=`oauth2blogger.py`; # pobierz ACCESS_TOKEN print STDERR "*** AccessToken: $ACCESS_TOKEN ***\n"; my $req = HTTP::Request->new( POST => "https://www.blogger.com/feeds/$blogID/posts/default"); $req->header( 'Content-Type' => 'application/atom+xml' ); $req->header( 'Authorization' => "Bearer $ACCESS_TOKEN" ); $req->header( 'GData-Version' => '2' ); $req->content($blog_entry); my $ua = LWP::UserAgent->new; my $res = $ua->request($req); # Jeżeli coś jest nie tak poniższe drukuje verbatim: # http://www.perlmonks.org/bare/?node_id=464442 # $ua->prepare_request($req); print($req->as_string); exit ; if ($res->is_success) { my $decoded_response = $res->decoded_content; print STDERR "*** OK *** $decoded_response\n"; } else { die $res->status_line; }
Wykorzystanie:
perl blogger_upload.pl -p 'treść-posta'
Treść posta musi być oczywiście w formacie xHTML i zawierać
się wewnątrz elementu content
, który z kolei
jest wewnątrz elementu entry
.
Element entry
zawiera także title
określający tytuł posta, oraz elementy category
zawierające
tagi. Przykładem może być coś takiego:
<entry xmlns='http://www.w3.org/2005/Atom'> <title type='text'>Marriage!</title> <content type='xhtml'> <div xmlns="http://www.w3.org/1999/xhtml"> <p>Mr. Darcy has proposed marriage to me!</p> <p>He is the last man on earth I would ever desire to marry.</p> <p>Whatever shall I do?</p> </div> </content> <category scheme="http://www.blogger.com/atom/ns#" term="marriage" /> <category scheme="http://www.blogger.com/atom/ns#" term="Mr. Darcy" /> </entry>
Opisany skrypt jest tutaj:
blogger_upload.pl
.