flickarr 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/HELP.txt +3 -0
- data/README.md +1 -1
- data/{HOWTO.md → doc/HOWTO.md} +18 -8
- data/doc/how-to-1.png +0 -0
- data/doc/how-to-2.png +0 -0
- data/doc/how-to-3.png +0 -0
- data/doc/how-to-4.png +0 -0
- data/doc/how-to-5.png +0 -0
- data/doc/how-to-6.png +0 -0
- data/lib/flickarr/cli.rb +30 -0
- data/lib/flickarr/collection.rb +4 -2
- data/lib/flickarr/photo_set.rb +4 -2
- data/lib/flickarr/post.rb +16 -2
- data/lib/flickarr/version.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c94bc7b1f3ffc434e88faf5a632728406d6c297e2256354d5053af345be8d649
|
|
4
|
+
data.tar.gz: d2ed214497de643eab92773755bd4b33a655532266736fd4d964d0a3cf1a07a4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0060a46d0cb77a92dfaae7a5cc9a21f2979dfa4b7d36d67a3c3bfb2930702e00751f8f9a204455ceb74a55199f7f23f7b93f87e1955889d406326a0a673babb0
|
|
7
|
+
data.tar.gz: 439ae3fbb665d42b07302b34b7d9070ca06e5064d227f998fcf7c8cb5d0a74dd08adf2b9adceec22f7eecb8e7c1cfe274b4baaa431ee0900dcdd1b7efffd7af1
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.1.6] - 2026-03-31
|
|
4
|
+
|
|
5
|
+
- Add `version` and `help` commands to help output
|
|
6
|
+
- Retry transient Flickr API errors ("service not currently available") with backoff, instead of crashing
|
|
7
|
+
- Truncate very long slugs to prevent `Errno::ENAMETOOLONG` on filenames exceeding 255 bytes
|
|
8
|
+
|
|
3
9
|
## [0.1.5] - 2026-03-23
|
|
4
10
|
|
|
5
11
|
- Add `flickarr version` / `-v` / `--version` command
|
data/HELP.txt
CHANGED
|
@@ -48,3 +48,6 @@ COMMANDS:
|
|
|
48
48
|
export:collections Export all collections (groups of albums)
|
|
49
49
|
export:collections <URL> Export a single collection (groups of albums) with references to albums
|
|
50
50
|
export:profile Export Flickr profile to archive
|
|
51
|
+
|
|
52
|
+
help, -h, --help Show this help
|
|
53
|
+
version, -v, --version Show version (checks for updates)
|
data/README.md
CHANGED
data/{HOWTO.md → doc/HOWTO.md}
RENAMED
|
@@ -22,16 +22,24 @@ gem install flickarr
|
|
|
22
22
|
|
|
23
23
|
You need your own Flickr API key and secret. Flickarr uses these to authenticate with the Flickr API on your behalf.
|
|
24
24
|
|
|
25
|
-
1. Go to https://
|
|
26
|
-
|
|
25
|
+
1. Go to https://flickr.com/services/apps/create and click the [Request an API Key](https://flickr.com/services/apps/create/apply) link in the **Get your API Key** section.
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
2. On the next page https://flickr.com/services/apps/create/apply click "[Apply for a Non-Commercial Key](https://flickr.com/services/apps/create/noncommercial)"
|
|
29
|
+
|
|
30
|
+

|
|
31
|
+
|
|
27
32
|
3. Fill in the form:
|
|
28
33
|
- **Application Name**: anything you want (e.g. "Flickarr Export")
|
|
29
34
|
- **Description**: anything (e.g. "Personal archive export")
|
|
35
|
+
|
|
36
|
+

|
|
37
|
+
|
|
30
38
|
4. Submit the form
|
|
39
|
+
|
|
31
40
|
5. Copy your **Key** and **Secret** from the confirmation page
|
|
32
41
|
|
|
33
|
-
|
|
34
|
-
<!-- TODO: screenshot of Flickr API key and secret confirmation page -->
|
|
42
|
+

|
|
35
43
|
|
|
36
44
|
## 4. Initialize Flickarr
|
|
37
45
|
|
|
@@ -73,10 +81,12 @@ This starts the OAuth flow:
|
|
|
73
81
|
2. Flickr asks you to authorize the app — click "OK, I'll Authorize It"
|
|
74
82
|
3. Flickr shows you a verification code (9 digits, like `888-675-309`)
|
|
75
83
|
4. Copy that code
|
|
84
|
+
|
|
85
|
+

|
|
86
|
+
|
|
76
87
|
5. Paste the code back into your terminal
|
|
77
88
|
|
|
78
|
-
|
|
79
|
-
<!-- TODO: screenshot of Flickr verification code page -->
|
|
89
|
+

