Flickr-Upload-1.6/0000755000175000017500000000000012757102637014304 5ustar sseverinsseverinFlickr-Upload-1.6/META.json0000664000175000017500000000212712757102637015731 0ustar sseverinsseverin{ "abstract" : "Module for uploading images to flickr.com", "author" : [ "Christophe Beauregard " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.04, CPAN::Meta::Converter version 2.120351", "license" : [ "unknown" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Flickr-Upload", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "Flickr::API" : "1.09", "Getopt::Long" : "1", "HTTP::Request::Common" : "1", "LWP::UserAgent" : "1", "Net::OAuth" : "0.28", "Pod::Usage" : "1", "XML::Simple" : "2" } } }, "release_status" : "stable", "version" : "1.6" } Flickr-Upload-1.6/ChangeLog0000644000175000017500000011702512757102637016064 0ustar sseverinsseverincommit 345907a047ed70dd37aadbde48cbd2cc112c5aff (HEAD, master) Author: Kensaku Yamaguchi AuthorDate: Sun Aug 21 10:24:59 2016 +0900 Commit: Steven N. Severinghaus CommitDate: Tue Aug 23 13:17:54 2016 -0400 accept array as photo argument commit c55f30130e39f257252e9b1e9e8d7e0b8b1e7ffb (origin/master, origin/HEAD) Author: Steven N. Severinghaus AuthorDate: Tue Aug 23 12:53:32 2016 -0400 Commit: Steven N. Severinghaus CommitDate: Tue Aug 23 12:53:32 2016 -0400 Support OAuth in flickr_upload; prep for new release commit 3f08f5b0c1a66084ea482992aeee2b3df8d780d6 Author: Kensaku Yamaguchi AuthorDate: Sun Aug 21 10:23:47 2016 +0900 Commit: Steven N. Severinghaus CommitDate: Sun Aug 21 14:37:37 2016 -0400 support OAuth authentication commit eb54480161560ab1284db412637233e5de293a5d Author: Steven N. Severinghaus AuthorDate: Mon Feb 1 17:44:44 2016 -0500 Commit: Steven N. Severinghaus CommitDate: Mon Feb 1 17:44:44 2016 -0500 Version bump to 1.51 commit 79a516abd69caff53d38dd948b11dd488d31a63c Author: Steven N. Severinghaus AuthorDate: Mon Feb 1 12:10:57 2016 -0500 Commit: Steven N. Severinghaus CommitDate: Mon Feb 1 12:10:57 2016 -0500 More helpful error messages commit c7cd939aff2f0f8b35ea2532db226fe5c38a0d3c Author: Steven N. Severinghaus AuthorDate: Sun Jan 31 13:07:27 2016 -0500 Commit: Steven N. Severinghaus CommitDate: Sun Jan 31 13:07:27 2016 -0500 Fixed and clarified documentation for --report commit b88983f3538f70ddca4d8cab8995a167b508e02e Author: Steven N. Severinghaus AuthorDate: Sun Jan 31 12:47:35 2016 -0500 Commit: Steven N. Severinghaus CommitDate: Sun Jan 31 12:47:35 2016 -0500 Updated more documentation URLs that are now https commit fd950c0d2055da816fd602451e6b48183df9171a Author: Steven N. Severinghaus AuthorDate: Sun Jan 31 12:44:17 2016 -0500 Commit: Steven N. Severinghaus CommitDate: Sun Jan 31 12:44:17 2016 -0500 All these URLs at flickr.com are now https commit 14f3bd08c297a95a7243db5f1ab40d36b4760c5e Author: Steven N. Severinghaus AuthorDate: Wed Jul 15 12:26:14 2015 -0400 Commit: Steven N. Severinghaus CommitDate: Wed Jul 15 12:26:14 2015 -0400 Implemented _sign_args because method in Flickr::API is private and has been renamed commit f0b3c48b031f81b28a5aa44f37a2d77ce782a3c8 Author: Steven N. Severinghaus AuthorDate: Tue Aug 19 21:11:19 2014 -0400 Commit: Steven N. Severinghaus CommitDate: Tue Aug 19 21:11:19 2014 -0400 Moved Upload.pm into lib/Flickr/ as is more customary commit 05a5186b6d588ef2e896851bada9e18fe40b5370 Author: Steven N. Severinghaus AuthorDate: Tue Aug 19 20:49:31 2014 -0400 Commit: Steven N. Severinghaus CommitDate: Tue Aug 19 20:49:31 2014 -0400 Updated dependency list in README Do we really need to maintain this separately? commit cd14e3d7de96e6c8d6eb16615100268c10478148 Author: Steven N. Severinghaus AuthorDate: Tue Aug 19 20:47:54 2014 -0400 Commit: Steven N. Severinghaus CommitDate: Tue Aug 19 20:47:54 2014 -0400 Eliminated dependency on XML::Parser::Lite::Tree I would rather use XPath, but it seems like XML::Simple is the easiest solution at this point. commit 533835ed439bd934d7092555fd0ed4fea7709bb5 Author: Steven N. Severinghaus AuthorDate: Tue Aug 19 14:04:28 2014 -0400 Commit: Steven N. Severinghaus CommitDate: Tue Aug 19 14:04:28 2014 -0400 Updated dependencies in README commit 48a82c79c350c066c941235655c239de02648e51 Author: Steven N. Severinghaus AuthorDate: Mon Aug 18 12:21:20 2014 -0400 Commit: Steven N. Severinghaus CommitDate: Mon Aug 18 12:21:20 2014 -0400 Merged in contributions by Assaf Gordon Assaf has added functionality to put the uploaded photos into a new set and to optionally reverse the order of upload. This merge is very literal so far. I have only tested that the functionality works but have not made any revisions to the code, other than a version bump. commit 01a38a57a50bd6395ddab1d6d8c2154638754a79 Author: Steven N. Severinghaus AuthorDate: Mon Aug 18 00:20:54 2014 -0400 Commit: Steven N. Severinghaus CommitDate: Mon Aug 18 00:20:54 2014 -0400 Appears to require at least Flickr::API 1.09 commit 207251a67870f816fb0d1e6037deef8cc1890503 (tag: 1.4) Author: maspotts AuthorDate: Mon Jun 30 21:37:55 2014 -0700 Commit: maspotts CommitDate: Mon Jun 30 21:37:55 2014 -0700 Update Upload.pm Bump version to 1.4. commit 9302c6ffe2b619a7da571eda46308708d8863596 Author: maspotts AuthorDate: Mon Jun 30 21:33:54 2014 -0700 Commit: maspotts CommitDate: Mon Jun 30 21:33:54 2014 -0700 Update Upload.pm Use SSL api. commit 5c210ab9bac949075c6a46d5d87718c79f3d6551 Author: Ævar Arnfjörð Bjarmason AuthorDate: Wed Oct 14 17:58:20 2009 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Wed Oct 14 17:58:20 2009 +0000 Add myself to AUTHORS/COPYRIGHT commit d9ccd247ca8b59e3e10329c6b4533c97a9ef0d30 Author: Ævar Arnfjörð Bjarmason AuthorDate: Wed Oct 14 17:50:41 2009 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Wed Oct 14 17:50:41 2009 +0000 Make --progress and --report boolean (!) switches in Getopt::Long This means --no-report and --no-progress can be specified as well as --report and --progress. commit 8d8f04c98438c938ee9894c33cfc2f4a2d0d7ae5 Author: Ævar Arnfjörð Bjarmason AuthorDate: Wed Oct 14 17:45:41 2009 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Wed Oct 14 17:45:41 2009 +0000 Add a --report switch to suppress the "Waiting for upload results" message Also add a facility to allow the default of --progress and --report to be changed via the ~/.flickrrc configuration file. commit 27a4da0cbd6b5556069d7007f692c7c888b6f325 Author: Ævar Arnfjörð Bjarmason AuthorDate: Wed Oct 14 17:32:11 2009 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Wed Oct 14 17:32:11 2009 +0000 "git-log" has been deprecated in favor of "git log" commit 6b2b1a87ccd4eabe66b28db56a2df7bf6e5f0f2b Author: Ævar Arnfjörð Bjarmason AuthorDate: Wed Oct 14 17:12:00 2009 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Wed Oct 14 17:12:00 2009 +0000 Solve RT#50493. Sometimes Flickr::Upload will die during upload due to network errors. Before it would do this: Uploading img_4289.jpg... junk '500 read failed: Connection reset by peer ' before XML element and simply die. Now it'll try again in case of errors: Uploading img_4289.jpg... Failed uploading attempt attempt 1/3, trying again. [...] Uploading img_4289.jpg... commit 77f2d561665af0cef16b6623b0e5c8ea4f929816 Author: Christophe Beauregard AuthorDate: Sat Dec 20 11:01:44 2008 -0500 Commit: Christophe Beauregard CommitDate: Sat Dec 20 11:01:44 2008 -0500 typo fix commit 687838e8b1f5a8d1d533ae10a04b7aa72be4b539 Author: Christophe Beauregard AuthorDate: Mon Oct 27 14:43:55 2008 -0400 Commit: Christophe Beauregard CommitDate: Mon Oct 27 14:43:55 2008 -0400 Incorporate part of http://svn.debian.org/wsvn/pkg-perl/trunk/libflickr-upload-perl/debian/patches/failupload.patch?op=file&rev=0&sc=0 So the failupload test works okay when there's no net access. commit f2c2c7f74fd2ad23241195c85168614eac4fe6aa Author: Christophe Beauregard AuthorDate: Wed Oct 15 06:33:17 2008 -0400 Commit: Christophe Beauregard CommitDate: Wed Oct 15 06:33:17 2008 -0400 whoo, we're a few years behind on the copyrights commit 36f2a4434770d8cbdcdbceee985fc19d4bc3f9f9 Author: Christophe Beauregard AuthorDate: Wed Oct 15 06:27:38 2008 -0400 Commit: Christophe Beauregard CommitDate: Wed Oct 15 06:27:38 2008 -0400 Merge Debian patch r26041 from Alex Muntada" : Hopefully fixed last TODO item via quilt: W: libflickr-upload-perl: manpage-has-errors-from-pod2man usr/share/man/man1/flickr_upload.1p.gz:324 commit 25738657dcf4623445f69433ae63973d1e24d4cb (tag: 1.32) Author: Christophe Beauregard AuthorDate: Wed Sep 17 20:17:52 2008 -0400 Commit: Christophe Beauregard CommitDate: Wed Sep 17 20:17:52 2008 -0400 Bump version to 1.32 commit df1eccffdc2b67943a7d5cf9660ad8a677bc729b Author: Christophe Beauregard AuthorDate: Wed Sep 17 20:06:52 2008 -0400 Commit: Christophe Beauregard CommitDate: Wed Sep 17 20:06:52 2008 -0400 Call env_proxy() on the user agent. Fixes http://rt.cpan.org/Ticket/Display.html?id=37725 commit fbee13a2b51531a6d61013b7416265af88f2e1e5 Author: Christophe Beauregard AuthorDate: Wed Sep 17 20:05:45 2008 -0400 Commit: Christophe Beauregard CommitDate: Wed Sep 17 20:05:45 2008 -0400 Minor clarification of the custom key/secret options. http://rt.cpan.org/Ticket/Display.html?id=39370 commit 85117d8c2e553fcef65702dbb7361185508088f4 Author: Christophe Beauregard AuthorDate: Wed Sep 17 20:03:35 2008 -0400 Commit: Christophe Beauregard CommitDate: Wed Sep 17 20:03:35 2008 -0400 Add a --check option to invoke the flickr.auth.checkToken API call and just generally validate the authentication information. commit 3afc55b07bc0087afe9919563ad0392c8a221c54 Author: Christophe Beauregard AuthorDate: Sat Jun 21 18:59:13 2008 -0400 Commit: Christophe Beauregard CommitDate: Sat Jun 21 18:59:13 2008 -0400 put more detail in the ChangeLog. Specifically, we'd like to see dates. commit 75d78079f595f2516a5efa4bfdada2961f0c08a3 (tag: 1.31) Author: Christophe Beauregard AuthorDate: Wed Jun 18 16:39:42 2008 -0400 Commit: Christophe Beauregard CommitDate: Wed Jun 18 16:39:42 2008 -0400 indent with tabs commit 76ca5d3bd960eedf21d9fea32fd83815f98816ec Author: Christophe Beauregard AuthorDate: Wed Jun 18 16:37:24 2008 -0400 Commit: Christophe Beauregard CommitDate: Wed Jun 18 16:37:24 2008 -0400 ticket and photo identifiers aren't necessarily numeric. Fix the async test to use 'eq' and document. commit 8ded78b619ef4696ffa27d6766d797720a0fca4e Author: Christophe Beauregard AuthorDate: Tue Jun 17 20:38:36 2008 -0400 Commit: Christophe Beauregard CommitDate: Tue Jun 17 20:38:36 2008 -0400 we use the generated META.yml, too commit bac5b6c6487b78622cea79c3b217c5f4f22fbb0d Author: Christophe Beauregard AuthorDate: Tue Jun 17 20:19:03 2008 -0400 Commit: Christophe Beauregard CommitDate: Tue Jun 17 20:19:03 2008 -0400 oops... we auto generate the ChangeLog, don't commit commit fbe6b5a03c5c6a5df4ca7266f04b89997ff60e92 Author: Christophe Beauregard AuthorDate: Tue Jun 17 20:16:37 2008 -0400 Commit: Christophe Beauregard CommitDate: Tue Jun 17 20:16:37 2008 -0400 bump version to 1.31 commit 9a7c22dc11e16ff483b01dc491f11e146683e2d4 Merge: b53a716 c87f9be Author: Christophe Beauregard AuthorDate: Tue Jun 17 20:15:14 2008 -0400 Commit: Christophe Beauregard CommitDate: Tue Jun 17 20:15:14 2008 -0400 Merge branch 'master' of git://git.nix.is/avar/pm/Flickr-Upload Conflicts: MANIFEST README Upload.pm flickr_upload t/progress.t commit b53a716f24dd0caa43f4837ac9965511e55d364d Author: Christophe Beauregard AuthorDate: Tue Jun 17 20:04:16 2008 -0400 Commit: Christophe Beauregard CommitDate: Tue Jun 17 20:04:16 2008 -0400 Now that we seem to have a more permanent home... commit 4a27eff4d9f6186e004d0e14ff18c6a30d2bbfb8 Author: Christophe Beauregard AuthorDate: Tue Jun 17 19:29:30 2008 -0400 Commit: Christophe Beauregard CommitDate: Tue Jun 17 19:29:30 2008 -0400 force an update commit 990747797c6ddcb4e8761005d5afd322111f0642 Author: Christophe Beauregard AuthorDate: Tue Jun 17 19:12:24 2008 -0400 Commit: Christophe Beauregard CommitDate: Tue Jun 17 19:12:24 2008 -0400 add note about GIT repo commit c87f9be69faff0a337e3f32e46758d97d407308b Author: Ævar Arnfjörð Bjarmason AuthorDate: Tue Jun 17 03:49:01 2008 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Tue Jun 17 03:49:01 2008 +0000 $progress->minor(0). This updates the progress bar a lot less frequently and it looks better since there isn't a '*'character jumping all over the terminal. commit 662868b31eb0b448f0951fa9da3e59ff0f1bf181 Author: Ævar Arnfjörð Bjarmason AuthorDate: Mon Jun 16 12:51:53 2008 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Mon Jun 16 12:51:53 2008 +0000 POD documentation for file_length_in_encoded_chunk Make the Content-Type regex more inclusive to handle other kinds of uploads. commit 74937457cfeeb6eed25c902191ab086262034f35 Author: Ævar Arnfjörð Bjarmason AuthorDate: Mon Jun 16 12:35:06 2008 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Mon Jun 16 12:35:06 2008 +0000 Update POD for --progress some more commit eecb5a5bd8c334229ade2cbd69fe73d8284544fe Author: Ævar Arnfjörð Bjarmason AuthorDate: Mon Jun 16 12:33:39 2008 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Mon Jun 16 12:33:39 2008 +0000 The manifest was way out of date, update it commit 83a78608a398e6310908ec5f19948ff2cd07b7a7 Author: Ævar Arnfjörð Bjarmason AuthorDate: Mon Jun 16 12:31:49 2008 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Mon Jun 16 12:31:49 2008 +0000 File upload progress stuff now works, with tests commit a60024fe5262528330a518a659982114ed215b9c Author: Ævar Arnfjörð Bjarmason AuthorDate: Mon Jun 16 11:42:25 2008 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Mon Jun 16 11:42:25 2008 +0000 Fixes these errors: t/progress............ok 1/0Can't decode multipart content at /usr/share/perl5/HTTP/Message.pm line 193. ...propagated at t/progress.t line 33. and added comment & newline preventing this: ok 3 ...........................................................ok 4 which would fail the 4th test commit d7d60c2a9228225c3a455b47457b87c400cf0f21 Author: Ævar Arnfjörð Bjarmason AuthorDate: Mon Jun 16 11:25:39 2008 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Mon Jun 16 11:25:39 2008 +0000 a working test! commit 184f10e79cb4b5463508487a59d39c306a4bd7e4 Author: Ævar Arnfjörð Bjarmason AuthorDate: Mon Jun 16 05:39:56 2008 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Mon Jun 16 05:39:56 2008 +0000 Deleted function prototypes, ->upload is a method and there's no need to call scalar on the arguments given to upload_request commit 9c509b24e425143c24f30a143b7daf72f16eb4a2 Author: Ævar Arnfjörð Bjarmason AuthorDate: Mon Jun 16 05:38:15 2008 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Mon Jun 16 05:38:15 2008 +0000 Deleted unused template code which was loading Exporter and AutoLoader without exporting anything or using autoload commit b194bd979e46c8a77f6f2cb4e667ee79ad456fed Author: Ævar Arnfjörð Bjarmason AuthorDate: Mon Jun 16 05:26:26 2008 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Mon Jun 16 05:26:26 2008 +0000 a version of my flickr upload patch on RT, currently broken while in development commit c5f6f1fd66c01bb76bbd897acf92be76e9a8d783 Author: Ævar Arnfjörð Bjarmason AuthorDate: Mon Jun 16 05:25:04 2008 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Mon Jun 16 05:25:04 2008 +0000 The beginnings of a test for the file upload parser, does nothing yet commit c5ef9a68944ed82b379f516c63c950545a8f9def Author: Ævar Arnfjörð Bjarmason AuthorDate: Mon Jun 16 03:40:18 2008 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Mon Jun 16 03:40:18 2008 +0000 Fix grammar in POD: "rarely be know to occur" "rarely been know to occur" commit 8edebaaaa7b4e420483ac287011f3eb7fcb8c75a Author: Ævar Arnfjörð Bjarmason AuthorDate: Mon Jun 16 03:21:09 2008 +0000 Commit: Ævar Arnfjörð Bjarmason CommitDate: Mon Jun 16 03:21:09 2008 +0000 1.30 from CPAN commit d469b79ff97d91e4adc9dfbbd79fa321e0b7fe32 Author: cpb AuthorDate: Sat Mar 8 08:45:19 2008 -0500 Commit: cpb CommitDate: Sat Mar 8 08:45:19 2008 -0500 *: patch from Pascal Terjan to fix http://rt.cpan.org/Ticket/Display.html?id=33750 *: also fix t/progress.t *: bump version to 1.30 commit 5b067d72b0199376eb0fbffc8bc8ad1fda3f0ec4 Author: cpb AuthorDate: Wed Jun 20 20:11:00 2007 -0400 Commit: cpb CommitDate: Wed Jun 20 20:11:00 2007 -0400 Minor documentation cleanups commit bacea3a958e964428ac04b631897c33a30209a17 Author: cpb AuthorDate: Wed Jun 20 20:10:22 2007 -0400 Commit: cpb CommitDate: Wed Jun 20 20:10:22 2007 -0400 oops, forgot to commit that. Same as last, just some build fixes. commit 93bf309e95366d1c30cee2629435abf4e96b0a6d Author: cpb AuthorDate: Wed Jun 20 20:03:57 2007 -0400 Commit: cpb CommitDate: Wed Jun 20 20:03:57 2007 -0400 Clean up the build dependencies so the generated files actually do get generated when we make a dist. commit b8f6a97156f5482508d56988348ba6e1c9cb530e Author: cpb AuthorDate: Wed Jun 20 19:55:26 2007 -0400 Commit: cpb CommitDate: Wed Jun 20 19:55:26 2007 -0400 Drop some files we're no longer using/maintaining. We can always get 'em back. commit 71d98dbd8e7284ffaff508160be566d4bdc88b9b Author: cpb AuthorDate: Wed Jun 20 19:28:27 2007 -0400 Commit: cpb CommitDate: Wed Jun 20 19:28:27 2007 -0400 Oops, not using CVS anymore. Use $Flickr::Upload::VERSION. commit f1232d66f2acffe69ed763c24e2461e0730c7a83 Author: cpb AuthorDate: Tue Jun 19 19:34:40 2007 -0400 Commit: cpb CommitDate: Tue Jun 19 19:34:40 2007 -0400 Add some documentation suggested by Mark Atwood. commit 2a64df91c32d3d6032219810daefc45032b73d5d Author: cpb AuthorDate: Mon Jun 18 19:28:29 2007 -0400 Commit: cpb CommitDate: Mon Jun 18 19:28:29 2007 -0400 CPAN doesn't like it when you remove the $VERSION from a module. So we'll just revert the whole "move into Makefile.PL" thing. commit 643895a413bfa095aa704b1217aefb2d922a8362 Author: cpb AuthorDate: Mon Jun 18 19:08:08 2007 -0400 Commit: cpb CommitDate: Mon Jun 18 19:08:08 2007 -0400 (#23) recent change to ExtUtils::MakeMaker appears to not cope with a missing META.yml... Even though it generates one anyways. commit 0f269b7a0cbdcc0a8f4e7888bd8fdce1a60aa8d5 Author: cpb AuthorDate: Mon Jun 11 20:41:06 2007 -0400 Commit: cpb CommitDate: Mon Jun 11 20:41:06 2007 -0400 bump the version commit 1206c237c13cfd371a180624c574e265558ac1ef Author: cpb AuthorDate: Mon Jun 11 20:40:42 2007 -0400 Commit: cpb CommitDate: Mon Jun 11 20:40:42 2007 -0400 Added a --option parameter so users can just add arbitrary options (like the new filtering stuff) without waiting for a new Flickr::Upload release. commit c9f103dd0c229756bcd5e6ceaa222a2ac42a5845 Author: cpb AuthorDate: Sun Jun 3 15:32:38 2007 -0400 Commit: cpb CommitDate: Sun Jun 3 15:32:38 2007 -0400 Bump revision to 1.26 Update Makefile.PL and Upload.pm to reflect switch to GIT, which means cvs2cl won't generate a ChangeLog and we need to bump the version number manually. commit b3bbd024fb5bcb162dcb782d55171dc276068f59 Author: cpb AuthorDate: Sat Jun 2 21:06:24 2007 -0400 Commit: cpb CommitDate: Sat Jun 2 21:06:24 2007 -0400 Refine the authentication documentation just a touch. commit 3f36b37a5d9399260e107a88e4a6f21d015c61fd Author: cpb AuthorDate: Wed Mar 21 01:34:04 2007 +0000 Commit: cpb CommitDate: Wed Mar 21 01:34:04 2007 +0000 Documentation: add an "EXAMPLE" section to clarify how to get started. Fix some spelling and formatting issues. commit 2d49bf3b2d93f33b481a8c32016acca214f99d58 Author: cpb AuthorDate: Mon Mar 19 00:45:15 2007 +0000 Commit: cpb CommitDate: Mon Mar 19 00:45:15 2007 +0000 oops, that's Pod::Usage commit 162150419f60a4a06d6214d3809f399575e3b03f Author: cpb AuthorDate: Mon Mar 19 00:31:42 2007 +0000 Commit: cpb CommitDate: Mon Mar 19 00:31:42 2007 +0000 http://rt.cpan.org/Ticket/Display.html?id=25521 Add Pod::Usage and Getopt::Long to dependencies commit 46a3092db4faf2d88461f0064012e50580bdef89 Author: cpb AuthorDate: Tue Dec 19 17:59:47 2006 +0000 Commit: cpb CommitDate: Tue Dec 19 17:59:47 2006 +0000 http://rt.cpan.org/Ticket/Display.html?id=23990 =Flickr::Upload::new()= takes a hashref (ala =Flickr::API=). commit 5690adae0ecedc2c4dadc155548d7ade0144197a Author: cpb AuthorDate: Fri Sep 22 00:27:23 2006 +0000 Commit: cpb CommitDate: Fri Sep 22 00:27:23 2006 +0000 upload URI has changed commit 0ffd3f92ca3c26c13c35b7d5efbd79888fa4f8c4 Author: cpb AuthorDate: Sat Jan 21 17:36:23 2006 +0000 Commit: cpb CommitDate: Sat Jan 21 17:36:23 2006 +0000 add some notes on generating tags with embedded spaces to the --tag documentation. commit 86beb003aa9ddbae9e8be0080de55088588406c9 Author: cpb AuthorDate: Thu Jan 5 23:52:05 2006 +0000 Commit: cpb CommitDate: Thu Jan 5 23:52:05 2006 +0000 _is_friend_ should be _is_family_ https://rt.cpan.org/NoAuth/Bug.html?id=16930 commit 95a2af893331e107d5301ba39de1b17863982358 Author: cpb AuthorDate: Tue Aug 30 22:43:53 2005 +0000 Commit: cpb CommitDate: Tue Aug 30 22:43:53 2005 +0000 change the test image name. Needs to go in MANIFEST... commit 88e3a12d57a3cb64b2fd340d488e0e2eccada27f Author: cpb AuthorDate: Sun Aug 28 01:05:06 2005 +0000 Commit: cpb CommitDate: Sun Aug 28 01:05:06 2005 +0000 (#11) key and secret can be specified in =~/.flickrrc=. This allows a user to get one API key, authenticate against it, and use =flickr_upload= and a bunch of personal scripts with just =~/.flickrc= for storing the authentication info. commit 4f2ac62f555e4b616518a579894604b1dfb86b88 Author: cpb AuthorDate: Sun Aug 28 00:59:17 2005 +0000 Commit: cpb CommitDate: Sun Aug 28 00:59:17 2005 +0000 (#11) change of ownership commit d2f691308e289ea9935f2baa5e7cb1f9237f465d Author: cpb AuthorDate: Sun Aug 28 00:35:55 2005 +0000 Commit: cpb CommitDate: Sun Aug 28 00:35:55 2005 +0000 (#11) some documentation cleanups commit 3783350260f87de11bfe646ac517a5c190e8a15f Author: cpb AuthorDate: Wed Aug 24 02:36:56 2005 +0000 Commit: cpb CommitDate: Wed Aug 24 02:36:56 2005 +0000 (#8) really remove refs to =thickr_upload=. commit 1dd1d3f64d1a227ea35643df047e4614fcc45722 Author: cpb AuthorDate: Wed Aug 24 02:32:58 2005 +0000 Commit: cpb CommitDate: Wed Aug 24 02:32:58 2005 +0000 (#8) switched to Debian, =cvs2cl= is called something else commit 666781f4801095233ae417dc28cc863d4af28515 Author: cpb AuthorDate: Wed Aug 24 02:31:20 2005 +0000 Commit: cpb CommitDate: Wed Aug 24 02:31:20 2005 +0000 (#8) remove some debug code, handle undef photo situation. commit 646e13fad7280da15f39eccbcc78aaf591ac138b Author: cpb AuthorDate: Wed Aug 24 02:29:52 2005 +0000 Commit: cpb CommitDate: Wed Aug 24 02:29:52 2005 +0000 (#8) first stab at getting =Flickr::Upload= working with the new authentication. *: had to completely gut the interface and inherit from =Flickr::API=. *: straight and async uploading via =flickr_upload= _works_ *: this means =Flickr::Upload::upload()= and =Flickr::Upload::check_upload()= methods are good. *: tests /Flickr-Upload/t/request.t and /Flickr-Upload/t/progress.t are _not_ passing, although the progress test is still uploading the image. commit f7d92b7749f9027fd5af5fa30eb610bc5f02cd10 Author: cpb AuthorDate: Wed Aug 24 01:57:42 2005 +0000 Commit: cpb CommitDate: Wed Aug 24 01:57:42 2005 +0000 (#8) remove =thickr_upload= for now. commit 9a5190fdf3c1f7b7df9b3378630de7a9705abcc9 Author: cpb AuthorDate: Wed Feb 16 03:16:26 2005 +0000 Commit: cpb CommitDate: Wed Feb 16 03:16:26 2005 +0000 non-blocking uploading... *: spawn the uploading in a separate process (via fork) and process the results in a pipe using Tk fileevent. *: error handling needs a bit of work *: need to manage the process id of the uploader so that we clean up when we quit. *: really need an upload manager window so we can monitor the entire queue *: also need to check for ticket completions commit db6bf06811a76752f181bd81762649d3acce8aed Author: cpb AuthorDate: Sun Feb 6 15:09:48 2005 +0000 Commit: cpb CommitDate: Sun Feb 6 15:09:48 2005 +0000 tweak ChangeLog options commit 850e525713067e3587e1ebcbdf4f3f687fcc2ef3 Author: cpb AuthorDate: Sat Feb 5 23:40:42 2005 +0000 Commit: cpb CommitDate: Sat Feb 5 23:40:42 2005 +0000 add blurb about thickr_upload commit 5bf426d1e01d7ffe456675a19f7eb650154b010b Author: cpb AuthorDate: Sat Feb 5 23:38:48 2005 +0000 Commit: cpb CommitDate: Sat Feb 5 23:38:48 2005 +0000 rename thkr_upload to thickr_upload. Long story, won't bother explaining. commit f3bd683ac3b7a7b9921484c3f996c61118b67799 Author: cpb AuthorDate: Sat Feb 5 23:35:17 2005 +0000 Commit: cpb CommitDate: Sat Feb 5 23:35:17 2005 +0000 remove the logo stuff. We'll just show some blankness. commit acb665aed11d78ca9f0dec12a0d191b01ddcd4fc Author: cpb AuthorDate: Sat Feb 5 23:16:23 2005 +0000 Commit: cpb CommitDate: Sat Feb 5 23:16:23 2005 +0000 *: remove some debug junk *: replace Tk::FileSelect with Tk::getOpenFile() so we can do multiple selection. This really streamlines things. commit 0b53ea9bcc443ec2f84b63222f8aaad92dd7e658 Author: cpb AuthorDate: Sat Feb 5 21:11:37 2005 +0000 Commit: cpb CommitDate: Sat Feb 5 21:11:37 2005 +0000 fix comment about best way to use LWP from Tk commit 0dd69bb6b25089778b3ebfeea26ac34f5fce3552 Author: cpb AuthorDate: Mon Jan 17 04:03:23 2005 +0000 Commit: cpb CommitDate: Mon Jan 17 04:03:23 2005 +0000 more churning on the uploading. Nicer cleanup. commit ae0424e12ab02b8677408ff4fa1b7a4d30d7c867 Author: cpb AuthorDate: Sun Jan 16 04:50:04 2005 +0000 Commit: cpb CommitDate: Sun Jan 16 04:50:04 2005 +0000 *: clean up preview a bit. Now properly scales images to fit, manages memory better, etc. *: make preview area a drag and drop site for photos. DnD code is a little crufty but works *: update menus a touch. Help is actually in the right place *: switch to messageBox for Help|About *: documentation *: use Tk::HTML::IO to, hopefully, do better IO for the upload. Haven't tested this and need to refine the upload process some anyways. We really want something like an "upload manager" view of what's happening. *: new photos are added at the front of the list. Just better feedback. commit 6a7db68d2466ceeacc189b318485fb5998237a8d Author: cpb AuthorDate: Sat Jan 15 05:08:53 2005 +0000 Commit: cpb CommitDate: Sat Jan 15 05:08:53 2005 +0000 added initial cut at thkr_upload, a Tk-based GUI upload client commit 2a1949baa9d2982225811852cf3de0400e74dff2 Author: cpb AuthorDate: Thu Jan 13 14:33:50 2005 +0000 Commit: cpb CommitDate: Thu Jan 13 14:33:50 2005 +0000 update documentation a touch. It's a new year, you know. commit 6680641bb776d5faad11401ce30e41c832f78e95 Author: cpb AuthorDate: Thu Jan 13 14:31:06 2005 +0000 Commit: cpb CommitDate: Thu Jan 13 14:31:06 2005 +0000 use the async option to pipeline the upload process so we wait for photoid assignments after _all_ the uploads are completed. This should reduce the overall time are multi-image batch uploads quite a bit. commit a75905f12f7e9aa148c8e9cc4a5f91596bdd9e8f Author: cpb AuthorDate: Thu Jan 13 14:16:29 2005 +0000 Commit: cpb CommitDate: Thu Jan 13 14:16:29 2005 +0000 added async.t to MANIFEST commit 609c961fb7ad0f07ddbe179d4f5994cdf79f3221 Author: cpb AuthorDate: Thu Jan 13 04:28:22 2005 +0000 Commit: cpb CommitDate: Thu Jan 13 04:28:22 2005 +0000 chunk could be undefined. commit efcaac3ad6f917abbf4c533d2c4efbea228f9d31 Author: cpb AuthorDate: Thu Jan 13 04:23:53 2005 +0000 Commit: cpb CommitDate: Thu Jan 13 04:23:53 2005 +0000 *: modified make_upload_request() to allow async and properly handle returned ticket ids. *: added check_upload() function to check status up async tickets *: added t/async.t to test/demo async functionality commit 1f764265df810e7f9e2aec524de31874d777f857 Author: cpb AuthorDate: Sat Jan 8 17:13:06 2005 +0000 Commit: cpb CommitDate: Sat Jan 8 17:13:06 2005 +0000 trim down commentary on async uploading. It _might_ be useful to such an API, given recent problems with delays in the uploading process (in something like sync conduits, for example). commit 158ab2c204d108a28a865b0fd980acd637b3d8c5 Author: cpb AuthorDate: Sat Jan 8 17:06:56 2005 +0000 Commit: cpb CommitDate: Sat Jan 8 17:06:56 2005 +0000 L<> in NAME section gets interpreted as links to a man page, leading to a synopsis like "Flickr::Upload - Upload images to the flickr.com manpage". commit 8552e7ca98801bc5a5c5a819ce60d73c5f360a0d Author: cpb AuthorDate: Tue Jan 4 00:36:04 2005 +0000 Commit: cpb CommitDate: Tue Jan 4 00:36:04 2005 +0000 Another Michael, another bug report... _Michael Parker says:_ I noticed this in the flickr_upload script distributed with the Flickr::Upload package (at least until 1.10): 'public=i' => sub { $args{is_public} = $_[1] }, 'friend=i' => sub { $args{is_public} = $_[1] }, 'family=i' => sub { $args{is_public} = $_[1] }, It looks like a cut and paste error, using is_public for all 3 rather than is_friend and is_family. commit 596972a78acf76d8fe195aa18a6d4789101777e2 Author: cpb AuthorDate: Tue Dec 14 18:03:04 2004 +0000 Commit: cpb CommitDate: Tue Dec 14 18:03:04 2004 +0000 Grrr... forgot the perl version dependency in Makefile.PL... commit 41a7415c856630609dd87a110e43d1cce2b1e794 Author: cpb AuthorDate: Tue Dec 14 00:38:27 2004 +0000 Commit: cpb CommitDate: Tue Dec 14 00:38:27 2004 +0000 Michael Stillwell pointed out that the restriction to perl >= 5.8.3 was silly. 5.6.1+ would be fine, and probably even earlier. commit ae5109e8a1024d61d68ef3c5f177fd4a9cb4f9c3 Author: cpb AuthorDate: Wed Dec 8 01:03:07 2004 +0000 Commit: cpb CommitDate: Wed Dec 8 01:03:07 2004 +0000 oops, not -no-wrap commit c166485609e204c8911fcc42c6f361965d4f875e Author: cpb AuthorDate: Wed Dec 8 01:01:13 2004 +0000 Commit: cpb CommitDate: Wed Dec 8 01:01:13 2004 +0000 Fixup the cvs2cl.pl options to something a bit nicer. commit 3d03bc68a1e9cba9ce16ae37b9c6519fdbad60e3 Author: cpb AuthorDate: Tue Dec 7 03:45:27 2004 +0000 Commit: cpb CommitDate: Tue Dec 7 03:45:27 2004 +0000 add some Makefile.PL magic to ensure that ChangeLog is removed by make clean and generated by make dist. Of course, we're building dist with access to my CVS repository... commit 6fc85f4529c8def8092fda7910dc12a2578095ba Author: cpb AuthorDate: Sun Dec 5 17:27:20 2004 +0000 Commit: cpb CommitDate: Sun Dec 5 17:27:20 2004 +0000 Add an example/test which uses the DYNAMIC_FILE_UPLOAD functionality to implement some progress feedback. commit a8e3b05d22092eff30fa37cec26e20dab3c8770a Author: cpb AuthorDate: Sun Dec 5 17:17:47 2004 +0000 Commit: cpb CommitDate: Sun Dec 5 17:17:47 2004 +0000 minor formatting tweak, but I also need to bump the version to do a new release. commit bb04c294d4541d4461f913ccad3bf7b14c62abbf Author: cpb AuthorDate: Sun Dec 5 17:17:11 2004 +0000 Commit: cpb CommitDate: Sun Dec 5 17:17:11 2004 +0000 *: HACK for the LWP content length warning. Apparently, add_parts() doesn't cause the existing content length to be invalidated and LWP complains when it recalculates it anyways. *: tweak description... sometimes the :: doesn't make it through. commit d75ec119706c7c348fdd95e568a4114f0302919a Author: cpb AuthorDate: Sun Dec 5 16:58:51 2004 +0000 Commit: cpb CommitDate: Sun Dec 5 16:58:51 2004 +0000 *: oops, chomp the password *: make test image private. We don't really need to spam the tag index with kernel and perl over and over again... commit 1ed55a736387c900016aeb1ea8b953bfa1d9c7bb Author: cpb AuthorDate: Sun Dec 5 16:44:35 2004 +0000 Commit: cpb CommitDate: Sun Dec 5 16:44:35 2004 +0000 make tests fail gracefully if no password file provided. This way I can do sane testing but cpansmoke won't complain. commit 676aa2bef0f0003530ada8c8cd081ee55db0b884 Author: cpb AuthorDate: Sun Dec 5 05:12:24 2004 +0000 Commit: cpb CommitDate: Sun Dec 5 05:12:24 2004 +0000 *: added make_upload_request and upload_request functions to allow for more direct manipulation of the HTTP::Request structure. *: added t/request.t to demonstrate how said functionality can be used to upload images directly from memory rather than having to create temporary files. It appears, unfortunately, that LWP doesn't like the way I do things, but it only complains rather than bailing. The end result works. commit d3e1a58be20b6ac07c22ea85fa4d72a12caa1c9f Author: cpb AuthorDate: Sun Dec 5 03:36:49 2004 +0000 Commit: cpb CommitDate: Sun Dec 5 03:36:49 2004 +0000 strip out the test comments commit cd3a5f617173fe9cfaad2235d63e0210b74b34c1 Author: cpb AuthorDate: Sun Sep 5 19:18:57 2004 +0000 Commit: cpb CommitDate: Sun Sep 5 19:18:57 2004 +0000 *: don't export upload() by default. *: upload() returns the resulting photo identifier on success (still undef on failure). We're also nicer about the resulting failure message. commit e307647393228d65881fe30826f7a4980af31ddc Author: cpb AuthorDate: Sun Sep 5 19:17:31 2004 +0000 Commit: cpb CommitDate: Sun Sep 5 19:17:31 2004 +0000 *: output uploaded photoid for each image *: read config from ~/.flickrrc. commit 57e30391fe1e8ba8025750f3e6fde99354819db5 Author: cpb AuthorDate: Sun Sep 5 19:16:42 2004 +0000 Commit: cpb CommitDate: Sun Sep 5 19:16:42 2004 +0000 in theory, zero might be a valid photoid. Probably not, but... commit fe3d263a2716ba4152e2b9ee2217ccd5d9e85277 Author: cpb AuthorDate: Sun Sep 5 19:16:10 2004 +0000 Commit: cpb CommitDate: Sun Sep 5 19:16:10 2004 +0000 change how we 'use' the module. commit be1f3f6e2d5b7bd58953d1e11d23b9b5e75413b2 Author: cpb AuthorDate: Sat Sep 4 22:33:04 2004 +0000 Commit: cpb CommitDate: Sat Sep 4 22:33:04 2004 +0000 assorted documentation niceties. Dunno how I forgot about the README... commit b9f2925c5102e910f43b13ebc770daf73dd10dc7 Author: cpb AuthorDate: Sat Sep 4 20:29:50 2004 +0000 Commit: cpb CommitDate: Sat Sep 4 20:29:50 2004 +0000 remove META.yml from CVS commit 0d680d109c4cc6d2d16f8f88eeb2da3a7c83e7df Author: cpb AuthorDate: Sat Sep 4 20:29:03 2004 +0000 Commit: cpb CommitDate: Sat Sep 4 20:29:03 2004 +0000 - assorted pod additions - cleanup failure handling commit 0ed62455cdbcb86e32c4760aabc4f0c84b1c9e30 Author: cpb AuthorDate: Sat Sep 4 20:10:18 2004 +0000 Commit: cpb CommitDate: Sat Sep 4 20:10:18 2004 +0000 - added flickr_upload script - clean up Makefile.PL (i.e. add prereqs, etc). commit d4eb3346c47d5674eaa36664ef06b0b5fb172c0d Author: cpb AuthorDate: Sat Sep 4 20:09:38 2004 +0000 Commit: cpb CommitDate: Sat Sep 4 20:09:38 2004 +0000 tags now just a string rather than list ref commit f417c013751e21c99a57d34ec5c8094b6e80c35e Author: cpb AuthorDate: Sat Sep 4 20:09:19 2004 +0000 Commit: cpb CommitDate: Sat Sep 4 20:09:19 2004 +0000 assorted fixes: - pod fixes - tags now just a string rather than a list ref commit 3796bdc8594aec2955b8ee83d1876b7a5d6e6a92 Author: cpb AuthorDate: Sat Sep 4 18:03:36 2004 +0000 Commit: cpb CommitDate: Sat Sep 4 18:03:36 2004 +0000 upload() works commit 89f08c0342226a94dcd68635dd65f186e46e1b2a Author: cpb AuthorDate: Sat Sep 4 15:49:59 2004 +0000 Commit: cpb CommitDate: Sat Sep 4 15:49:59 2004 +0000 change tests commit 6920d3522b4cc331d87944cc418ce83c5b6b9376 Author: cpb AuthorDate: Sat Sep 4 15:49:44 2004 +0000 Commit: cpb CommitDate: Sat Sep 4 15:49:44 2004 +0000 adjusted testing, added test image commit 13bdf123607bcdba5c7e701585375b1709d98769 Author: cpb AuthorDate: Sat Sep 4 15:49:07 2004 +0000 Commit: cpb CommitDate: Sat Sep 4 15:49:07 2004 +0000 laid out pod, initial stab at how API will work commit 643cc3e3ee961aa3a2646aa5291ca25d374c920f Author: cpb AuthorDate: Thu Sep 2 21:43:07 2004 +0000 Commit: cpb CommitDate: Thu Sep 2 21:43:07 2004 +0000 added META.yml commit a845211957e95b6dfb3481fb882475c2cd6cb030 Author: cpb AuthorDate: Thu Sep 2 21:34:44 2004 +0000 Commit: cpb CommitDate: Thu Sep 2 21:34:44 2004 +0000 Initial revision Flickr-Upload-1.6/Makefile.PL0000644000175000017500000000135712757100034016251 0ustar sseverinsseverinuse ExtUtils::MakeMaker; sub MY::postamble { return <ChangeLog META.yml: Makefile touch META.yml END } WriteMakefile( NAME => 'Flickr::Upload', VERSION_FROM => 'lib/Flickr/Upload.pm', ABSTRACT => 'Module for uploading images to flickr.com', AUTHOR => 'Christophe Beauregard ', PREREQ_PM => { 'LWP::UserAgent' => 1, 'HTTP::Request::Common' => 1, 'Flickr::API' => 1.09, 'Pod::Usage' => 1, 'Getopt::Long' => 1, 'XML::Simple' => 2.0, 'Net::OAuth' => 0.28, }, 'EXE_FILES' => [ 'flickr_upload' ], 'clean' => { FILES => 'ChangeLog META.yml' }, 'depend' => {'create_distdir' => 'ChangeLog META.yml'}, ); Flickr-Upload-1.6/flickr_upload0000755000175000017500000004127112757100034017042 0ustar sseverinsseverin#!/usr/bin/perl use strict; use warnings; use Flickr::Upload; use Getopt::Long; use Pod::Usage; # get your own key and secret from https://www.flickr.com/services/api/key.gne my $api_key = '8dcf37880da64acfe8e30bb1091376b7'; my $not_so_secret = '2f3695d0562cdac7'; my %args; my @tags = (); my $help = 0; my $man = 0; my $auth = 0; my $oauth = 0; my $progress = 0; my $check = 0; my $report = 1; my $reverse = 0; my $photoset; my @photo_ids; my $upload_config = "$ENV{HOME}/.flickrrc"; my $oauth_config = "$ENV{HOME}/.flickroauth.st"; if( open CONFIG, "< $upload_config" ) { while( ) { chomp; s/#.*$//; # strip comments next unless m/^\s*([a-z_]+)=(.+)\s*$/io; if( $1 eq "key" ) { $api_key = $2; } elsif( $1 eq "secret" ) { $not_so_secret = $2; } elsif( $1 eq "progress" ) { $progress = $2; } elsif( $1 eq "report" ) { $report = $2; } else { $args{$1} = $2; } } close CONFIG; } GetOptions( 'help|?' => \$help, 'man' => \$man, 'tag=s' => \@tags, 'uri=s' => sub { $args{$_[0]} = $_[1] }, 'auth_token=s' => sub { $args{$_[0]} = $_[1] }, 'public=i' => sub { $args{is_public} = $_[1] }, 'friend=i' => sub { $args{is_friend} = $_[1] }, 'family=i' => sub { $args{is_family} = $_[1] }, 'title=s' => sub { $args{$_[0]} = $_[1] }, 'description=s' => sub { $args{$_[0]} = $_[1] }, 'key=s' => \$api_key, 'secret=s' => \$not_so_secret, 'auth' => \$auth, 'oauth' => \$oauth, 'progress!' => \$progress, 'check' => \$check, 'report!' => \$report, 'option=s' => \%args, 'reverse' => \$reverse, 'set=s' => \$photoset, ) or pod2usage(2); pod2usage(1) if $help; pod2usage(-exitstatus => 0, -verbose => 2) if $man; my $version = $Flickr::Upload::VERSION; my $ua = Flickr::Upload->new( {'key' => $api_key, 'secret' => $not_so_secret} ); $ua->agent( "flickr_upload/$version" ); $ua->env_proxy(); if( $progress ) { eval { require Term::ProgressBar; Term::ProgressBar->import(2.00); }; if( $@ ) { # Remove duplicate newline from error, and no need for two 'at line' messages chomp(my $err = $@); $err =~ s/\) at .*? line.*/)/; die "Term::ProgressBar needs to be installed for `--progress' to work: (error from perl: $err)"; } } if( $auth ) { # The user wants to authenticate. There's really no nice way to handle this. # So we have to spit out a URL, then hang around or something until # the user hits enter, then exchange the frob for a token, then tell the user what # the token is and hope they care enough to stick it into .flickrrc so they # only have to go through this crap once. # 1. get a frob my $frob = getFrob( $ua ); # 2. get a url for the frob my $url = $ua->request_auth_url('write', $frob); # 3. tell the user what to do with it print "1. Enter the following URL into your browser\n\n", "$url\n\n", "2. Follow the instructions on the web page\n", "3. Hit when finished.\n\n"; # 4. wait for enter. ; # 5. Get the token from the frob my $auth_token = getToken( $ua, $frob ); die "Failed to get authentication token!" unless defined $auth_token; # 6. Tell the user what they won. print "Your authentication token for this application is\n\t\t", $auth_token, "\n"; exit 0; } if($oauth) { # See Flickr::API SYNOPSIS on OAuth authentication, upon which this is heavily based $ua = Flickr::Upload->new({ consumer_key => $api_key, consumer_secret => $not_so_secret }); my $response = $ua->oauth_request_token({ 'callback' => 'https://127.0.0.1/' }); my %request_token; if($response eq 'ok') { my $uri = $ua->oauth_authorize_uri({ 'perms' => 'write' }); print "Please open this URL in your browser and follow the instructions:\n\n"; print "$uri\n\n"; print "When you authorize this app, you'll be directed to a dummy URL\n"; print "that contains a confirmation code. Paste that URL here.\n\n"; print "URL: "; my $input = ; chomp $input; my ($callback_returned, $token_received) = split /\?/, $input; my @params = split /\&/, $token_received; foreach my $pair (@params) { my ($key,$val) = split(/=/,$pair); $key =~ s/oauth_//; $request_token{$key}=$val; } die "Bad OAuth token received! verifier => '$request_token{verifier}', token => '$request_token{token}'\n" unless $request_token{verifier} =~ /^[0-9a-f]+$/ && $request_token{verifier} =~ /^[0-9a-f-]+$/; } else { die "Unable to initiate OAuth authorization. Response from Flickr was: '$response'\n"; } my $ac_rc = $ua->oauth_access_token(\%request_token); if($ac_rc eq 'ok') { print "Saving OAuth credentials to $oauth_config\n"; $ua->export_storable_config($oauth_config); } } if(!exists $args{'auth_token'} && -r $oauth_config) { $ua = Flickr::Upload->import_storable_config($oauth_config); bless $ua, 'Flickr::Upload'; } die "No auth token or OAuth credentials found.\nUse --auth or --oauth to generate credentials or --auth_token to specify a token.\n" unless exists $args{'auth_token'} || $ua->is_oauth; if( $check ) { exit( checkToken( $ua, $args{api_key}, $args{auth_token} ) ); } die "No image specified to upload.\n" unless @ARGV; $args{'tags'} = join( " ", @tags ) if @tags; # pipeline things by uploading first, waiting for photo ids second. $args{'async'} = 1; my %tickets; $| = 1; my @argv = $reverse ? reverse @ARGV : @ARGV; while( my $photo = shift @argv ) { my $rc; if ($progress) { $HTTP::Request::Common::DYNAMIC_FILE_UPLOAD = 1; my $photo_size = (stat($photo))[7]; my $req = $ua->make_upload_request( 'photo' => $photo, %args ); my $gen = $req->content(); die unless ref($gen) eq "CODE"; my $progress = Term::ProgressBar->new({ name => $photo, count => $photo_size, ETA => 'linear', }); $progress->minor(0); my $state; my $size; $req->content( sub { my $chunk = &$gen(); $size += Flickr::Upload::file_length_in_encoded_chunk(\$chunk, \$state, $photo_size); $progress->update($size); return $chunk; } ); $rc = $ua->upload_request( $req ); } else { print 'Uploading ', $photo, '...'; $rc = $ua->upload( 'photo' => $photo, %args ); } # let the caller know how many images weren't uploaded exit (1+@ARGV) unless defined $rc; # check those later $tickets{$rc} = $photo; print "\n"; } # check if( $report ) { print "Waiting for upload results (ctrl-C if you don't care)...\n"; do { sleep 1; my @checked = $ua->check_upload( keys %tickets ); for( @checked ) { if( $_->{complete} == 0 ) { # not done yet, don't do anythig } elsif( $_->{complete} == 1 ) { # uploaded, got photoid print "$tickets{$_->{id}} is at " . "https://www.flickr.com/tools/uploader_edit.gne?ids=$_->{photoid}\n"; push @photo_ids, $_->{photoid}; delete $tickets{$_->{id}}; } else { print "$tickets{$_->{id}} failed to get photoid\n"; delete $tickets{$_->{id}}; } } } while( %tickets ); } if ( $photoset ) { if ( scalar(@photo_ids)==0 ) { warn "Cannot create photoset '$photoset', no photos were successfully uploaded (or, you didn't wait for the upload verification)\n"; } else { my $primary_photo_id = shift @photo_ids; warn "Creating photoset '$photoset', primary_photo_id = $primary_photo_id\n"; my $photoset_id = $ua->photosets_create ( title => $photoset, primary_photo_id => $primary_photo_id, 'auth_token' => $args{'auth_token'} ); warn "Created photoset, id = $photoset_id\n"; while ( my $photo_id = shift @photo_ids ) { warn "Adding photoID $photo_id to photoset $photoset...\n"; my $rc = $ua->photosets_addphoto ( photoset_id => $photoset_id, photo_id => $photo_id, 'auth_token' => $args{'auth_token'} ); warn "Adding photoID $photo_id failed...\n" unless $rc; } } } exit 0; sub response_tag { my $t = shift; my $name = shift; my $tag = shift; return undef unless defined $t and exists $t->{'children'}; for my $n ( @{$t->{'children'}} ) { next unless $n->{'name'} eq $name; next unless exists $n->{'children'}; for my $m (@{$n->{'children'}} ) { next unless exists $m->{'name'} and $m->{'name'} eq $tag and exists $m->{'children'}; return $m->{'children'}->[0]->{'content'}; } } return undef; } sub getFrob { my $ua = shift; my $res = $ua->execute_method("flickr.auth.getFrob"); return undef unless defined $res and $res->{success}; # FIXME: error checking, please. At least look for the node named 'frob'. return $res->{tree}->{children}->[1]->{children}->[0]->{content}; } sub getToken { my $ua = shift; my $frob = shift; my $res = $ua->execute_method("flickr.auth.getToken", { 'frob' => $frob } ); return undef unless defined $res and $res->{success}; # FIXME: error checking, please. return $res->{tree}->{children}->[1]->{children}->[1]->{children}->[0]->{content}; } sub checkToken { my $ua = shift; my $key = shift; my $token = shift; my $res = $ua->execute_method("flickr.auth.checkToken", { 'auth_token' => $token, 'api_key' => $key, } ); # FIXME: this could be parsed, but I'm not going to get too fancy print $res->decoded_content(); return 0; } __END__ =head1 NAME flickr_upload - Upload photos to C =head1 SYNOPSIS flickr_upload [--auth] --auth_token [--title ] [--description description] [--public <0|1>] [--friend <0|1>] [--family <0|1>] [--tag <tag>] [--option key=value] [--progress] <photos...> =head1 DESCRIPTION Batch image uploader for the L<Flickr.com> service. L<flickr_upload> may also be useful for generating authentication tokens against other API keys/secrets (i.e. for embedding in scripts). =head1 OPTIONS =over 4 =item --auth The C<--auth> flag will cause L<flickr_upload> to generate an authentication token against it's API key and secret (or, if you want, your own specific key and secret). This process requires the caller to have a browser handy so they can cut and paste a url. The resulting token should be kept somewhere like C<~/.flickrrc> since it's necessary for actually uploading images. =item --auth_token <auth_token> Authentication token. You B<must> get an authentication token using C<--auth> before you can upload images. See the L<EXAMPLES> section. =item --oauth Interactively perform OAuth authorization with Flickr. The user will get a URL to visit at Flickr at which Flickr::Upload must be granted write permissions. After granting permission, the user will be redirected to a dummy URL that contains an OAuth token and verifier. This user will paste this URL at the prompt, and L<flickr_upload> will save the resulting credentials in C<~/.flickroauth.st>. =item --title <title> Title to use on all the images. Optional. =item --description <description> Description to use on all the images. Optional. =item --public <0|1> Override the default C<is_public> access control. Optional. =item --friend <0|1> Override the default C<is_friend> access control. Optional. =item --family <0|1> Override the default C<is_family> access control. Optional. =item --tag <tag> Images are tagged with C<tag>. Multiple C<--tag> options can be given, or you can just put them all into a single space-separated list. If you want to define a tag with spaces, the quotes have to be part of the tag itself. The following works in L<bash>: flickr_upload --tag='"tag one"' --tag='"tag two"' image.jpg =item --reverse Reverse the list of supplied images. Useful when uploading contents of directory with sorted filenames. Following example will upload the last glob expanded file as first and vice versa. The last file will appear first in target photo stream. flickr_upload --reverse *.jpg =item --set <NAME> After successfully uploading all photos, create a new set named "NAME", and add the photos into the set. One (random) photo will be the set's thumbnail. =item --option key=value Flickr periodically adds new features to the uploading API, and these are almost always implemented as new key/value pairs. Rather than waiting for a new L<Flickr::Upload> release, you can specify any of the upload API's optional arguments using C<--option>. flick_upload --option content_type=1 --tag='cats' two_cats.jpg You may also use C<--option> rather than L<flickr_upload>'s command-line options: flickr_upload --option is_public=1 --option title='cats' two_cats.jpg While Flickr may add new options at any time (see L<https://www.flickr.com/services/api/upload.api.html> for the most up-to-date list), currently known options include: =over 4 =item --option safety_level=<1|2|3> Override the default C<safety_level> notation. Set to 1 for Safe, 2 for Moderate, or 3 for Restricted. Refer to L<https://www.flickr.com/help/filters/>. =item --option content_type=<1|2|3> Override the default C<content_type> notation. Set to 1 for Photo, 2 for Screenshot, or 3 for Art/Illustration. Refer to L<https://www.flickr.com/help/filters/>. =item --option hidden=<1|2> Override the default C<hidden> notation. Set to 1 to keep the photo in global search results, 2 to hide from public earches. =back Note that options unknown to Flickr will result in undefined behaviour. =item --check Checks the authentication token via the flickr.auth.checkToken API call. This can be used to verify API keys and credentials without trying to upload an image. The output is the raw results of the API call. =item --progress, --no-progress Display a progress bar for each upload with L<Term::ProgressBar>. That optional module will have to be installed on the system. The default is not to display a progress bar. That can be changed in the configuration file: echo progress=1 >~/.flickrrc =item --report, --no-report Report the status of each upload ticket after uploading the batch via L<Flickr::Upload's check_upload method|Flickr::Upload/check_upload>. On by default. Checking the status can be canceled by pressing ctrl-C. The default is to display a report after each upload. That can be changed in the configuration file: echo report=0 >~/.flickrrc =item --key <api_key> =item --secret <secret> Your own API key and secret. This is useful if you want to use L<flickr_upload> in auth mode as a token generator. You need both C<key> and C<secret>. Both C<key> and C<secret> can be placed in C<~/.flickrrc>, allowing to mix L<flickr_upload> with your own scripts using the same API key and authentication token. Getting your own API key and secret is encouraged if you're tying L<flickr_upload> to some automated process. Note that if you do get an authentication token against your own API key and secret, you'll need to specify the key and secret along with the token when uploading images. The default L<flickr_upload> API key and token won't work in that case. =item <photos...> List of photos to upload. Uploading stops as soon as a failure is detected during the upload. The script exit code will indicate the number of images on the command line that were not uploaded. For each uploaded image, a Flickr URL will be generated. L<flickr_upload> uses asynchronous uploading so while the image is usually transferred fairly quickly, it might take a while before it's actually available to users. L<flickr_upload> will wait around for that to complete, but be aware that delays of upwards of thirty minutes have (rarely) been know to occur. =back =head1 EXAMPLES First, you need to get an authentication token. This is a requirement driven by how Flickr manages third-party applications: cpb@earth:~$ flickr_upload --auth 1. Enter the following URL into your browser https://www.flickr.com/services/auth?api_sig=<...>&frob=<...>&perms=write&api_key=<...> 2. Follow the instructions on the web page 3. Hit <Enter> when finished. Your authentication token for this application is <token> Unless you like typing long numbers on the command-line, you should keep the C<<token>> somewhere handy, like a configuration file: echo auth_token=<token> >~/.flickrrc Uploading a bunch of images is then as easy as: flickr_upload --tag 'dog' 'kernel in a window.jpg' 'sad in sunbeam.jpg' =head1 CONFIGURATION To avoid having to remember authentication tokens and such (or have them show up in the process table listings), default values will be read from C<$HOME/.flickrrc> if it exists. Any field defined there can, of course, be overridden on the command line. For example: # my config at $HOME/.flickrrc auth_token=334455 is_public=0 is_friend=1 is_family=1 Note, however, that these defaults override the defaults you've assigned in your Flickr profile. You may want to do all that stuff in one place. =head1 BUGS Error handling could be better. =head1 AUTHORS Christophe Beauregard, L<cpb@cpan.org>. E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason, <avar@cpan.org>. =head1 SEE ALSO L<flickr.com> L<Flickr::Upload> L<https://www.flickr.com/services/api/> L<https://www.flickr.com/help/filters/> =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Flickr-Upload-1.6/README����������������������������������������������������������������������������0000644�0001750�0001750�00000002105�12757100034�015147� 0����������������������������������������������������������������������������������������������������ustar �sseverin������������������������sseverin���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Flickr::Upload ============== Uploader for flickr.com INSTALLATION ============ To install this module type the following: perl Makefile.PL make make test make install DEPENDENCIES ============ - LWP::UserAgent - HTTP::Request::Common - Flickr::API - Pod::Usage - Getopt::Long - XML::Simple - Net::OAuth EXAMPLES ======== flickr_upload is a completely functional batch uploading tool. There's also a good amount of examples included as tests in t/ of the source distribution. And you can always ask me how to do something if it's not obvious. I might even know. Git repository at: git://gitorious.org/flickrupload/mainline.git http://git.gitorious.org/flickrupload/mainline.git COPYRIGHT AND LICENCE ===================== Copyright (C) 2004,2005,2006,2007,2008 by Christophe Beauregard This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. This module is not an official Flickr.com (or Ludicorp, or Yahoo) service. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Flickr-Upload-1.6/MANIFEST��������������������������������������������������������������������������0000644�0001750�0001750�00000000460�12757102637�015435� 0����������������������������������������������������������������������������������������������������ustar �sseverin������������������������sseverin���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ChangeLog flickr_upload Makefile.PL MANIFEST META.yml Module meta-data (added by MakeMaker) README t/async.t t/failupload.t t/progress.t t/progress_request.t t/request.t t/testimage.jpg t/upload.t lib/Flickr/Upload.pm META.json Module JSON meta-data (added by MakeMaker) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Flickr-Upload-1.6/t/��������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�12757102637�014547� 5����������������������������������������������������������������������������������������������������ustar �sseverin������������������������sseverin���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Flickr-Upload-1.6/t/async.t�������������������������������������������������������������������������0000644�0001750�0001750�00000002043�12375006452�016042� 0����������������������������������������������������������������������������������������������������ustar �sseverin������������������������sseverin���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������use Test::More qw(no_plan); BEGIN { use_ok('Flickr::Upload') }; my $api_key = '8dcf37880da64acfe8e30bb1091376b7'; my $not_so_secret = '2f3695d0562cdac7'; # grab auth token. If none, fail nicely. my $pw = '******'; open( F, '<', 't/password' ) || (print STDERR "No password file\n" && exit 0); $pw = <F>; chomp $pw; close F; my $ua = Flickr::Upload->new({'key'=>$api_key, 'secret'=>$not_so_secret}); ok(defined $ua); $ua->agent( "$0/1.0" ); my $rc = $ua->upload( 'async' => 1, 'photo' => 't/testimage.jpg', 'auth_token' => $pw, 'tags' => "test kernel perl cat dog", 'description' => "Flickr Upload test for $0", 'is_public' => 0, 'is_friend' => 0, 'is_family' => 0, ); ok( defined $rc ); print STDERR "Got ticket id '$rc'\n"; do { sleep 1; my @checked = $ua->check_upload( $rc ); for( @checked ) { if( $_->{id} eq $rc and $_->{complete} ) { ok( $_->{complete} == 1 ); # completed ok( defined $_->{photoid} and $_->{photoid} ); print STDERR "Got photoid '$_->{photoid}'\n"; $rc = undef; } } } while( defined $rc ); exit 0; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Flickr-Upload-1.6/t/request.t�����������������������������������������������������������������������0000644�0001750�0001750�00000002672�12375006452�016425� 0����������������������������������������������������������������������������������������������������ustar �sseverin������������������������sseverin���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������use Test::More qw(no_plan); BEGIN { use_ok('Flickr::Upload') }; my $api_key = '8dcf37880da64acfe8e30bb1091376b7'; my $not_so_secret = '2f3695d0562cdac7'; # grab auth token. If none, fail nicely. my $pw = '******'; open( F, '<', 't/password' ) || (print STDERR "No password file\n" && exit 0); $pw = <F>; chomp $pw; close F; my $ua = Flickr::Upload->new({'key'=>$api_key, 'secret'=>$not_so_secret}); ok(defined $ua); # slurp in the photo my $photo = 't/testimage.jpg'; my $photobuf = ''; open( F, '<', $photo ) or die $!; while(<F>) { $photobuf .= $_; } close F; ok( $photobuf ne '' ); my $req = $ua->make_upload_request( 'description' => "Flickr Upload test for $0", 'auth_token' => $pw, 'tags' => "test kernel perl cat dog", 'is_public' => 0, 'is_friend' => 0, 'is_family' => 0, ); # HACK: this will be recalculated when the content is regenerated, but # if we leave it as it is we get a nasty warning because the added part # will invalidate the existing value. $req->remove_header( 'Content-Length' ); # we didn't provide a photo when we made the message because we're # trying to generate the message from a data buffer, not a file. # Now that we have a request, add in the actual image. my $p = new HTTP::Message( [ 'Content-Disposition' => qq(form-data; name="photo"; filename="$photo"), 'Content-Type' => 'image/jpeg', ], $photobuf, ); $req->add_part( $p ); my $photoid = $ua->upload_request( $req ); ok( defined $photoid ); exit 0; ����������������������������������������������������������������������Flickr-Upload-1.6/t/failupload.t��������������������������������������������������������������������0000644�0001750�0001750�00000001073�12375006452�017047� 0����������������������������������������������������������������������������������������������������ustar �sseverin������������������������sseverin���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������use Test::More qw(no_plan); BEGIN { use_ok('Flickr::Upload') }; my $api_key = '8dcf37880da64acfe8e30bb1091376b7'; my $not_so_secret = '2f3695d0562cdac7'; my $ua = Flickr::Upload->new({'key'=>$api_key, 'secret'=>$not_so_secret}); ok(defined $ua); $ua->agent( "$0/1.0" ); my $rc = $ua->upload( 'photo' => 't/testimage.jpg', 'auth_token' => 'bad_pass', # we're supposed to fail gracefully 'tags' => "test kernel perl cat dog", 'description' => "Flickr::Upload test for $0", 'is_public' => 1, 'is_friend' => 1, 'is_family' => 1 ); ok( not defined $rc ); exit 0; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Flickr-Upload-1.6/t/progress_request.t��������������������������������������������������������������0000644�0001750�0001750�00000004356�12375006452�020352� 0����������������������������������������������������������������������������������������������������ustar �sseverin������������������������sseverin���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use Test::More; use List::Util qw(shuffle); use Flickr::Upload; # The $HTTP::Request::Common::DYNAMIC_FILE_UPLOAD facility allows us # to supply a callback that'll get the contents of each chunk in the # multipart POST request to flickr. By inspecting the whole multipart # request we can determine how far along the upload is and display an # accurate progres meter via Term::ProgressBar. # Ideally there would be a callaback that would be called with the # contents of each logical form field in the multipart chunk, since # that isn't the case we'll have to build an ad-hoc parser that gets # called with each chunk in the multipart request and returns the # number of bytes of the file that has been uploaded. my $NUM_TESTS = 9001; my @IMAGE_SIZE_RANGE = ( 500, 1000 ); plan tests => $NUM_TESTS; my @PARTS = ( q[--%ID% Content-Disposition: form-data; name="photo"; filename="%IMAGE_NAME%" Content-Type: %IMAGE_TYPE% %IMAGE_DATA%], q[--%ID% Content-Disposition: form-data; name="async" 1], q[--%ID% Content-Disposition: form-data; name="api_sig" XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX], q[--%ID% Content-Disposition: form-data; name="auth_token" XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX], q[--%ID% Content-Disposition: form-data; name="title" Test], q[--%ID% Content-Disposition: form-data; name="api_key" XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX], q[--%ID% Content-Disposition: form-data; name="description" Test picture], ); for my $num_test (1 .. $NUM_TESTS) { my $image_name = "Testimage.png"; my $image_type = "image/png"; my $image_size = 512 + int rand 1024; my $image_data = ('x' x $image_size); my $chunk_size = 128 + int rand 256; my $id = 'qBXwGcnVvxg14vk2iUnT8v2YB3FilB3b3rmAmVeq'; my @parts = shuffle @PARTS; my @mod_parts = map { s/\n/\r\n/g; s/%ID%/$id/g; s/%IMAGE_NAME%/$image_name/g; s/%IMAGE_TYPE%/$image_type/g; s/%IMAGE_DATA%/$image_data/g; $_; } @parts; my $whole_message = join("\r\n", @mod_parts) . "\r\n" . "--$id\r\n"; my @split_message = unpack "(Z$chunk_size)*", $whole_message; my ($state, $size); for my $part (@split_message) { $size += Flickr::Upload::file_length_in_encoded_chunk(\$part, \$state, $image_size); } cmp_ok $size, '==', $image_size, "Correct size (img_size: $image_size) (chunk_size: $chunk_size)"; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Flickr-Upload-1.6/t/testimage.jpg�������������������������������������������������������������������0000644�0001750�0001750�00000166440�12375006452�017240� 0����������������������������������������������������������������������������������������������������ustar �sseverin������������������������sseverin����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������C�  !"$"$�C�Photoshop 3.0�8BIM��������7�03/15/04�Kernel & perl.jpgn�Christophe Beauregard<�15:55:44�08/04/04#�17:57:49�Petsi�Kernel & perl.jpg�Pets��"������������ ����}�!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������� ���w�!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz� ��?�\*OB8Ġv`�s)v0k&vf?�A�(fc;łԁ�z}4;Q{QQR}ڟ#Z2 E9+1�Bw2r!V%C(\O6>Z#,zmXR]c}rx@�Wx#֒i:GpI8<p[jӥ+UU&ݖ*NEfP>^iJH?QJ3Q 0ޔͥ%2S0$ρ9V<zxI~V  u �tޭBQ~ZЭm nYD9?z'Fʜfz#8Jשm7wNP4sdnt4 6k<GjځHV\ZSSb;7F8ֺG%SFf#"|1۽8Ƿb#a[@Z܂*z?-Myo# ̱cʹY}J-'^ G#V&i3e/N;]-<uC+agȆ7jmlĺy pߧZQ\69Vՙ5$].cl?:5v_ןg)]lx5Y-6[nn522#5gF:p!ĤJr_Y𤖳}2d7�v9$F|I2o"xR'8+{UH#~ON:05SXm 91iUR3s;&ǑZdY].6଀Oǯ/Oҳkf>wNyj6SQd\dc'=̧,H<uLm$lHqCc2mLb [lM'ku9]UȌdv#f۝.O9{U]2 �dOZ] wN\3uTؿ@@ v$osZgGI~ P HUsca prj[vLΝH۫8*RJa+)qk#J9u {jqBc[IF)ymn}q\ɷi;߯OoߜiDK6}�JutMxws�W�3 ǙoHdB>m<{j>! 8Z5ÀqFMN!RHUPq �UH?yc4r&y5xéAnA�c}+66nPGq5 [lRSMm=(YEl�lA%+і[(h[{bzx-I ͹xc�=OJP/*e{8Xx _Qٮ W:l3IZ>S<+T+n`njҵ_ЙyBp#V[�p:s(W_ٟ7|~uO6u⛵t);r}aVsVP%dp^f-i*8zԫ[=<?:oQE[ NiHT_ڠݼ`� hz.i5t*<Dr 95WP.JȬOl^4c$HӦI[ ]-O2%dhdVi 1wM=+Lds4egrIYolf*8.$m~ VҘyd�v=E4a7sl.#DQ7%FyOt # Lm70Go9N�^M<nsTowwџOq]Xa ޚrkBy\eǵJ4O,20zWz, Oq#DOn{AIhyDZ6?Zع (q 2x8]2U;h.cvQT0l0#5RinW 2=0iG M4 [[at#+s0U°|FKJFODlދ pe`>\cc.x Xd.~cg+c9d+TˡxƁ.IK Q"+v1{*�bCR ?ʘ3N@ۀ>bLTy qE\`q4$(pW<8 JNۂ1<�$p5G4bNWa; &O�xjMVK!O*&IS�EzLJulC1+\:\vKKVŴKj8F6ǵye3[7I<#9$"~l/]mYrDrӊ断sGԲ8:~V�cO8<rSPC�.@#H:$ $zSIڶFT3 c^=)ivJv2u Plk{/eE+(aG1,64sFHQ5MQ&{zלE<ؘ7O5veb!@ob8RXѪ<W]8j>|Ѽ!_edҷ[pF7n-v:zԏCץL#96:栖xh>lt?,{XƏ`YfK-[Nː;!@|?t #G*zLWAsO 3j{{JPg�]:زXݳYutdb!�_ٶ@q}0|tKYEÝJV28r֫IR q>i$xȣg80GU%U|i 9ߥmien%f(?_nk腳ۀP-mw�IYSz3Ibc}ռ!w2h1KO+g5" mɷ*v5nF1R,|? Rm 5gKaۘO5>q_DyP}FGyvΔaV/rX4csy~H zѪ|.RdfS&܍zHͰQ.sSϟ�S$cDz=պfgb{03gK8ɏ4)DI46}|>$#\LZe 0k H�: ?R)9^ƪu<e>უܒxɤ]d$'ڽԌ4\S�]ޣٶlmH?|S;^=*iiK<'$^FT wf5~vlk}2ocB2>Jܧ>!y^vy�ZЧ-՞ߚeqB%{U|<E,3_Oz1gϥws]ؘd|d8�xj]2MB&T`4W�ڎ)RC�gZ~6㚾nQw#aݮ7>8e<*- L>$mvkj* -�3rO80; zV姄cPu$송dP9:8X=zZmsnO4siׄ ?O k}(dF5j'e91)]ñ9W@=,dN03PV�A1"G*p?iYw1c?E q~i&[Wi*/@>Z esݍG.W .n>;Pmz gc Uv#ͫsX|9yT*7 gQl<~]*䚴B)$q3Bn%wЃܨ܋@:h ws2%'j?y"d3asyV%DHvBWw(@d)-…#֩SfsY;s!E"zzLqX[1ƼrMV>&B�9QZm6`5R1^%~L z�n-91� BgFR7q\{ӧh A0�8I増C)`ݍ�d(L.>jɽwE[CT@k>W_Zx 9ϯZG_J/uj5W #xvw/Rxz$cKWmy<n\89 縧 \Q(nwv y3�G*>Ω$VN?u> Yl'+ds<W&Y9늵cpaIkc#ҦOP{%V0 O�l6b"Qjn,b� W'zM+F#޺Rg  rk4>T*r�r oj.#0A9W9az΢Pij{ 9��֣q` d�@_j OZ'm,n, !@?oGVi�g�U$>UMޗ/#bF1rjUo6#ҨBꪣ'' N9{ԥeq|4-»vxYb+ГW&p F={WE/#=zVg%taI>nzB́*Hkɻ'^G^r\ 0y 뾚;oųa?LSx0>ďC_;X%#66ɼ%9n)K{s;葩ڥ=F 2Fr_;_\bl-׿ν-m�nMnBvϤFs-өnw|ɃsX";o\彽�|KrX79oօ$q>uIDV@?�ׯ oy>+xOvSKV UOdg:?nI2ڃKh �1xϵRJN*13mJEepmƾ`+@=}Pftb>M( V�>7m%8'{PfL\N1PΣ_*^> Np=~݊* >kaˆ7�!B;ɯ3F3vaq�8 <toi?OyG1'=~CO\ׄx?ϥ4x_lǥ+BCNePgR;Ԑ3ɯ)�7Ҍt#�S'})c(Aa׎qL7(6&EK##�Zokt 7|j0+e^4Av:#U ׈u;9/F_nѿm'|£kRGϠa|ָ3ܒ:~cUUo.8皤N5/>}p> ہۃ_;Ũ7w, $Hc ci;5lܚ�I/&x\$F1ҙ~v͎'<} F}r)ϩ^HE@u5@ wn(+/^.rLc$|5Fq<k[iot}Lu)�omjO| ]+*1NG?k(U[|G3*WfI-�?L׃Ii*fɤ͒׮i*k] w%LmdJt: @? K*#?ҦRya6fv;_- ~]ڜQ^$|݀pi]|Vn=."3�QOLqs^mgm<'9*ӧ 8*N=j]ozhQI;T/-0 ܆ }6msƥ8uxN2`[;xOw+NxsH,&`鎾QwѝW$yc~[+@G=8'&,;pR, g8 Ak) WspF?JA+Ff$7-0a֠6s.88N54vJ)/yL$ssg3Ue5 \r\ZII^KGnGRB4^ez+ĕ^j=):P$ C{pmcDuEd% 1*reR}\4qt Vf-3uJ;[c<A-�@%đX9> DwifRF qڞV=0S؎ىګ]orB�,Xz6~ڟ4J0\qOEp8Bpr3I%Wps Ӷ +V*$_H rN=28QR8�4)kxLMp^Cv�֨9`U 8jЯ ºB]]>t5o3}^^8#'_<d[PM%P:VĤǂ,w#'b(o%Bw=ݭn¦er0۽)]l#QLn>~HyN>mA'N<y<cPpy;!Z$yjL:֩F6#ubܒYlS@زvt >^z\Sg xm(ʟ~Gߛj'Sգ8U4[1~6if[t c>29 WӾ5�mG^@|ݭ@m\Lw8>Y86eNᚹƲ K0ĘT!l qZzqIR}=7ŞjZꑴLY 7zRʸ܀R 5XF*rc[4 <|^r1ZMb[f,j^-|sK6'P@96Hp>S⡴/nZ.|rUG52Ge"9RTIv{&+3M4ӷ5;ß:3$"]d0q6cot?“znli_[v^m.tC<b[hF`p:Ibӡq=y&Ε4-:&[qtm+x%`+*3nbIʌ2OZG{:. uHt$eXdg nm9~>9À~(.>~iS``;;p95J9Uͻy̅AaYF-�bg18\ɦcc?)*K>g *Tm#*b 7\5MoLxv cjxK#yϯzuu35�:r CkceNVlOXgZd&@$c\%/O8AqE/dvI7*Iu;]Z%LF+(?)$gɞ�o(3\'3_ZO:*%$or8j~ltCq`�ژ?[v~i1.݌g~?&v*[XV]yǽKac,'\wnA$9tV/C`?fOI0Z(fQ ZZ8FP}f3 d#C[[,d<'ٙuZ$ vԄ95qI>&2y~wb�28sz}2=M>MB%�qZ-9$�.qJ˩WO:�ȪPf8F8C+b\ft5ϛ�G.sOB/ q l�JKP tG;aʱҘ̈SRVf6N@a\`v; `�*ry8<SdBBFx)=Rq~m)dbHn hH%iLepW9<H2sMR31�jea#p X`H'C$88r}jRMYi&[i>\_G4x㞕;Wx{QH嗂88J-y!xloϾ1)w=8֫scޗ3qvjG}9봓]6'�5Q[ g}�Jr:Vi ff_nut5+2ftʃڱ[GY7Ȫ@=?:P`Fs[kwn k& U:`sX,jZwcEsӃH$d_jzg z +A@ Œ~=p'"NV 9@#=TޥU�p͸+ڐ HǶ(spxqv4 �Gٽ$F|nN@a/i[|s^sI霎Z|'p^bLsRZǡDD$X!idw�\?iJcoTzg'lHArKB)mL+7)#ޔm旜k.@JNj*`$4N*yRz1A af#<Iڠ d*[}8g"4-�@k%Pgn#JmʕۃϯLcYBTM^6b=xH3`R+翈KYjWS!<B^iP?~.@Ƙ q�N5sln&P=<t&HIo>J2*H-2PWs ?)�&ۚe!5 ( t7>l�ZLmfMc=GC*wq^RQlG8 g5[LB&Q|JKo2+xuY Ie;B*Fnt? 5 =\W7nQ\# 1=ю5n_ CBz~KZN& r?^+[Kk!agmxX=W6*&Մ>a8cI )#E\DFc%AOƚKmܫ!IǷPiQ+GPGJ։``<Qxꦡa.&WdyU!Mv&U#ʼ@>rG>O.'mI $L';;YC6:~UR/A {Z`PyQx!󶛬JiYxn:w?ٳf*:לۀ +`ʕm^ú0t\Om |cW.[p<g N༶:o޿W3zkqb;̽nl}�llv[ ox n.PNLRFN+Frws<*$e:[I+-uCnfXmHJdֳ=4z+bF !v�8xZ\(R23FW> Ιϩ5uƉA`+·jڒn`�6UP| f~%�x~'m'9O] g9ȬzM7:Ndhmʐ22{)xXr3]Si׷8!,8T- ivv]6{~{s+s: Q>Q۵TuVl�z$Q$ r*M1lZ29G3c=9RYKt کރW'bl~^*e"Z#5Cc<FqQަ*@�S̴LSKMdհð;o%QYEXǶ 9<s$ ܻ;+%,�9HU߿ү)(Ly;zwKt94Y<-_krXm`qM _۳Oõ[i 1QLXA=~w*1 t~ gx95Fۣ�:t�֏rxv06a@�RmmC:@Jnp:TgiC0r9~Kv@G mjs{LZ$̩bRA9Q`_9-Î<pyQQ'vOW/9~|{ &֗a2F{WT泏G)_eʹH##!�*>slg+?p1R4¬@fBsG F2>up#Z -V �ZJiUpi#lodc8SN],<ԇp9Q#ϽGM]B<dwo+Fi"1л #&ǮJ|" ʞ)Pqnݕ#Lƪ76U(�iN?:wV ӨUKa;QΒGwUX&l:߭l~ZKX" � r�e0�Dq)*8⧙-(/dPX+ ǁJ)\Ɯ$dt݉kߛ!G\pPQꢥ /_z=)<*IjrhB#8:Ԉ�=} Nh+J"vz&n;ӏ|յUL"]Sg0i%=Oŭx(>a;LG7,OOzt�6WRw/I9VO5d,ѼLa [!OֺkOPJ]d8+<=xL8n[ ¤G<6H$BEp0$�QO�}b$],qFb,CR 6zG44۸{t<1<2 ^KoxJcSm3[F8J[_R%Z4|9ӥ)q8�X[IhH�d֙)H|lד~%e&;g G_zA&ey DS~oEC*q �Z'öu̳Al5ik,[/+BX֡(Spj9kvYg/ BY_Ғ0涮mUWpK7s1ЖrG>I/'l7 *z*@osW-Nvf9 Ygzɚ&� 5wrG|T`rxϥOh>b�q5P{RwN\QR^;r@'C[F$;1?~�d  iUO#ל&rVD<r6j$#/cZ;SC;yj#V4( Oln#Uaq#-^(K$e#85j̜ /{S!>IWB[|9^ ꤦ$aqչO8}?"hg9}+|- ]X+G0's]h%IZZ#3?_b|BF�>@-3-e~U֓S%6qsUpj 4N[»7~Cg$}vgE221VuJ"O]l%u<-Pǡӎ4^x JkƸ�#v/r/IϔIK*gT-=Ùs3 "~Q 9=+ьiiRR]7RhR@_^pN=A^Q1]׎&JLn}3L�!19C)߂,IGE(n}>?AHFΘ(W|pzm4XMb9 lWg]>?'/텣K!s#o@Oc]ەbrLm5iMnM$+rr:(n8+� SRsi8 ]j1$v<?-wRQ›=U$Վ.?/wdyov4nGZܣd\}WUy.aGE**Si נ#c0ޮO/gуfp%Prz?Ȧ3{ֽ0LMu8_oRN}BL?<�nN�#ۀ:c4!N2)ёA=k|V=/ I+(9aО7jE$F � ;SHPOqsMbKzsޅ NI2ywnX8+,|◤'dS~lR`FPH;l<u݃MK��FzNmYeR$@==?zQN=sI 9ZWbm4ͱ*SUZL<ܹ8� 0]۞T�RIH*JW4dPXlPr>Ef$SJ^Ԛ&B՚mWhҴR'8ڰ#t: yyH>b?${\?:\%̨8b;ײ-F?v�W1$|2@2n|oOwNgUn1N4QUrOQ6`_],w\ΞsɎ�9رV$c\c}yY4R6_*~Tu[ Й/!d �=�Zǿo#4 $e,xWk#uwZ{3Vɘ};vkOf/׃k^%5_)n@v¹֞lgASJXUh+t2T|�E =X]mU# *,Z}C/OZ滾4cdFAQV'T 6DbFcuzقh T ӥEՎY 8Tk&ݕqǐр?Cuf �"ےФ8 ]|7t5\mjk3}V ͝tc ֯W09X^dl`V#Y!*2#>�*֝s Hde C.3Uqڻ'dLupMdoNFS"N**h^2NN@^&�u\vSU\pjn&rqkxGpTV沂:⤲]*Սz&<oQYnE,¼6wS)G,ְ0?6GNkB)6f#d͢Ժ <)E@l<zjHP*cߜk,ݻ\4`�` �Zj7zE]Z_P.$T/*OV*ɝ54:Ɵ10*5j:,3 vaNkGRzǵw\$/; i STդY1O"ybLWY^#ϭZY=]:rESQMYFO��b'/Ҽ]'&YAfr˹ޛC~1Lk5<W 'lwem-�1֣7+i95 61Uv}ͯ� ToZ9g;Qƫ�Gҗ'[+z`y%xCbFwF3ԎYsUr]!8u;vۊE j$qNvC1?_Γj E;Xt͐9%pāu9؎~qIJ۝kV9JdW#a$uk,[G*6nK0i^;WխYzt늊MZ�|#<`WdU6~ޗ;m{*/[PH޿θYB\VJ+Zřl֮�nd5>c:)d�</֍D{7-:~q�TMç{ҼiJ#/SϵTRpXHG=.н!鞱Tkezcy\I]3 ϕ\gs7{Z)0)ss�?PʛrNyQO(8%'FݜSaf6OIRzee Bt#ylJ uH鎴 >"A+pTcLK]Pd1$坙VAb:�?NlUgtMv*34pNy\ޕ~lesM  ΫX|Azo  p*sΥJpbd088WH-;MEQn>i >p�4I%\TJ\m{T<2ǹ)7aH* $S8=r:Tэ>px2<{Ĉj킴p|<oT?G&C!U<zs_R5@I(l1XB=EVJ '}altlq+(4.%XNOV_�[G o,;~!L1WQ(y49^Qjo%'6N^UxZ/^ GY< ;Tx8Gޗn)}Ȣk&Τ!Vwr:U<:.*p2zD +GF#zޗ!Kn+;4"oZ?Yv@#Mkב2@̡iS1隚ԛe_z-͔N]00*3�bpqW!e| ʷ-v`Px<d}i6n-cLS8<gڳb21G`棚*�9~]d'I5\-iszy_Z/GNKF@A=U $yav3ޓvөV*K{yp #+Sb W\HdBBH)>T# Y-].d{�MFX^%`I T:}ҋsN{WjrYsZV+jx0.1T>X�~kC[޷-"qڱؒr'޸սERՄu鎵B:eSrvZ0ި#ڡE]"9>ƣ,v7~\JK(7lVtd8EjM(O7W 8sMI%vSNxfja-8ƛeGw2rIQ޷!2Fzu�W$<֯X𫄚~K—!Ш溈 כx XT$C8=dܬ yi<+*+sM;jF dƍlVsO摔23$I'#ʜVC-H9\�0ӚSi^zsڣy JE4E!qZ!�^~XL㟥TnrW8ғC 9=M:Vwu�T$<bWa?8J{` ǥ1vgNФIz  ڔ#r "Ǚ| q9+ݾCjJ�A'#Mv 2B]p5۩y ܞÓ\;[B rFGJ&'Y ?Jq!sU)kv-lVf@觏CXcpLw1;~*Qg=3[:a.A`8GZ葷.7m54j6obI< W0 9<J.VE,Ġ$cd l 6;ԥa6`'HI QYb$)۳SyR$@<٦ۃ`qVRH`{ABsCwaq(G98FC`G�8=b^Jj䉀[A=)ġ$t 9昁Y{Z\r <g>ڜJ sMg hݷӭiÔH:SK'x9!s(c<toq;#gM'?(R`#n`.K+5qꀜPN`8cj!p}jV}@ �($(9ŕjUd=2gBi\KdOո7皡osjiP4*m'qv=7րumn<.cfװ|dm</(cY mzx� ]ƙ/In2ex|3w2徝` Z[l/RXV�o>˷Wյ NX9'S@s@rsm5sGKH}h֗ #ږ0@j( `0:RAv Re>\@w95Ӫہ$b9n@ޕiI$BI%|#_j.J8KffxU Wª6TI^cgp,G9aE|; Ǹ3sZ6R6;|ZڍL1r$vSeGng9��>&VUh0rǦyHTQАs*Uvs-(qH'yz=ZgF #iQٗiD'YԦڹ"8�׊Y LXwNFPʜ#Z*q5rKG=WoR~ʠsQ<uE*Ź OhUn@$ Rj q]AW�뎸N O1`]N-ڒWffm!E?(cT�uPXU<:6J\cog̯_m f0$`܎ J`LwQ =PMhgX_iS1Qry{G�:-mw1Y>U?5ᷓ~؂Q='Տ:Z3>K[6 UԴف>%jmu,?6=+K=r[Ln|z%m"88!}i8Y!UsNs@eEY%}Nv2lMfwVFȮRhǘH p* #FX$<rxCV~#H͐s'L{�Jr+i{ЛcwVtq\vZBc\uj;"QWn*�D0f{2TtE-L  >s,9&aj#fa 9z!ᛣ*Mi9tg@ <~G6O*@}b>{#8>m9�bD� U]͕\2FEԐgVqЊivrTiԩ"|#ÓթSjG5(8mQ{j[} y]G5p>`nb5pQO#% .#Q�רq<NՓ ASeM6x_6!dMdFԓڭO Qg!THKh%kWpnH�<urʬHoQjj sblM_*+T` `2aܹJQ H'Y9[cwclmIH QI6OJA%`v݇5`J=M`7@*<qNI4WR6RH~OqD|\뎴Ka/w@qjp:.~#0{R �9S_M} .w0N}:S#QFa9:ޘIRAqL+7^0:�*g%1 NpzX'Hı䃟ḄoZJ]HԳn$]wq]wm"^ݐcS9ZV13{�v͹'Ej^, pLVq �~}h#9o)9$ӣ+"vz \wt'zZL\ѓav-%'M/@nIL4%/CENQۚ1GuWӶq=i@oPNǦhҴ)BH*x'ڭ] b5kRh1t?9! +? 'NJqƶ/ f=kFG,?wO֖[a$oqum qZII{ ͻ6Łx/r-`<`k2rA#<z79P}1޲JLѮmBo$;@9|GjەvON3tAhH<dMLyБ*5%qp3ڪ^4) 8�{v9W$6g/o3UE>Qysv�EW$c8.^SA5Oy1{TJ.NzSM;<B O4 WW�Þ$NEdxoL�ˎ;SM5̙2GaVvՁ+;A9C;e)*ɜd+oH (689+>t苍2-6Zmë7 [xvx[[i�Ty7Oj|Ku{#uW}ߙy_uv&47uuw\a8.ٲ%aJݎG\t"5Q=`>+$?-0*<q֭X>z nH䌩~*3}K\UgW_>áڡWsq8IIo�CJo&#GJOKmR18{Ӝ ͐yy5vՓ"%D[4Ʋ)!vE5&,6ӄ-GHxC g+w?)n#P|i{TTGryj&Eʹ�~EEߩ+s>:2l?.}yN$Z3`R�݂3s^?uC<+#W!H>�KzQU w0'E8'3աqؓY 6ܳ*9v$ϭ']tWռ˒Fs cX&ۈWJйf9G[5G ݒ1`yR.>kFnxMXP|g/maRG~je{  5@dtB8rwTwDcA8nP26$VqIV.7`)׀ P `񜃅 1])R q*emJX;.\pHm4CRxf6]̳6^LdR"E!K '4#%qAI3#A)\﷮)EYZV%{z2|7(b#&@P?: ǭ*ݰO<i\V Ōt=֕@%IC( 1H Ҷ`HB�`mq_ʃ*B\&1Y%1HRT*@'`_}0{ӚGު͌ztQn\rxAx'ZB`ҚacӚӎvLx֙! u=is(D�j9 ԌKz ԑP32�a>o9ʢvs=E? q֡y93g⒓޶^Fm�z֖WL3I~iI(> $(?Ƃ3ۥ-B'j)x 5g>ޔqE 9)h="%O o#�86m:͙gn:>ZAit9 J4Kxr#MSc\ u8� /`X:Z4,s>@N1�V>2qNpEt03mHZA5f"N8j \(Vs2+c*}}ɴ?b+ IUW&=5qFHS$>3TjHԒ>@?J )fM,bG-85zÿ�?>DNZ'~v2.c?ֽoᶮ.ccO�W%aZGo$xȐ=*Gm**rHlc�N1gvZ�_i,mu0J܃<>HNz?i>#x<qX_)&m6*mvِFQF#8t>bD�݃ cޫM9Z#>wfY񶞐+&׌~T֑W M:]i <C=F Osp@_v/.+K1CIU2xYJx4:xu?~jt<z۹?`%R3Zm9/ ䷯Z|?O.hLO )'*Q_ٙIP�#{?B#γs`Ч?zՎkr[fFy=fHSk?˹z ֬盼f^Ӵ[}tP<=?ZPP# d[Ig#<HdHD$R/�S7r2r|dfh7穨;Y%>VenK_ojkY�YIq2ȫlQ.A''9$_v;r{bn!e?^K+ X6ӈ?: [cV`y;z ׋w#^A:׵\`! PǬ y탞F5&oI+/ֈvߚ! s<.'Sttv,fz=:Էа>M$8fGoS,Lh^15Wfr[s<**6##W)wVp?Қm E6s2QRkA2EO<Yr%3XE-R�1; gn:IV@[$dU;@�8#I7%Z1pwnqQ8$RI@S?E,2IUl�х\H ӻ9P9$4"dmj�U 7 CP&ٖ`S XK}d:u1ӎ:~`V<Tbte�!Svg8SYNGCPʑ0=z@.tW[AN+'4,H\Μ~fZ�8I`�>E? }:jzb+0l@P˖$7oLc*=3i\Wjm u*w`L9=HߧzVƵfG^$.ҤII':*;0vH^ b6ͻw�9# "}Τ*nq)_ө7Ut2RU<斓PxVPqF{R JVh?ҌR$w-Pviflo'q[v4�]&쮋9yh5A'8X1< T+V/e>4vz4 Ms|g_e]n)FZO+9Ѹr+kH�)�U4`1�]^C8~ZeKuнxzU}cHpB= Uax [#lq]:TQi2PW[m"�q_vj*8` g;u;(8<�TR[y`{V6yshXpW*åjE°#i4Z}t} S"pm.0=Nj#1A[hR<qT$ $�FM{Sv3Ͳ ۜ׍S@9iN9@Q!9B}w4iIo__ONBS޴@H3 _i% ,w9B`U|Ǐ"<70 Oݬ۽ݵbѵвьg mOn*gYHfl#e~vK�f֜[ZiJRE*T&n<NX瑓P\xpV ݎIdkoelW1UgE7g�W363,?oEs*O9Eq:vg[ms-ԏSס>yu m+ I'WE—ܸ~归F`*^ӂ9|˝Fr�ztϵS{ 6K]%B19$$sG%8Im,p'c(gB@UһDc70Oxqև=teJZ8o8Tpd\42Zn-o9;td3DgԸK;n(sx@S2'0?)Oo͋48,=Zh|HOC;D+_ VXè^QbK+njJ�מ|WpCHֳcz\#ѬaI# uN\+qe?#9Wo @Xuc6ǡF�)�=Ʌ�lマjBsR;-V"IF: 5ʐF~c=x)vw t-@}n)DnΠO֣_+ $qSEXs* -!b s`jo\h@wbPOM<Tʭp;V02g50ČfZ{*~VIf;q�Tm(|*8?K5!mPŒޣ0ۃ0I(V+=%�~lt=j�|sji܄m�[0_".\eTըXhbA=+8ђ$dqS  H;ћaܕ:Pץ8(#b5O|՜F=+m\R̎ S5"N܁'0c;V e&|boLq9Ilg4mgozsm8*kjVCzg)![Ӱ@?FHH(*:!rƒXH'= *G?ҕ?( xi e@Z$޶cӰ$Ih`|H9U<dTl xcMm ܥ~C)5!#0?v�sU;s4IqޞԂJCFHU-3YоZ=r>�־{5ZSm˥rޫ/h"ޏŠ%d-!ҁBw{9SEhCh2LW Jv|`G$u=Η{6zsJC[8jӭJnwT9=5)]6M(F''5lJJwc+,� Mm!0{SUɝTeefuM(\l 2>o\\K5(?)`$r4KkݖuJks k"`*=\I(N=OJX8f 2sZŴ.u*<7x5~@(ݎee% cgUѷ 5"d @*EfpӚeҪf3 е+ zTE]pyzɹYbR^=+fLNqNi& $ |KWN~ ҩ:VMхu6*b۩E {bC#\1�I-Cm Gs8$pszUkrU%7nWR{bOLQ_۫3#8^ySa\+vԐHwsklَ6ϱӱK$n}?J"1�׫po1V{x<Eg`#ܭ+ I�.j5g8@?us}ıaֲu?PkP[-&V"`4`>ߺ3UI.$f$9U{+iuaGf�Vl/fo* ͜㏥iX_J"*a#¿a>lepgӵv:r3Ip OS)G[|+I<M5$-$ ?uܼ +k{,#_(jܸEcQfʎ"vgqYO_ݶ%xÖEon2x{OLfP&p@&ȉUMI�Z\:bT|_(-R]xb(Hc< ̪crcӔc%MJ;$[XڹjQ\,D"뒱uk7M<AX%ojRI)M$`З_IWn)27jX�3Ӵ$71ad4pO52݁'ڒJlrbxñGjKzY-co,$�AiYyA *&DIԲDpk[jr߫�1 F`knhI$](f0] \ �ujjh;Ӽkc!*@+�:X^[%偌gi>H98<jGzvpm֙4Qprp5Bsӽg/W_j~^l֢3EO-֣w41PA䓜UD۸1Ӥ�F铃Ue߮5;#A㊮ȥicKTq? &7'J0j%ՄРV1 ^Jq�HΝA ;d-wcQO} TC$Qe �>lS$i.7g?Omb)!>QMjDhR{x́ݲ3Ӛy ;.xqY:η =k<C}yG�u7R2H&vZ"d3ڹIm)2pGj<~@�`u00p9�WuH_TqBN/0szpi0IG#�~@>�1\:]@.~q6Kn/#8t#<B�uL(�o g�vܢ0đ)ӽw'\izs)mzTʅh÷Af,F,bXn8 }(Uޤ0}FsBdm"`6뎵C}CU m+"6 .X _D|ΣqN@w|EM%ڎ0#ܩd;0rk_ko.l`PШ=wF3NUUѵo =hmm0 8MEdtz|cޓ#&Rp�GZO ,Gsԕm^ZlAJ]h U+[MSiqں[(Řԟ@Eg4ƑmMN9 w8Ѥ@ޠSmI\ghD@qץSWj۝+}<6! {d+ڿHyyTqEE=5h/B> u8BDV=-Z�fFdNk cdJ6OL>c =i2d29Vb8+PV]#8Dl0�199T7&UȑBda-.@>;Ui%dvEFqrj2;Xzu}LoMgdQ!�E%̌#ܮĨ�?Ta8Pɸ68(&++s j-VR7W11Y3]Lg�&b&o)r*rjsƏvW v>n8hh|I;ޜfv wxmpԶkKPk.-BXc$F�#I 4&NqEoK>xMp)ϥ`z4nH�tX .?JVإ%-y6"dV!u,>w\b;GmoukWTKkRdw'Utʤaf[ZbJ yuepEEZf[줆=t9oړkir}JΦ31FykextMP`tQVVUTj9#e*+)X~a䎕},w� ˼H @Fw-̮r1ոtv85z0$zUfAUӵ-,615VK3ԯl<|<RQsv9 �Q̼Ѿ>h ޻;�f3xM4pݍM��6C-rasd�$WUY-IK"Ƿ?7Rw"}-óQ֤[m?!j޼ӐY\c5{"̮c9PFzvv\ ʨ`AL6"6xUv^#% Vvs"/bٕBsoHR2:Wi%$kï[o]K5GR9\謴=6٢pQמ8fّ]C@)qbzsBq|3.�.TQ�0@\Ǻ6/Z(0?3JOm Dʯ1*THq SHmۃM @dU ӲFIX=PmBD8$sUՀ?Ψ6Fu+R7vL{`nv\ �ƢOWqϽ4MGNnl&d sGWq?mH™[1ҼidS$sN.VuķR'Ex4HP}t̒M5a ɤ֪39di<5<cŷc�ҮKEc63,v $e|v:H!-}qESx5.SFGz@S)GS\c1_1ҥE+ ɤ_{R7S(Ɍ( y8Eu<3{vF|ǯ^ `pøj@SSNBN nE>#CNȣ žl-#uLے}Η#zP"BE1xo‹VHl y !mRx >LPi� 6by0=;xJ^jOef>HɮBȭ .rNFqZ]fEeܧ霹kEл@Ƿ\yF Hֳᔆ؞lRLaUr"9]ޥi/FG9$ ^ǚ"W9>zzd~Ojٷ@8ϯ֪Dz7b,챂juk! 9y<wIdۍۊLHK={hP6 4I[wyF#-Xec8UqAզF/®]X)o"7IBbYwcr*G`xPx9J+Z._+p3c wZdUYІA 7n-qQ] `mF=:r2*cji < �gZ 884jدfgS$19*ce6g @HbQ7}O6f 3S 32.-UwC$h{h\Ǖr=�+.^ RX`q.ng#էmyd6AsO<[ˑjֶtC3^0 sY*$ǯ^{?Tz8Ú`#H@}3Wcpdu<9ؚctܘp@|SV4̓[egy��+HbvaH8'Eý=jIs1PMp̗,I@>^zi9=]ޤf [xs=뙿ե SXv}h4NRFf{t$Wzƚob\"Wc&cw5_RUha~s|+CS-'+۴±AZ$ΝYZ{܃EJ ,�ՌBcVl9zC>cBzd3揑8>J JDE'$o+ixp�gN,$"3[Z*235[d#8˹a+/͊\<40&2%{~=cF\^ȩ+ SeH*f�?%*A `jZ)XB=d+|͟º:<ڳB)bۀ&ܤ~bqj�8i#@bӑGv+XӤ%T;Y#I?t a/Yc9#Qn+G. RX+;U`_n J:nkj|4#^veXGt^tc~"f/"ݓV6,jCnS :oki5%c_p*8''EeZM"#W6JʙB7qҫ}oK~jr%[ƐnB";KR�[s3z f[!xjW2<fvT}z ^`K sLq{t8;Ρ84.̴;)cjrTm=5fi 'N05|GcsISRت䎧#(IAbJ_5*"U5<=Q<gֺ4X&sqAh۶X>YQ}vӇ-V9 A>A ䷖zₜ GAҠ{鶞FTݴx=s%~cp8#3I)Dd<cR[1]WC8vyjvktTAǽU`c~k=N=F7|tEeFvR�9<B�F[\vRT} BIy(E NPI :z�%%:BAG<zWo mN h;gƹb _F| ZkL{�"nM7d^碤6 xc'm~y+~7YYx=�w e9cQ^"IE6WuDsHI Jˑ^6!$o_@J$8 *7V|GOWB !Q�18Z퍠uV#e;)^7]-ŵ`K >GFG WzC#\ ݰ#�CZu*gK`c硭T@AOJ&e;օY@r8�SuԞ3@8ZKvǏʲ1S>`JU9GJ }9=1PlA9p?:T<ÜzKqqJm`G4^VOBF0y 1W!,@jrj &T*AtsG,޽A ܿDoN:h#;JTR.Lg�]B!07/ZQm: v,)]R2Uq�Ԋ3�̰ S~7c;yWazU_Z=LlLRE899T?!n3IH[Ě?,Tg Y;NIXQ$2urZLh7I۞2]KϹ@帺鞸К3ڂ/>HgY5(3=1MKi!Bg5ŵ;udQ\I+68赒Rjz;Wd];sr]d$ {RUT7=g@#jJB%+m}nwHdAS˶ 1K(z?|*)ٖ"ClU=B30P<þ|C h"l1%L>|6] $<;ZI)_ֽ.J;_)9<npԽGަ=D_sMRA Q FRvm-"thݜdh )qGԹ&VSң�9r v3)8 *92TG8Kgx[(4ڢmK)֨s5xa o֪5&!qR|)H#s8&@;qKF2*@J͐ݳӵjW;X bM e#�{ # , {N0GW @ι2ޓF8犦6qGzx؜rGLk>V޿JKqnP1ՇU LU-!d>8GT=:^O]vhIqWVw`v @XTmq<%tUAuj^Hʣ?{=8ǽTy~n�JeHsgmi{ v(RGzr uZDa'~(1vRљޒ |WL9䟺~#$n__JH岣z7{+j|*39'vCWsVJ=1֫'pʎ?Z]GaEird ;וxZTNkԵ jt`W bkJ1ĻkmPC SM}c95Vʑ�{5uQ:96GSSA!݃3AE*He(eoFvWRCH,61xi$=OjOznG ڕ�\]OBxP['<g(~n38jjIR-~nvW;W%8iCWx1{xH �QvLp٘cE,>a]r==pqN ?_zVQZ=P�,hnr>1֮X( Yv_z7tuMf#-r;w_PoB8D,/>[ZTEN~,I6$Ek.n<mK.X ˬ:bBB~s�&'V' Uc!Fx[Ԩ..H(#:["x5KT&mx_+suvG̈o~s\*殟NΈBJA",$ Vb'œpk& bp}+E$2eO7µNx9=@ȹt̬҇kn{ `_AZٳL\EpR�hÝ;ޢm^ȸ/"@ FAB]iu(GQk d=V)+XS#G/ZpguUQIHy�P ;M%7M$vclvxkRl�:\Ww�N zTw %[8>Wئ֭VGR7*k\ޗ(�\ٴ-cv'_oDKAϛdqk9luM:Q+J*zs-#�\O^;*n/(PHKebe1S:\/}̱[q=?7z2dmO|zWUmvuj]2QDҴln"7Iƒ$l@PNFjKx*P�ZukkI[d֫e86fp~c<w}CUH;a:NW?fZ=17&m>mلbsc<=zeǙt /59YR> �>#-UP/>d8YM{w8 ml#<s)wׇᳶ]m\X\=vhvJڳzj2$HɩrzM.(cJISRrI_pRzT' Seg[S!G\.G\+ i6 ǸP{öZ%}Ef2TdB� Nz tW9h .6FTQ_Lb=~2nW+˛@p=qD :֣*DS]X9suV ,dsޱu<s'֣BrpKtej3 qʒAW<>c1։[ zSA�˹kur'1Ubk4bvf ]Oֺ]:\DA{UK1D\W8Tes滝R3`W3 +9Crm$ |c8tO=<U:͢B qR<u]WZܩi92sr�_scMV%LA´,2ub7rB`c�m:T- $VD+)$t>GPF~f*2AVklxnDAə!7rzRdkJFҼb {Nsf! J1ʇrbk@2^3zA|c}+|>u4U^}Ҭ:zQQ{s\ 8KcvpI'*5nyGZQvI7d}x[sN6M p@\֕W<p?Z[*|ރMc=1Ҩp9I$zfQPIOE<a~’JœB>Ҧ_-@gN0:I&BcԬ^h FBH]ݰPȠ79|$cr׸QQY\ޓl vF|WQ*]GUqfa[<{#{ב\O[-T:4-bB @9&H۲(^U.oZYgbsrJ5ΧjРcZ[H0瞙C*բ/$5GxlMOإBU2(cWt`Uv*  T}+YTkpOrN˩m~բhe[ ڼ[u meRE}wyiEdsWxQo1 ;2gآNnw<*&.OA5d7~<UmcJnXqmo!MǹF:Ff'}E_Iێޟzr`ִLlKcp*- vW223JaR 2orj%1 9=>dc>*i�֡9f`ޢo<=)34m p03Zv2'v0tydOfqSmK<sFwD3C<8d4'<jn-`bLk# ࿭OEcWy]MN8$*惢uCɨMÌU+ag( yKwVnM3+iKݙTaQj9 DҶU ~I9¡85xQI<re4yLy`{sҹ ek#<PI?hYaq# ~[غ=Z.X_޴w<յeb8+ɯ<Х;^hkѴm&8cwZ$S1m`7e;CF9`J5xjs簬&w[&0 ֻ" * {# *_ƚ#j΢wԈ[QEQE�QA搊kq2;#p)ޒGɼ^Hץt%ec+܉Uvw۔ iIJN:fҭIj;RhuJB\wg5Xnx|czM+Y\QVԹ `3 88c7F@5q̮<'j˺ݞi$ky{m݂FF2*}éV%qQAMOжнMҗ sQ<89O856v4ѩ#đ(!q޶4ڊ5iO,ܜaU=c]J<'~H* fyFKҦƢޅ-[8 ۊ\3SJ9i'ﮒE!]Oʴ:hd zn1նVU�Y6eSZ<{JrTz .I zqޘ󝤄i� F#?=;eHM8P�e5M㙕όs qj,rY 9 KR{B*T�z%wJkmuk~(X4\c`lt?{sӤ򯡓3M-$G"IC ^ ḯLu>G= Z\DjQ֒2 0=1ִC(]@$pCNc%Y $Z$K�#ҏſ̱9a<H7]\иv<JyI!\v[[br^R1�w(3BjOqe8@)c 8�7Tx?Z^w$1\8}M1g2=*Jҧ2׻9<gxM1gx RKm&7U0il}s5P͎:Wњ}:>Dӭp ?{f x�jݣVtXzMrMzZ-K8=r288$t&ܳ;1,O= �#OQ*5Ԋ1qwZz)qNG8=psǦztӚQW*@jM_;H%,CՖ �&P}ZoV5@3j0Y&$d}( 1Q̦u-Xtbx-Tl $c{kY<D9U EMR}p�?Z/%}8_TZ�[ Z ]8;I~#iSbR66ѲOufU0�Fz.�F/< 1T+W2}y<=23*=~x,Z(<fg;EsXXɎs9U"JЃ&9@&W*'ۡ6Ń1ƤF3j$duքnh,s9%5^%k^0N321~Pp 2SR)"]FN9,=*+ZI1t~mwbޥ HǜF>],y )j܅U�篽e`,09&vFsٲ@qN?5wzzmags~'> \^|Bp#g:ܪVyo-~1n2:v^/wh|@�9-{]+I-&b*G΢1$L9FBoz|Mf/ >tx0GU:+gD%dHaV"Ne!69�ϭ&{iALM a_> GI UV']EXY']X#֪I$o9O=봎4 (%V\ʂێ:={k&9.qޭt1qQ*REESU Ӎd՞9⺽<ё8'Tbޡ.7�GN٪̪.I!@A4A޺lD|ڙ Ɉꢥ[8V]ɐcf9liCBU ?njLUb VTL2;'iR\:ԚmII {dwI#(ӥ H<1kW[6L#VFq\d�J돭)nV(!-<L+4یML'(^SXC-cU*|9.:}koV千 qew4Y8銕YiiͰ.̫;kc`1TH^ѧ &:չ+51D>ՊZqr:עjb1bb$K󑞸Kr?mv'5_b;ʣVlbX$�u2bJVsJ&ћ nX#AEkI7s*}Ӯݯ<xh;xqzzx0Ϝ䫃VYz1PDQi5bVXcK0%Yqht%A8c|Ɂt}dݜg<U]+J:4Ti =ֹ?^3€>K6UARx©^ē[ `AvҊR`Һ^HTɬX JH0Rr5JK["C-ui8\̗.ј$A#}x%�8Z5-w7ͼuPpO3gQB6z(̫ {T0YzUrfZC{Inz{sQ|3ܚ=o", z&)B:9N*GD9 5:19psMPr21O+|ry5;J5m@ HBcW8J=`d#^jRIG[5MwjW%G#;tEs֒Hsz/_�e&g]9U$dI<  o\K:"#in22Ni9rOl&(U =WxWKNV1&Xn.vr>$D^W]OqIقN{KbkTPOQݘLIO8oy xF)颎?>  'ooJ"I^9d$?�UYh(snI>bBViin`;v^QWŽd֛~Ex_TlH wT7\4L>g9p5ǺM_5V-gڋ+ۥyDѽӤPHB8q\N Ɏ(*P =G+m6a$bB^\G>~մcXV8 6I [Ũ�`i-Cn*x#..d#�zj9.vMĨT ]@n~F"qYcIJw8c:qjm-~GKxp5mNCnR|3ǘS={Tym@$xN6Huj~v$<l%ȊKzvա1lHcOCW`Rc.y**]IJzI_hP"X`¶p. �8:#T9 Kah_cUavaOZ#\n8xUq[y$c|@�T<üGUsɧkM4O,bXئ;JdcR&CK.Tt>e#gEJ+^GfS.4`t=Y�gz³)I" Tjʣí)\1P@8٣LFRGSv̷vBB=B-v9g"tSmj[]Fe{!85b\l)N5uDgiCXP2O�T6q8)WW--qRlFYP�ZFC08qCbĜ/piQTPN85j<!�(R%ۼ1t@Ӻ:#Myߡ<al=+<13DCjavKX5K{*GBK%2zzTWURZYr\HRb]PzUe5|Q(21\ٟ5m.]]˨e9&1VTyJ#Z^"p23zĻKvjgc�bWԧ}c+=Zܶ~^p1֨EhJ7;tCNĐ[F|*sSl/?Zބ;ȕÁr?f_ AAV\pyeǞ* jҔh؀'օ"iS#<g$yg!�:ιO">CjdzMŞYbo9a#>ntLsWż]Fzv+lvu8G&Q�ٷ r�8#jL<wuRp,gֲ C*ƠI�tO9=Q*A� i7)]")eL,HBːI=OJl)ަ$�*VAʬ!8_hkRY{W2J½#pHBzzWF6$hB[Щ+9+VR53gv sst`͐}~nߕykFd\D�+|/<Jr8* T,%e =~~oQ*D2OzLϰS( 1z5˱6eIJ\ >SzLKV#K_#_3�18ԩd#E]մm#K 68lêerGdΈQcBi6YHw5<x` BRvmj;/BPjs bӊ0tž>�֪j1`d.M_꫶azi(7)2 'c��c~tN qS^vn߳WS5i3rqS<6 B'_J~ P<u8@.=ϵ>"#4s)9KG>f:3$F;�ă6.d>~l楝åܴҰ B�3ɭevLZ�@VcNZOR^H| {Xퟔ* f;I/>R3T41~_xȼUSñoAF&y2p/TבK5k4|cJuK0۬Ү�m~ }?Px9$z,WfA,֟"Fco1p9?zY]Z<G ƓUrYt#EO TU�=?U 8DsŐl%pƣf@W805+^ M szD(!=B=4"r�<J%Ym̨]z9 N^"6e1$HzMih'.})-{7L"!{֯[K\Xֆ+Yl2hĶq$; Ğ֦c RG2y}#*$BEKHh:?XG8b�� ;Oyiͺ'gmi$ʏQX֦Md lwZ c$x'W%[*ޣ֤RHFH�q�תb] UvAOb2 c95$I dzt ȌJO@O~4tE"^'�u�7w`==}D=%_PK8w=axyjj츫ךI6PCWrB;d\cs]sU[Dv]Nzs39 z2Y.t$:/,5RY)%}—L-ieH#Oj*GLb-3Pb�6&lz=(mC&J#B vjN,$gDbT|ijFuhF;%f#׊nD~mtzmmX&HO^iҩ iwSR/rfq2]~`=7bn.#c< i\-pGoZKQ`^_\VfN6F K}9ʮi8 RZ[Īr:fcީY=*1-jՌET/LTEW؀U?=:*{SFyT]ZIMiX,OQ}. ̂5#T_."ppyl!~.K]9v$Uʊ救>+lO ds\u4k/%X8 }V229Jyv:Xӭ7 ޷ TB9zuFrAl VL5_# g] I2@%chhrz8W �$Z)"!9;pFSqz3K&0t r=�*Ys8íVxā@q*lQM#I[jd}Gy#s8|?JtƇ 9߾*3 *r ^˓RW7GlێEebj6f#ҷPl;;gH>sU{L<gg;J+xΏ~ᇥ%b&Ko,G_;�LW6tJH_?56yhG.XlbPl p^>ZO Ou3\6>n9<WM9&KFr6隱 z⨣UQ�שv x[$"?Zu xtaОF)/mV?i|FmҜ3֬d��sQN)Y6jH#dųT'qUϘ1\X:VtV�d[7"BH\ `mz]onp{TJܨƗ5TG\F.Bkݡ+M8C(Í+AQgq�G',28Y<4ޛr+pDFisR_n!gp>NKǵ("؀m�Jz#{8#eXxz1w�:X͊72$"L.L0~b91f[y&%OP\{R91ݬI|XzQk]1}<N2 `~j峷ٚ6q,^N %)LLcz+`(,B=i;̢\$Ѣg!oR[v$x/;QwYOb #G8c~=jMojWk HY%AfSFI9SczYo(Kh9S!]uI,0=�BomO_/78A (`ˆc{a,xԜ9eVx H�BzӪ&@[$᪥-7,VsDSV6��՛FHQ/\TX5{)ɑؼ=jiR9"[#U-j+Ƕ!ԊtoY&?oaJ,M;29Mn9տ5XHAx>ơ̷�V w=R,m".Y{++$i +K;{T#2\³b1w IK_ w_ιڰ'Գ)GVXj]@ H[|_j,I3A.14WEwL:cPDi4v;ԓv늦rO@kc?JtVZi7zL$QRj>G\N�pH=ǚ|ϟ QDS<VԘtVo@Z[F'͔+#r<QJR+U5}+*Y`NOznx%*jjBMTl%+ 6\�׬ۂT+NGc#Ӛ-ckK:sGb\8>G]$g:TL"� ߓSf;&:SWhϽl3)QLV4l:}֦e2UA�9sO<Ql\ҹ=Z}[8y#$* 䞝+.KV񘑻'էmrsoOb $L6 j%c'<sfƕRJb J h&gnA׊miLm>k!##ZZ~%Ge@Bj:�^`w o3jS 4qLŦH&iMБ*vnk*v -Zc<ӑV5>TA3G$O%V,�yMu%{Xe -ϻ' !ۆ2X3-Bd3Ȳ9?(iŶe<hJw}L޻+-1ą{YٯY n{RklR�-Ͷi'ޤz |d͵W sgϣlV:klqz ٴGۀd[vW;RTLTrJGl4F̱}Ǡ21U-bpGV #9`N' ϓiXIb_~f$S| I= |Tҏ5|�taNibwqLh�઎ 3VV OjI&Uܠc>kٗv*q)GlqϷ5#˹JWt8\~?RJNOvƯ�<[^[a5 l܁":ݑNR[إI3ldoN޶<= #[I(W'N@<+嶏qq^f%9KGE!0 ?zA8k5xN<C^G��m8F51 3c3V>YtqrdV;d}zvV&,VhToSk#$o|`Hղejh%!{wv%sXr,F}88<YJBd| ,7]]&[=q:$ 2| e6l_b[ݴ6gd A;0�gh $/lx�Tnˇdqǯ|U_KY߷c1^yݕF;V6<d�Z<r";yi8^4b6=P#HpP-u>]^}eD[his[%PMAȝ ނ=*m68[1/*Nw%S_1 S.F#|7ۃޝmd7"K5ف"m}-\FIiHϿGMuh$r)n,UI�:yo^[�0z ?:L0M^`(>RGSO<nA=ihkt{1f`rOs*mmw vrmnE$řATw4W#6L*W/W Al'."lgۺI h%S$!pA8ɧo_LOo„whͰ]Ô<{Z FdvvW$M$Q$+7%^DwYvIR>Lv/%ĭ#y�3j%綎ZT\TIm28o>X6ηm4hrpsޒ~dm; k޻FF̖X-d $ػ2g`M#e[8�T Ƒ[\HMH ئ^Œ"-ɑ#RvA? rY`iYf`So#( eޞO"yR)tsHO "xS͵نyAN0nD!E❬IJ{We@2޸=@dVVeuŞ26*@+H# {BKRn <sUe[)eG5,T[Wc<:ԫ3I'o-77֢v2,Ò7ߎՙ{ps+ ]JMl7[6Crzڔw*#8zW0e7c Hnt4*F=QI&&QHv oxY73x7YIT`j=5ИҹFO6W'5=H=0�j?4 mds\6,y N Dn'^c[Zl->rW8MZ_{s"rvmg9b19VޓpM%u�"K$Knёu4>&y,�"T{R<3C-aF} $Pۼ"C;Zr[C,, ʔZ?_ni򢉶~&mh](x)7jYTCRyݙRTRSMy~bJ#C)g368Kw^HgI&682M,;+J#{Y +}Ar]BBH-*}饖"dTqXGGOzkJK8MQs҉AܛtFmvq'4 ,qS֛ yC5uD~BQq/.vy`1Rj)Ĉ#n7qAp͙:BPM Hyy>n{7UMlμoOrJ�?egJ).TZŕ�օmm,G{��J֎$q#<VrwBiHm{�Y5DUl#8QHUla;ԑ$<$biFzHVSH9;Uc� +sq&Vww݃OD 6@P0XC43枒(u< idl>:8r.YTPgr@1V!L98#�Z@@e=;Qf r[e1=?)۔WW%�ש1PpIz5dJn]ih<oTw֑[Ep�@* 1ޭI6ZzXȺa+HD_GFTeqS֬h̝= eJݒ�8p{(8겳;ID?pq;W3kOʓ:*y&2Rf/Bȹsv#cT<�ҧѫ+8ؽHɮkO-ZzFn#Uizd$O~sQR5ArQӭWڹ992k/>Y]sׯҹ< YZC;Fp$i)?vqeQs4M9hTmb%%@$땷(T S{G;[WЂzz[[lv7쐤oVmo$~Od}O ]N''lvv~|7t]Qr'֖R=H ݇uIʲK$kЮ1X;@LČNFuvS]v!e�z}iHΝ@#? q9̒TV8f+&@?5=y{mR<u_[߭ŸGQ!ڝk=%t �ZTwm6/Կڰ}#jŷw:,AKc3eCsOo481@ ۊ#IUIϥHn⺊hbDjwc-R_I2K%S-H(sޖI4`N2V!!ut ҥ=28mN˓h]k,7XJcCE`EEi�ֱ`M8MtdpNpyS-IJEzdu!;zqzZC^v;D`+CLGܳg}1T]%rw>dN΢PI46ŋYb[k}$pQԳLoe*=:W`M%\ q*i?wzU6ԭĕk+gũY0Sn^ $sL[aX \݅Ck;c V׶>?X;gO٥YKHi$ Vi>P 3"/?>VTm4%ͪVvVґ稭@ ZW# D|+ԵXtwQQz3eRG f:⠺D<2g'5Yd FGBjY J"jL䌕lc"5ɇ<_9W#8�hM*A:֢%0Tryۻ6xJJ了0D)3ޛ _eV 5x=727qqcP �NxXuW<ezc$\ _öIecrGqxͭ i=C]J"f$tO#E~#sRKio N >6msgIsDNUN0kIhzL*Tn; b4S+30"{7fdhKQEt+BHJlVɐ#8Z>eiYQ?+u=xF˚ {&d)8 a} <oc Q[NўO[0dEISIBm;�"$6%Ą]E4L9PS[H++uJ^͹PjIyzΑ9frHGNUP)^2yPup(`6O"3BFk-_^37Kz{Lha*rsifB9!Utn`zR<6.Y*qC᥆hј Jv0wqQ%+b]eH( uC-e7E%}!VMĶy`qZk<V%gI'։+j�Q[]V̂LL)#xI'p'뗆sF+hz Vb]&9`s�3^1nC1^Q/�,n,PW=3uMq=@k ̆5 J8]l6 ޲W;+ jg)Ny#8)[bz0 y8?ҔZ+1)\qU" qS&Bu+?zp(Y]q�J/Owr4:zHB g9L}.pNG c% zWO޵,P~c'tŝ�'=�UUa$+SG/<ث{.*xy0uJ ;d&gk* ߒ<Úɑ<< u]-ҶЌj9|~lƵ)+9ݎ<ZDՄ\zV/=0558sy=?Ȫ#�N[F7k]pQdxG̹]|Augzi"-J4<RE(ysqjϩҡ7Q]@;Xn,fhKoC?W}AWN-4sN#,˖PyR4ml<uN?p C� REp9َ|nFM#1 BGOJ]D'q\ηIzUu�1/|;ϟ皙7kՍc٤Pv`cS8Y~Srku,;Ԗ;zsH~1hgFhw8^JNko[`]"30zӄ7 _S[&FXD6zO31I })Hx jc'-@1۟ީNɽE@I!(\dagfڹ>$}d)(|6֦إBi]t9jrCa#@]3VIWj0j],k\)_zpZњaQIS6W*lPU$L~ջ-Fs nbN<xIo<cH*G^YUfb\U- ]^�mO .R&7Ck2JNd<Q;ʸ_NA&rfe(쥀9?Omwr025i#F>෽/DA. )drſC_ڮW@�&}3qӓ\;Y <j!G # Y}-sj[#US\ѯlW3>sMsQ>0?xW%SjM,{qY8j]fwr47aQ =ƬP;#K¹'o4;* XjH݃Ȼ~�qOОWesbIJn�ol^M(\:V vig5$HN߻ey)&+:-P. Iq�iX /svdEJqǥ,7Ĥ$)9Եh.^; 1Q oĆ&Ge@2TjJeY0G=>ʍ9qlw:FB(>rc�8^]Yb-uSiW<#/85mȵ{Ф&-"tZZ"v:}+QI{Ju`?늉Kvtg pmo*i\qq=@TmWvRs m*' 2;ZspWq8}ȘůWk�_qǢ@lPXڴM$>zVܘ &<Tr6�i_%F 9]_d�]:~#R# ϭuk*B28бl1&`3�jW6Jqm2#:dv~u 07|q% N}kK;VCխb[6vD{[~-[98pKxRnB]8($1܂J?z+՝rW;c LS'"0lh6|RڬӟlECem2ϻTj[#R &B{_6h0$ : ζ"imʩ?*Q^Ags d=GvYineXTD>g-@'Yb b@Wo%HHM4EG~VY.QK 9su9ʞ i/H\[#([/"(w6H _/P@x⢷ۢ0g5BҼ;՜: Mu}6;ȶhOOƼ�ɝV@9s2qo=xf�Dm@Gjݔ;pEv>rqֺ{}<pGB19Kqkݗ8X&;$ȥbqTQITFs ?K)UzTOXNsQIe?�JSrI'z&صEW  9,G\UUQsZR,1)^H?jfUgy2be<Hl)ysŔJ8Scn#ߏ�]  'vN>R8ʎW�Lu7|DZ'kM4xJIvV!&F?*6y$wVg$/Nڟ6M%@֜4NpMC0 Q5 Y� $ޅJN堅A@3NH<Hű"1$Idvރ"[h�zl63{;X,Ynd;3vTẒ;ͫlP&�A'Gaj9cC^Dޣ^! #pZ$zЬ5rLFk|dJIt1VoL���3޻ ;]2ǕT8aݏ"prWS{s#<V5'ǰ}9+Ң[`Any=j葨%v /2|�$6a{߸ 5ծEqSEYx?`$:hm˻x9FcR\5\$t Êܸ@r(3[]-6 o|ρ .tśc zj]k6U�Hާ8*P#k,E[_4WsYZ>v&yR3Fpޮ5|oSs !}nOꭙB# R%y9U좡fD<:ME:j0cĺ|I1“:kX�ry'Tk"HIzzUx‘rkkli(ŒZxCwbŖ:qJMDn�<Uy,3,><d(cŖ�yDIV"$sҸ7wGJQd ؏hRjZ;}bV!qVRHe p'-6`Ҵ9K AUʳݟR6#8觏ƸƆPdzRJ׉E#3ҧIfW8V}ig1$r+5V"T\HKNd8�*5%ks:oFzi�ʹE] yQs$h K1DCq5^/e _HT,r0@9"⋅EeAGV|g]KDG˳;Zɀ>a ʾp+_BbTK'b$ u &T9O:c3湹<QިU]f 4tf_ ^ԕXwgH?:@a+<?gIoAۓH<@FzgA/ <)#AQ^Fdc$q! ;KFJl/Ӡ2ΰl^O%$3�xcPo#7fE5"AFF]H\"m'ju�E5LcaݼTF̷ 4 |"Ye7Rx'#FH[14%B1LG+&wMvRN�Jf v�;<Аۆ0v߼fZKHJb7w4dҳ#f!AhG2Y:>^.:hm*u<g4C}M2� j1Bdw 1ɪH wsڨ4R'LiUU-Q)ipQWг{mtS:1tr1U.$X 8Zu @@N-{ FI]'V=dnh['<zUKwrWNNW 8c׽tpq}k3HF#@8j#� e~k]Qێs‚p(n;d�yXr 8ϥ1AWyPKܬl[$fp�0'�c"Ϙs�T7 r8<ޔ*RQ[kF0=H�^0sOM$_1+3clg҆}FW{}Z}�۸}j"rʊpGҠtoV@rNJ^/}F}y6G\wyr@p#$ Ƒ, 'QmRVE)< W##9#'#|2yS(0y[CZL}j Dtz[L&E9&Mnmǂh1ҧEg!r? G'n;N85sOވ}-t:Rsb󂼜:R rzgH;*s$^-+i[v#ϰMPFr.y流U}JV@([F&#Xc'=+'Kv":'Q# 'Y-wHp S^-N=j12ו V2 dbҵ\g,(Vqǹrl.@1P I<jke鷯^-Wі, uEXs�dAt9gk= &,H r l rzԷa�zҮPURM"0PG* v9)PIajZI7Α$U=Ty`cp>]=*Y6% RzX*NtqSlAV1{*{U.*!lg9~TyG'-!20+~xT!ݎ98w'v?švv)i0ʸp1UJ 2s+~5IHΡcb@wӮqsgH`ޚ3mWI -r;( jsE>T9670յ&0[cq`4JOWG{vҲiG~n~|ʣ1*$4dȯR �*׬cH$|<f7㑊 drszz{e88Z4=jpxZ=uH}qɫl!]ɸz[:#nr? μ\CӚO8ϵfM֡F :tJL$}hA G!#YT {[g'Osd#h Ik/#DL_ʶ^WQerB954D(뎧+@c50r^KkYT3�!Yvcب_EL|#کٕ9Ͱ,>^A$jlCF݄*(=zUieE[CERۘЇ;-25p�n'5m2" �㜎R||~zlwLKe&g�Z2ۖ\^-Aw2V*I {~ B0gN?*siQ1< l 8Riw5qb ,ۑ;84YhHpppj'(F KlY@\mΔ9uרrE#y '<a2?֩|v}) AqTOF)PF Qvk:͂ S~b9UTB�" ľ!r>r|151lEH8BByDe-*QZu;c%OYG$$ցV5sj#%)c9/eTEu]iEaiD,x,KOXݔH9�𤶺e-5hѰ.R:WKm<K\vW/f wqZEv~-$dѱ`a%xօwz8T mԠlqoB:$zw Ams8$z�EJ�Uk 2=pMFeEoO9Zr7Md(6nI&@~^1U2́*ܯ~,jUNp3JG]g21A^XDܣ>�M*( b6z=JEwOz«6^<x#0 Dž'-StqR@&7zdcֻ~$[XAbqcV7OAV-򁃞==rA v0ΪUE^r@?_ƪ*-8'מrGi+1ږa!sVmEVSjYQh{3RbTBT ;N]̊s�Zc汵l6"LgZط@TqY6@ݴ88^r9.HV qJȝ[絎&mk*q!9 � F>ϗRMM X`ukoQ([F0ĺ?ƥ]d�p1⪫�BT`{>jڴ�"q:#r3nlap sU# `5`Uݐ`dj%tX2fksV8`߰nnOS/VYZG [AcTrĊ]4K}̙XsztL9 A-9_̜.G8ճLGpH}ʩPFN1Kib"r\SԪO!q` �9sPmT׀&$2烊@qInʀ GzT,2*KTL$vv0rйr}i(ܜi˒ēB8RƙP��gk DB08޶a<g<W<ͩWO,yU`?:B3's Z 2~Vmhf2S* J-ό)\XCR{6-w6Kb�q:}j!1O w^]d@c�S\$bWx+LP�͵.$HXF=I� uT.y#>jh>敥EX?0:df\[�Z}븂r!NsLC1P:g<P U& c"G^ֽgAl-R\+˼V:-Պᾞ떳!FyM# ~I~1U7cTb~@{u�T{vX T7S@ }0++w5N<9qMxc{j{H Q9?JkAq+KZ$ۇZ[ <|*))9�I>Oa[[*8$m�=* Fy:Tj7ǕlŗE 09FdkL8'=T\⧓Jl憆�UFE$uPZ2QʌY448?.*1 FJ�@+}H"cSr>'sT CE9H}qŽ?5�>_ NϥSU"g6r[ s]q�]#qURN ;D$4ĐQ_@�ͤ\�CAVO smhn4 #֟T1|gHusFNBPrzc42sm ʣ]m6kIШ9˨d楸\fm?WLm݃ߌSwA:ua1cR=52ww#:#r`�8O\OLXPcZk YrObW0ȹV2k`"sr:zqUnؙ0_T+Is;2ϙ OP͐13]I' Ǡ)h0 08?[wФzw# |T>[gҭ\[1R1Uſ@sֱܨnx�[6۫'< `R6=:U A�Fy%M7m(?֕ǘdiޥ;ڢRqW[Y0'V8aU`ʀW';sչ1ۧM%{w:rZPBnW}Bjjs:KGmRyR )g'*JC+ry+)c�?Jٷ!!$zw+.7uxj䎘!y]8>C�_:[%=1S)>[ N#ZeRO9j�hsҳm] &bt)mR4ѲF0(';ې}ɭt1Ɖ<f4KfN*, ϓOJI+vu $U{) >dXxUn&VA0A?Rg{h]R"� ңA~aLua)R9{S@r*d< w�ۙ�R?Ωԫؙ~UaךLm8C zwG>hW#CѾ=XFJlcHJI4=Vq;܂1ܕ;w{UfI< UB'ȭ~+3*}1UGW, PFN95'mG'2%Y#ڡIcNgmw1֪MP8ϭKՉhk#˜qڴhsZB#F} I95wVGAl:!V%ϨJw,G;jF[uEAg"ߩPܒ4V`xR*.@$#ӽ!XYX�+ d#FwέE/xRoc2c|Z8pJ2IbQ-ۖ+ߌ4Ga�a=TvD%c|'+?LYbiمi2zօ0drHUY;ccz$k+F=xKo"\t<{,6e*p>a#!qk L2;`�Zq}^iX�0#?zm  G5z7jĢ&Ps e⤘.zsTZ+؃O_ҥ;;%t5B7m.iIU y֓ :$' v-"j; 6cYPdֹW+IhBPIdIcb<P87N7ܥH)c9=RG͂OzBAV{Qn l1+cޡbLckԲ?b/5wI yi-- |rGq~\Tg!Iǧ#FFr#7yby*yT2G#tJ�O5^b 9cP'-KrI$Rxu`Pe@B*T'ޣprI?B(ӗP#$2mI6HGTRܮIսR�9 TrF X_zcISvNH(M5u#x�l*(Þ ӓ]Pz1@>*ON{QO08E.ń_4p ; Lӆ@ ([RYST1܀|oWp8PM2OLVqJH2E(VD*6=;sKtH<�yc N zUnd3ߓ�Tk-v oS�eK Bj]mRuqL3p}X*MҚp-)*:ǵ[ \Q`ԅ^RD꣉1WewG{ *]nZn PGi$^� Mkn]0@SҖvOSsv4FW9>�A]\iр-zԱPôrFzSdpIWB].<ZQ@XË}v=zWkdR?ϽJ.Bu~p0!RHήx8V5)Z8*:+a >OH<UFFN QTxVF�^$aUp&Mx][~Syi(˓CoL|PrA犹#!L$ǿ<Uu" 6⡺*OrEnC~A+P[ g~M % :{,Oy60sCð=W%֧=G] K"wqLVPN(RS$qWEX<MKw!cUUN*U;u21: U=*m5NQl¯zt5L 3(n�Vg{8^hB;[�*ơm8@ S.ssEbxO2d(@#ۚtRn\A#P̪mT Xs[t3n��B ܓӦ} hTc8rٕr0.m^r**SNx8SMGɌqԴisa v9ZX %VM5D`F m!;խRsqo*̓CW*mm# & �;y&`NhfR8?Zxmi~5;0vvǂ ܻF*_.��T3H6d3Sjۍ"+Rs$ vt\anj`1kGNʻI ӻ[(kihT~s|xҬ$c8zK�T)R8MA!פnѢɀ"ruFfQ`{uKlяNj[Q]N;T 1l9KvVFY}I]G^}jSo[1gen=)c"1w9D Mݽ Onr Ճq^Aj8s8?Θ:@4%lhڪ$sYA#:Tt\MiGO M=0? f^n'%q}99*` ^MCri ߜQnYȶv<rsԚnjl<P*Q4qA#F^8:xTr*zZXI(r2XL}d.A*& Tu_ԖA =01PPMVi--8tlj[]A}p^> Ȧ #9ʣ89 =GzFy�9H]ܔ$r: {w(RwOyQ <Tfhm�; )\EXÌ1cӯSndWFFpANIQ؍9 wLĔ2::nVOVe!x@>J唹xLB[د*'ab dl*FG׽O;Zp�ZvNJ6e ?Z�xN2Ĝ9'=)mkYD\':ʳN?!��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Flickr-Upload-1.6/t/upload.t������������������������������������������������������������������������0000644�0001750�0001750�00000001236�12375006452�016214� 0����������������������������������������������������������������������������������������������������ustar �sseverin������������������������sseverin���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������use Test::More qw(no_plan); BEGIN { use_ok('Flickr::Upload') }; my $api_key = '8dcf37880da64acfe8e30bb1091376b7'; my $not_so_secret = '2f3695d0562cdac7'; # grab auth token. If none, fail nicely. my $pw = '******'; open( F, '<', 't/password' ) || (print STDERR "No password file\n" && exit 0); $pw = <F>; chomp $pw; close F; my $ua = Flickr::Upload->new({'key'=>$api_key, 'secret'=>$not_so_secret}); ok(defined $ua); my $rc = $ua->upload( 'photo' => 't/testimage.jpg', 'auth_token' => $pw, 'tags' => "test kernel perl cat dog", 'description' => "Flickr Upload test for $0", 'is_public' => 0, 'is_friend' => 0, 'is_family' => 0, ); ok( defined $rc ); exit 0; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Flickr-Upload-1.6/t/progress.t����������������������������������������������������������������������0000644�0001750�0001750�00000002243�12375006452�016573� 0����������������������������������������������������������������������������������������������������ustar �sseverin������������������������sseverin���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������use Test::More qw(no_plan); BEGIN { use_ok('Flickr::Upload') }; use_ok('LWP::UserAgent'); $HTTP::Request::Common::DYNAMIC_FILE_UPLOAD = 1; my $api_key = '8dcf37880da64acfe8e30bb1091376b7'; my $not_so_secret = '2f3695d0562cdac7'; # grab auth token. If none, fail nicely. my $pw = '******'; open( F, '<', 't/password' ) || (print STDERR "No password file\n" && exit 0); $pw = <F>; chomp $pw; close F; my $ua = Flickr::Upload->new({'key'=>$api_key, 'secret'=>$not_so_secret}); ok(defined $ua); my $req = $ua->make_upload_request( 'description' => "Flickr Upload test for $0", 'auth_token' => $pw, 'photo' => 't/testimage.jpg', 'tags' => "test kernel perl cat dog", 'is_public' => 0, 'is_friend' => 0, 'is_family' => 0, ); # all we want to do is replace the default content generator with something # that will spit out a '.' for each kilobyte and pass the data back. my $gen = $req->content(); die unless ref($gen) eq "CODE"; $req->content( sub { my $chunk = &$gen(); print "." x (length($chunk)/1024) if defined $chunk; return $chunk; } ); $ua->agent( "$0/1.0" ); print "# "; my $photoid = $ua->upload_request( $req ); print "\n"; ok( defined $photoid ); exit 0; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Flickr-Upload-1.6/lib/������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�12757102637�015052� 5����������������������������������������������������������������������������������������������������ustar �sseverin������������������������sseverin���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Flickr-Upload-1.6/lib/Flickr/�����������������������������������������������������������������������0000755�0001750�0001750�00000000000�12757102637�016264� 5����������������������������������������������������������������������������������������������������ustar �sseverin������������������������sseverin���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Flickr-Upload-1.6/lib/Flickr/Upload.pm��������������������������������������������������������������0000644�0001750�0001750�00000041363�12757102502�020044� 0����������������������������������������������������������������������������������������������������ustar �sseverin������������������������sseverin���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Flickr::Upload; use strict; use warnings; use LWP::UserAgent; use HTTP::Request::Common; use Net::OAuth; use URI::Escape; use Flickr::API; use XML::Simple qw(:strict); use Digest::MD5 qw(md5_hex); use Encode qw(encode_utf8); use Carp; our $VERSION = '1.6'; our @ISA = qw(Flickr::API); =head1 NAME Flickr::Upload - Upload images to C<flickr.com> =head1 SYNOPSIS use Flickr::Upload; my $ua = Flickr::Upload->new( { 'key' => '90909354', 'secret' => '37465825' }); $ua->upload( 'photo' => '/tmp/image.jpg', 'auth_token' => $auth_token, 'tags' => 'me myself eye', 'is_public' => 1, 'is_friend' => 1, 'is_family' => 1 ) or die "Failed to upload /tmp/image.jpg"; =head1 DESCRIPTION Upload an image to L<flickr.com>. =head1 METHODS =head2 new =over =item Using Flickr Authentication my $ua = Flickr::Upload->new( { 'key' => '90909354', 'secret' => '37465825' }); =item Using OAuth Authentication my $ua = Flickr::Upload->new( { 'consumer_key' => 'your_api_key', 'consumer_secret' => 'your_app_secret', }); =item Retrieve saved configuration (possibly including OAuth access token) my $config_file = "$ENV{HOME}/saved-flickr.st"; my $ua = Flickr::Upload->import_storable_config($config_file); =back Instantiates a L<Flickr::Upload> instance, using either the Flickr Authentication or the OAuth Authentication. The C<key> or C<consumer_key> argument is your API key and the C<secret> or C<consumer_secret> argument is the API secret associated with it. To get an API key and secret, go to L<https://www.flickr.com/services/api/key.gne>. The resulting L<Flickr::Upload> instance is a subclass of L<Flickr::API> and can be used for any other Flickr API calls. As such, L<Flickr::Upload> is also a subclass of L<LWP::UserAgent>. =head2 upload my $photoid = $ua->upload( 'photo' => '/tmp/image.jpg', 'auth_token' => $auth_token, 'tags' => 'me myself eye', 'is_public' => 1, 'is_friend' => 1, 'is_family' => 1 'async' => 0, ); Taking a L<Flickr::Upload> instance C<$ua> as an argument, this is basically a direct interface to the Flickr Photo Upload API. Required parameters are C<photo> and, when using Flickr Authentication, C<auth_token>. Note that the C<auth_token> must have been issued against the API key and secret used to instantiate the uploader. When using OAuth, C<auth_token> is not required, and the L<Flickr::Upload> instance must instead contain a valid L<Net::OAuth> access token which can be added by calling the L<Flickr::API> C<oauth_access_token> method. Returns the resulting identifier of the uploaded photo on success, C<undef> on failure. According to the API documentation, after an upload the user should be directed to the page L<https://www.flickr.com/tools/uploader_edit.gne?ids=$photoid>. If the C<async> option is non-zero, the photo will be uploaded asynchronously and a successful upload returns a ticket identifier. See L<https://www.flickr.com/services/api/upload.async.html>. The caller can then periodically poll for a photo id using the C<check_upload> method. Note that photo and ticket identifiers aren't necessarily numeric. =cut sub upload { my $self = shift; die '$self is not a Flickr::Upload' unless $self->isa('Flickr::Upload'); my %args = @_; # these are the only things _required_ by the uploader. die "Can't read photo '$args{'photo'}'" unless $args{'photo'} and (ref $args{'photo'} eq "ARRAY" or -f $args{'photo'}); die "Missing 'auth_token'" unless $self->is_oauth or defined $args{'auth_token'}; # create a request object and execute it my $req = $self->make_upload_request( %args ); return undef unless defined $req; return $self->upload_request( $req ); } =head2 check_upload my %status2txt = (0 => 'not complete', 1 => 'completed', 2 => 'failed'); my @rc = $ua->check_upload( @ticketids ); for( @rc ) { print "Ticket $_->{id} has $status2txt{$_->{complete}}\n"; print "\tPhoto id is $_->{photoid}\n" if exists $_->{photoid}; } This function will check the status of one or more asynchronous uploads. A list of ticket identifiers are provided (C<@ticketids>) and each is checked. This is basically just a wrapper around the Flickr API C<flickr.photos.upload.checkTickets> method. On success, a list of hash references is returned. Each hash contains a C<id> (the ticket id), C<complete> and, if completed, C<photoid> members. C<invalid> may also be returned. Status codes (for C<complete>) are as documented at L<https://www.flickr.com/services/api/upload.async.html> and, actually, the returned fields are identical to those listed in the C<ticket> tag of the response. The returned list isn't guaranteed to be in any particular order. This function polls a web server, so avoid calling it too frequently. =cut sub check_upload { my $self = shift; die '$self is not a Flickr::API' unless $self->isa('Flickr::API'); return () unless @_; # no tickets my $res = $self->execute_method( 'flickr.photos.upload.checkTickets', { 'tickets' => ((@_ == 1) ? $_[0] : join(',', @_)) } ); return () unless defined $res and $res->{success}; # FIXME: better error feedback my @rc; return undef unless defined $res->{tree} and exists $res->{tree}->{'children'}; for my $n ( @{$res->{tree}->{'children'}} ) { next unless defined $n and exists $n->{'name'} and $n->{'children'}; next unless $n->{'name'} eq "uploader"; for my $m (@{$n->{'children'}} ) { next unless exists $m->{'name'} and $m->{'name'} eq 'ticket' and exists $m->{'attributes'}; # okay, this is maybe a little lazy... push @rc, $m->{'attributes'}; } } return @rc; } =head2 make_upload_request my $req = $uploader->make_upload_request( 'auth_token' => '82374523', 'tags' => 'me myself eye', 'is_public' => 1, 'is_friend' => 1, 'is_family' => 1 ); $req->header( 'X-Greetz' => 'hi cal' ); my $resp = $ua->request( $req ); Creates an L<HTTP::Request> object loaded with all the flick upload parameters. This will also sign the request, which means you won't be able to mess any further with the upload request parameters. Takes all the same parameters as L<upload>, except that the photo argument isn't required. This in intended so that the caller can include it by messing directly with the HTTP content (via C<$DYNAMIC_FILE_UPLOAD> or the L<HTTP::Message> class, among other things). See C<t/> directory from the source distribution for examples. Returns a standard L<HTTP::Response> POST object. The caller can manually do the upload or just call the L<upload_request> function. =cut sub make_upload_request { my $self = shift; die '$self is not a Flickr::Upload' unless $self->isa('Flickr::Upload'); my %args = @_; # _required_ by the uploader. unless ($self->is_oauth) { die "Missing 'auth_token' argument" unless $args{'auth_token'}; } else { croak "OAuth access token needed" unless defined $self->{oauth}->{token}; } my $uri = $args{'uri'} || 'https://api.flickr.com/services/upload/'; # passed in separately, so remove from the hash delete $args{uri}; # Flickr::API includes this with normal requests, but we're building a custom # message. $args{'api_key'} = $self->{'api_key'} unless $self->is_oauth; # photo is _not_ included in the sig my $photo = $args{photo}; delete $args{photo}; unless( $self->is_oauth ) { $args{'api_sig'} = $self->_sign_args(\%args); } else { my %oauth = ( 'nonce' => $self->_make_nonce(), 'consumer_key' => $self->{oauth}->{consumer_key}, 'consumer_secret' => $self->{oauth}->{consumer_secret}, 'timestamp' => time, 'signature_method' => $self->{oauth}->{signature_method}, 'version' => $self->{oauth}->{version}, 'token' => $self->{oauth}->{token}, 'token_secret' => $self->{oauth}->{token_secret}, ); $oauth{extra_params} = \%args; $oauth{request_method} = 'POST'; $oauth{request_url} = $uri; $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A; my $req = Net::OAuth->request( "protected resource" )->new( %oauth ); $req->sign(); my $tmp_body = $req->to_post_body(); %args = (); foreach (split '&', $tmp_body) { my ($name, $val) = split '=', $_, 2; $args{$name} = URI::Escape::uri_unescape( $val ); } } # unlikely that the caller would set up the photo as an array, # but... if( defined $photo ) { $photo = [ $photo ] if ref $photo ne "ARRAY"; $args{photo} = $photo; } my $req = POST $uri, 'Content_Type' => 'form-data', 'Content' => \%args; return $req; } =head2 upload_request my $photoid = upload_request( $ua, $request ); Taking (at least) L<LWP::UserAgent> and L<HTTP::Request> objects as arguments, this executes the request and processes the result as a flickr upload. It's assumed that the request looks a lot like something created with L<make_upload_request>. Note that the request must be signed according to the Flickr API authentication rules. Returns the resulting identifier of the uploaded photo (or ticket for asynchronous uploads) on success, C<undef> on failure. According to the API documentation, after an upload the user should be directed to the page L<https://www.flickr.com/tools/uploader_edit.gne?ids=$photoid>. =cut sub upload_request { my $self = shift; die "$self is not a LWP::UserAgent" unless $self->isa('LWP::UserAgent'); my $req = shift; die "expecting a HTTP::Request" unless $req->isa('HTTP::Request'); # Try 3 times to upload data. Without this flickr_upload is bound # to die on large uploads due to some miscellaneous network # issues. Timeouts on flickr or something else. my ($res, $xml); my $tries = 3; for my $try (1 .. $tries) { # Try to upload $res = $self->request( $req ); return () unless defined $res; if ($res->is_success) { $xml = XMLin($res->decoded_content, KeyAttr=>[], ForceArray=>0); return () unless defined $xml; last; } else { my $what_next = ($try == $tries ? "giving up" : "trying again"); my $status = $res->status_line; print STDERR "Failed uploading attempt attempt $try/$tries, $what_next. Message from server was: '$status'\n"; next; } } my $photoid = $xml->{photoid}; my $ticketid = $xml->{ticketid}; unless( defined $photoid or defined $ticketid ) { print STDERR "upload failed:\n", $res->decoded_content(), "\n"; return undef; } return (defined $photoid) ? $photoid : $ticketid; } =head2 file_length_in_encoded_chunk $HTTP::Request::Common::DYNAMIC_FILE_UPLOAD = 1; my $photo = 'image.jpeg'; my $photo_size = (stat($photo))[7]; my $req = $ua->make_upload_request( ... ); my $gen = $req->content(); die unless ref($gen) eq "CODE"; my $state; my $size; $req->content( sub { my $chunk = &$gen(); $size += Flickr::Upload::file_length_in_encoded_chunk(\$chunk, \$state, $photo_size); warn "$size bytes have now been uploaded"; return $chunk; } ); $rc = $ua->upload_request( $req ); This subroutine is tells you how much of a chunk in a series of variable size multipart HTTP chunks contains a single file being uploaded given a reference to the current chunk, a reference to a state variable that lives between calls, and the size of the file being uploaded. It can be used used along with L<HTTP::Request::Common>'s $HTTP::Request::Common::DYNAMIC_FILE_UPLOAD facility to implement upload progress bars or other upload monitors, see L<flickr_upload> for a practical example and F<t/progress_request.t> for tests. =cut sub file_length_in_encoded_chunk { my ($chunk, $s, $img_size) = @_; $$s = {} unless ref $$s eq 'HASH'; # If we've run past the end of the image there's nothing to do but # report no image content in this sector. return 0 if $$s->{done}; unless ($$s->{in}) { # Since we haven't found the image yet append this chunk to # our internal data store, we do this because we have to do a # regex match on m[Content-Type...] which might be split # across multiple chunks $$s->{data} .= defined $$chunk ? $$chunk : ''; if ($$s->{data} =~ m[Content-Type: .*?\r\n\r\n]g) { # We've found the image inside the stream, record this, # delete ->{data} since we don't need it, and see how much # of the image this particular chunk gives us. $$s->{in} = 1; my $size = length substr($$s->{data}, pos($$s->{data}), -1); delete $$s->{data}; $$s->{size} = $size; if ($$s->{size} >= $img_size) { # The image could be so small that we've already run # through it in chunk it starts in, mark as done and # return the total image size $$s->{done} = 1; return $img_size; } else { return $$s->{size}; } } else { # Are we inside the image yet? No! return 0; } } else { my $size = length $$chunk; if (($$s->{size} + $size) >= $img_size) { # This chunk finishes the image $$s->{done} = 1; # Return what we had left return $img_size - $$s->{size}; } else { # This chunk isn't the last one $$s->{size} += $size; return $size; } } } =head2 photosets_create Calls Flickr's "flickr.photosets.create" method, to create a new Set. The set will use the PrimaryPhotoID as the thumbnail photo. returns: UNDEF on failure, PhotosetID on success. my $photoset_id = $ua->photosets_create( title => 'title', description => 'description', primary_photo_id => ID, auth_token => AUTH_TOKEN ); $ua->photosets_addphoto ( photoset_id => $photoset_id, photo_id => ID ); =cut sub photosets_create { my $self = shift; die '$self is not a Flickr::API' unless $self->isa('Flickr::API'); my %args = @_; carp "Missing 'auth_token' parameter for photosets_create()" unless exists $args{'auth_token'}; my $auth_token = $args{'auth_token'}; carp "Missing 'title' parameter for photosets_create()" unless exists $args{'title'} && length($args{'title'})>0; my $title = $args{'title'}; carp "Missing 'primary_photo_id' parameter for photosets_create()" unless exists $args{'primary_photo_id'}; my $primary_photo_id = $args{'primary_photo_id'}; carp "Invalid primary_photo_id ($primary_photo_id) value (expecting numeric ID)" unless $primary_photo_id =~ /^[0-9]+$/; my $description = ( exists $args{'description'} ) ? $args{'description'} : "" ; my $res = $self->execute_method( 'flickr.photosets.create', { 'title' => $title, 'description' => $description, 'primary_photo_id' => $primary_photo_id, 'auth_token' => $auth_token, } ) ; #TODO: Add detailed error messages return undef unless defined $res and $res->{success}; my $hash = XMLin($res->decoded_content(), KeyAttr=>[], ForceArray=>0); my $photoset_id = $hash->{photoset}->{id}; if ( ! defined $photoset_id ) { warn "Failed to extract photoset ID from response:\n" . $res->decoded_content() . "\n\n"; return undef; } return $photoset_id ; } =head2 photosets_addphoto Calls Flickr's "flickr.photosets.addPhoto" method, to add a (existing) photo to an existing set. returns: UNDEF on failure, TRUE on success. my $photoset_id = $ua->photosets_create( title => 'title', description => 'description', primary_photo_id => ID, auth_token => AUTH_TOKEN ); $ua->photosets_addphoto ( photoset_id => $photoset_id, photo_id => ID ); =cut sub photosets_addphoto { my $self = shift; die '$self is not a Flickr::API' unless $self->isa('Flickr::API'); my %args = @_; carp "Missing 'auth_token' parameter for photosets_addphoto()" unless exists $args{'auth_token'}; my $auth_token = $args{'auth_token'}; carp "Missing 'photoset_id' parameter for photosets_addphoto()" unless exists $args{'photoset_id'}; my $photoset_id = $args{'photoset_id'}; carp "Missing 'photo_id' parameter for photosets_addphoto()" unless exists $args{'photo_id'}; my $photo_id = $args{'photo_id'}; my $res = $self->execute_method( 'flickr.photosets.addPhoto', { 'photoset_id' => $photoset_id, 'photo_id' => $photo_id, 'auth_token' => $auth_token, } ) ; #TODO: Add detailed error messages return undef unless defined $res; return $res->{success}; } # Private method adapted from Flickr::API # See: https://www.flickr.com/services/api/auth.howto.web.html sub _sign_args { my $self = shift; my $args = shift; my $sig = $self->{api_secret}; for(sort { $a cmp $b } keys %$args) { $sig .= $_ . (defined($args->{$_}) ? $args->{$_} : ""); } return md5_hex($self->{unicode} ? encode_utf8($sig) : $sig); } 1; __END__ =head1 SEE ALSO L<https://www.flickr.com/services/api/> L<Flickr::API> =head1 AUTHORS Christophe Beauregard, L<cpb@cpan.org> E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason, <avar@cpan.org> =head1 COPYRIGHT AND LICENSE This module is not an official Flickr.com (or Ludicorp, or Yahoo) service. Copyright (C) 2004-2008 by Christophe Beauregard and 2008-2009 by E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Flickr-Upload-1.6/META.yml��������������������������������������������������������������������������0000664�0001750�0001750�00000001165�12757102637�015562� 0����������������������������������������������������������������������������������������������������ustar �sseverin������������������������sseverin���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������--- abstract: 'Module for uploading images to flickr.com' author: - 'Christophe Beauregard <cpb@cpan.org>' build_requires: ExtUtils::MakeMaker: 0 configure_requires: ExtUtils::MakeMaker: 0 dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.04, CPAN::Meta::Converter version 2.120351' license: unknown meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Flickr-Upload no_index: directory: - t - inc requires: Flickr::API: 1.09 Getopt::Long: 1 HTTP::Request::Common: 1 LWP::UserAgent: 1 Net::OAuth: 0.28 Pod::Usage: 1 XML::Simple: 2 version: 1.6 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������