|
|
80
90
|
|
|
81
91
|
After authenticating, your access tokens, username, and user ID are saved to the config file. You only need to do this once.
|
|
82
92
|
|
|
@@ -134,7 +144,7 @@ flickarr export:albums
|
|
|
134
144
|
Export a single album by URL:
|
|
135
145
|
|
|
136
146
|
```sh
|
|
137
|
-
flickarr export:sets https://
|
|
147
|
+
flickarr export:sets https://flickr.com/photos/USERNAME/sets/72157718538273371/
|
|
138
148
|
```
|
|
139
149
|
|
|
140
150
|
Collections (groups of albums) work the same way:
|
|
@@ -146,7 +156,7 @@ flickarr export:collections
|
|
|
146
156
|
Export a single collection by URL:
|
|
147
157
|
|
|
148
158
|
```sh
|
|
149
|
-
flickarr export:collections https://
|
|
159
|
+
flickarr export:collections https://flickr.com/photos/USERNAME/collections/72157666222057746/
|
|
150
160
|
```
|
|
151
161
|
|
|
152
162
|
## 10. Check your progress
|
data/doc/how-to-1.png
ADDED
|
Binary file
|
data/doc/how-to-2.png
ADDED
|
Binary file
|
data/doc/how-to-3.png
ADDED
|
Binary file
|
data/doc/how-to-4.png
ADDED
|
Binary file
|
data/doc/how-to-5.png
ADDED
|
Binary file
|
data/doc/how-to-6.png
ADDED
|
Binary file
|
data/lib/flickarr/cli.rb
CHANGED
|
@@ -510,6 +510,18 @@ module Flickarr
|
|
|
510
510
|
end
|
|
511
511
|
warn "Failed to fetch page #{page} after retries: #{e.message}"
|
|
512
512
|
break
|
|
513
|
+
rescue Flickr::FailedResponse => e
|
|
514
|
+
if transient_flickr_error?(e)
|
|
515
|
+
page_retries -= 1
|
|
516
|
+
if page_retries.positive?
|
|
517
|
+
warn "Transient API error fetching page #{page}: #{e.message} — retrying in 5s (#{page_retries} left)"
|
|
518
|
+
sleep 5
|
|
519
|
+
retry
|
|
520
|
+
end
|
|
521
|
+
warn "Failed to fetch page #{page} after retries: #{e.message}"
|
|
522
|
+
break
|
|
523
|
+
end
|
|
524
|
+
raise
|
|
513
525
|
end
|
|
514
526
|
total = response.total.to_i
|
|
515
527
|
total_pages = response.pages.to_i
|
|
@@ -600,6 +612,17 @@ module Flickarr
|
|
|
600
612
|
log_error archive: archive, post_id: post_id, username: config.username, error: e
|
|
601
613
|
return
|
|
602
614
|
rescue Flickr::FailedResponse => e
|
|
615
|
+
if transient_flickr_error?(e)
|
|
616
|
+
retries -= 1
|
|
617
|
+
if retries.positive?
|
|
618
|
+
warn "Transient API error on post #{post_id}: #{e.message} — retrying in 5s (#{retries} left)"
|
|
619
|
+
sleep 5
|
|
620
|
+
retry
|
|
621
|
+
end
|
|
622
|
+
warn "Failed post #{post_id} after retries: #{e.message}"
|
|
623
|
+
log_error archive: archive, post_id: post_id, username: config.username, error: e
|
|
624
|
+
return
|
|
625
|
+
end
|
|
603
626
|
warn "Error on post #{post_id}: #{e.message}"
|
|
604
627
|
log_error archive: archive, post_id: post_id, username: config.username, error: e
|
|
605
628
|
return
|
|
@@ -618,6 +641,13 @@ module Flickarr
|
|
|
618
641
|
end
|
|
619
642
|
end
|
|
620
643
|
|
|
644
|
+
def transient_flickr_error? error
|
|
645
|
+
message = error.message.to_s.downcase
|
|
646
|
+
code = error.respond_to?(:code) ? error.code.to_s : ''
|
|
647
|
+
|
|
648
|
+
code == '105' || message.include?('not currently available') || message.include?('service unavailable')
|
|
649
|
+
end
|
|
650
|
+
|
|
621
651
|
def log_error archive:, post_id:, username:, error:
|
|
622
652
|
log_path = File.join archive, '_errors.log'
|
|
623
653
|
FileUtils.mkdir_p File.dirname(log_path)
|
data/lib/flickarr/collection.rb
CHANGED
|
@@ -28,13 +28,15 @@ module Flickarr
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def dirname
|
|
31
|
-
|
|
31
|
+
truncated = Post.truncate_slug(slug, id: id, ext: '')
|
|
32
|
+
[id, truncated].compact.join '_'
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
def sets_to_a
|
|
35
36
|
@set_refs.map do |s|
|
|
36
37
|
set_slug = s.title.to_s.slugify
|
|
37
|
-
|
|
38
|
+
set_slug = Post.truncate_slug(set_slug, id: s.id, ext: '')
|
|
39
|
+
set_dirname = [s.id, set_slug].compact.join('_')
|
|
38
40
|
|
|
39
41
|
{
|
|
40
42
|
description: s.description.to_s,
|
data/lib/flickarr/photo_set.rb
CHANGED
|
@@ -42,7 +42,8 @@ module Flickarr
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def dirname
|
|
45
|
-
|
|
45
|
+
truncated = Post.truncate_slug(slug, id: id, ext: '')
|
|
46
|
+
[id, truncated].compact.join '_'
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
def photos_to_a
|
|
@@ -110,7 +111,8 @@ module Flickarr
|
|
|
110
111
|
def relative_photo_path item
|
|
111
112
|
ext = item.media.to_s == 'video' ? 'mp4' : item.originalformat.to_s
|
|
112
113
|
slug = item.title.to_s.slugify
|
|
113
|
-
|
|
114
|
+
slug = Post.truncate_slug(slug, id: item.id, ext: ext)
|
|
115
|
+
base = [item.id, slug].compact.join('_')
|
|
114
116
|
date = if item.datetakenunknown.to_i.zero?
|
|
115
117
|
Date.parse item.datetaken
|
|
116
118
|
else
|
data/lib/flickarr/post.rb
CHANGED
|
@@ -8,6 +8,7 @@ require 'yaml'
|
|
|
8
8
|
module Flickarr
|
|
9
9
|
class Post
|
|
10
10
|
FLICKR_URL_PATTERN = %r{\Ahttps?://(?:www\.)?flickr\.com/photos/[^/]+/(\d+)}
|
|
11
|
+
MAX_FILENAME_BYTES = 255
|
|
11
12
|
|
|
12
13
|
def self.build info:, sizes:, exif: nil
|
|
13
14
|
case info.media.to_s
|
|
@@ -25,14 +26,25 @@ module Flickarr
|
|
|
25
26
|
id = item.id
|
|
26
27
|
title = item.title.to_s
|
|
27
28
|
slug = title.slugify
|
|
28
|
-
basename = [id, slug.empty? ? nil : slug].compact.join('_')
|
|
29
29
|
ext = item.media.to_s == 'video' ? 'mp4' : item.originalformat.to_s
|
|
30
|
+
slug = truncate_slug(slug, id: id, ext: ext)
|
|
31
|
+
basename = [id, slug].compact.join('_')
|
|
30
32
|
date = compute_date_from_list_item(item)
|
|
31
33
|
folder = date.strftime '%Y/%m/%d'
|
|
32
34
|
|
|
33
35
|
File.join archive_path, folder, "#{basename}.#{ext}"
|
|
34
36
|
end
|
|
35
37
|
|
|
38
|
+
def self.truncate_slug slug, id:, ext:
|
|
39
|
+
return nil if slug.nil? || slug.empty?
|
|
40
|
+
|
|
41
|
+
# "id_slug.ext" must fit in MAX_FILENAME_BYTES
|
|
42
|
+
max_slug = MAX_FILENAME_BYTES - id.to_s.bytesize - ext.to_s.bytesize - 2 # underscore + dot
|
|
43
|
+
return slug if slug.bytesize <= max_slug
|
|
44
|
+
|
|
45
|
+
slug[0, max_slug].chomp('-')
|
|
46
|
+
end
|
|
47
|
+
|
|
36
48
|
def self.compute_date_from_list_item item
|
|
37
49
|
if item.datetakenunknown.to_i.zero?
|
|
38
50
|
Date.parse item.datetaken
|
|
@@ -75,7 +87,9 @@ module Flickarr
|
|
|
75
87
|
end
|
|
76
88
|
|
|
77
89
|
def basename
|
|
78
|
-
[
|
|
90
|
+
longest_ext = [extension, 'yaml'].max_by(&:bytesize)
|
|
91
|
+
truncated = self.class.truncate_slug(slug, id: id, ext: longest_ext)
|
|
92
|
+
[id, truncated].compact.join '_'
|
|
79
93
|
end
|
|
80
94
|
|
|
81
95
|
def date_taken
|
data/lib/flickarr/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: flickarr
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shane Becker
|
|
@@ -91,10 +91,16 @@ files:
|
|
|
91
91
|
- CHANGELOG.md
|
|
92
92
|
- CODE_OF_CONDUCT.md
|
|
93
93
|
- HELP.txt
|
|
94
|
-
- HOWTO.md
|
|
95
94
|
- LICENSE.txt
|
|
96
95
|
- README.md
|
|
97
96
|
- Rakefile
|
|
97
|
+
- doc/HOWTO.md
|
|
98
|
+
- doc/how-to-1.png
|
|
99
|
+
- doc/how-to-2.png
|
|
100
|
+
- doc/how-to-3.png
|
|
101
|
+
- doc/how-to-4.png
|
|
102
|
+
- doc/how-to-5.png
|
|
103
|
+
- doc/how-to-6.png
|
|
98
104
|
- exe/flickarr
|
|
99
105
|
- lib/flickarr.rb
|
|
100
106
|
- lib/flickarr/auth.rb
|