Skip to content

Commit a568518

Browse files
Changing how thumbnails generate HTML. Now the first thumbnail *with a reference location* is chosen, instead of just the first.
1 parent 9633c41 commit a568518

File tree

2 files changed

+104
-48
lines changed

2 files changed

+104
-48
lines changed

lib/AppleNotesEmbeddedObject.rb

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,27 @@ def initialize(primary_key, uuid, uti, note)
2929
@uuid = uuid
3030
@type = uti
3131
@conforms_to = uti
32-
@note = note
33-
@version = @note.version
34-
@is_password_protected = @note.is_password_protected
35-
@backup = @note.backup
36-
@database = @note.database
37-
@logger = @backup.logger
32+
33+
# Set variables to defaults to be overridden later
34+
@version = AppleNoteStore::IOS_VERSION_UNKNOWN
35+
@is_password_protected = false
36+
@backup = nil
37+
@database = nil
38+
@logger = Logger.new(STDOUT)
39+
40+
@user_title = ""
3841
@filepath = ""
3942
@filename = ""
4043
@backup_location = nil
41-
@user_title = get_media_zusertitle_for_row
4244

45+
# Variable to hold ZMERGEABLEDATA objects
46+
@gzipped_data = nil
47+
48+
# Create an Array to hold Thumbnails
49+
@thumbnails = Array.new
50+
51+
# Create an Array to hold child objects, such as for a gallery
52+
@child_objects = Array.new
4353

4454
# Zero out cryptographic settings
4555
@crypto_iv = nil
@@ -49,21 +59,13 @@ def initialize(primary_key, uuid, uti, note)
4959
@crypto_iterations = nil
5060
@crypto_password = nil
5161

52-
if @is_password_protected
53-
add_cryptographic_settings
54-
end
55-
56-
@logger.debug("Note #{@note.note_id}: Created a new Embedded Object of type #{@type}")
62+
# Override the variables if we were given a note
63+
self.note=(note) if note
5764

58-
# Variable to hold ZMERGEABLEDATA objects
59-
@gzipped_data = nil
60-
61-
# Create an Array to hold Thumbnails and add them
62-
@thumbnails = Array.new
63-
search_and_add_thumbnails
65+
log_string = "Created a new Embedded Object of type #{@type}"
66+
log_string = "Note #{@note.note_id}: #{log_string}" if @note
6467

65-
# Create an Array to hold child objects, such as for a gallery
66-
@child_objects = Array.new
68+
@logger.debug(log_string)
6769
end
6870

6971
##
@@ -75,6 +77,11 @@ def note=(note)
7577
@backup = @note.backup
7678
@logger = @backup.logger
7779
@database = @note.database
80+
@user_title = get_media_zusertitle_for_row
81+
if @is_password_protected
82+
add_cryptographic_settings
83+
end
84+
search_and_add_thumbnails
7885
end
7986

8087
##
@@ -631,7 +638,13 @@ def generate_html(individual_files=false)
631638
##
632639
# This method generates the HTML to be embedded into an AppleNote's HTML for objects that use thumbnails.
633640
def generate_html_with_images(individual_files=false)
634-
return @thumbnails.first.generate_html(individual_files) if @thumbnails.length > 0
641+
642+
# If we have thumbnails, return the first one with a reference location
643+
@thumbnails.each do |thumbnail|
644+
return thumbnail.generate_html(individual_files) if thumbnail.reference_location
645+
end
646+
647+
# If we don't have a thumbnail with a reference location, use ours
635648
if @reference_location
636649
root = @note.folder.to_relative_root(individual_files)
637650
builder = Nokogiri::HTML::Builder.new(encoding: "utf-8") do |doc|
@@ -641,6 +654,7 @@ def generate_html_with_images(individual_files=false)
641654
return builder.doc.root
642655
end
643656

657+
# If we get to here, neither our thumbnails, nor we had a reference location
644658
return "{#{type} missing due to not having a file reference location}"
645659
end
646660

lib/AppleNotesEmbeddedThumbnail.rb

Lines changed: 69 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,20 @@ def initialize(primary_key, uuid, uti, note, backup, height, width, parent)
2424
@parent = parent
2525
@filename = get_media_filename
2626
@filepath = get_media_filepath
27-
@backup = backup
27+
@backup = backup
28+
@zgeneration = get_zgeneration_for_thumbnail
2829

2930
# Find where on this computer that file is stored
31+
back_up_file if (@backup and @filepath.length > 0 and @filename.length > 0)
32+
end
33+
34+
##
35+
# This method handles setting the relevant +@backup_location+ variable
36+
# and then backing up the file, if it exists.
37+
def back_up_file
38+
return if (!@backup or !@filepath or @filepath.length == 0)
3039
@backup_location = @backup.get_real_file_path(@filepath)
40+
@logger.debug("Embedded Thumbnail #{@uuid}: \n\tExpected Filepath: '#{@filepath}' (length: #{@filepath.length}), \n\tExpected location: '#{@backup_location}', \n\tOrphaned Parent: #{@parent}")
3141

3242
# Copy the file to our output directory if we can
3343
@reference_location = @backup.back_up_file(@filepath,
@@ -42,6 +52,16 @@ def initialize(primary_key, uuid, uti, note, backup, height, width, parent)
4252
@crypto_tag)
4353
end
4454

55+
##
56+
# This method sets the thumbnail's Note object. It expects an AppleNote +note+
57+
# and immediately calls AppleNotesEmbeddedObjects note= function before firing
58+
# the thumbnail's get_zgeneration_for_thumbnail.
59+
def note=(note)
60+
super(note)
61+
@zgeneration = get_zgeneration_for_thumbnail
62+
back_up_file
63+
end
64+
4565
##
4666
# This method just returns a readable String for the object.
4767
# Adds to the AppleNotesEmbeddedObject.to_s by pointing to where the media is.
@@ -53,37 +73,47 @@ def to_s
5373
# This method returns the +filepath+ of this object.
5474
# This is computed based on the assumed default storage location.
5575
def get_media_filepath
56-
return get_media_filepath_ios16_and_earlier if @note.notestore.version < AppleNoteStore::IOS_VERSION_17
76+
return get_media_filepath_ios16_and_earlier if @version < AppleNoteStore::IOS_VERSION_17
5777
return get_media_filepath_ios17
5878
end
5979

6080
##
6181
# This method returns the +filepath+ of this object.
6282
# This is computed based on the assumed default storage location.
83+
# Examples of valid iOS 16 paths:
84+
# Accounts/{account_uuid}/Previews/{parent_uuid}-1-192x144-0.png
85+
# Accounts/{account_uuid}/Previews/{parent_uuid}-1-768x768-0.png.encrypted
86+
# Accounts/{account_uuid}/Previews/{parent_uuid}-1-216x384-0-oriented.png
87+
# Accounts/{account_uuid}/Previews/{parent_uuid}-1-144x192-0.jpg
88+
# Accounts/{account_uuid}/Previews/{parent_uuid}-1-288x384-0.jpg.encrypted
6389
def get_media_filepath_ios16_and_earlier
64-
return "#{@note.account.account_folder}Previews/#{@uuid}.#{get_thumbnail_extension}.encrypted" if @is_password_protected
65-
return "#{@note.account.account_folder}Previews/#{@uuid}.#{get_thumbnail_extension}"
90+
return "[Unknown Account]/Previews/#{@filename}" if !@note
91+
return "#{@note.account.account_folder}Previews/#{@filename}"
6692
end
6793

6894
##
6995
# This method returns the +filepath+ of this object.
7096
# This is computed based on the assumed default storage location.
97+
# Examples of valid iOS 17 paths:
98+
# Accounts/{account_uuid}/Previews/{parent_uuid}-1-272x384-0.png
99+
# Accounts/{account_uuid}/Previews/{parent_uuid}-1-768x768-0.png.encrypted
100+
# Accounts/{account_uuid}/Previews/{parent_uuid}-1-192x144-0/{zgeneration}/Preview.png
101+
# Accounts/{account_uuid}/Previews/{parent_uuid}-1-2384x3360-0/{zgeneration}/OrientedPreview.jpeg
71102
def get_media_filepath_ios17
72-
zgeneration = get_zgeneration_for_thumbnail
73-
zgeneration = "#{@uuid}/#{zgeneration}/" if (zgeneration and zgeneration.length > 0)
103+
zgeneration_string = ""
104+
zgeneration_string = "#{@uuid}/#{@zgeneration}/" if (@zgeneration and @zgeneration.length > 0)
74105

75-
return "#{@note.account.account_folder}Previews/#{@uuid}.#{get_thumbnail_extension}.encrypted" if @is_password_protected
76-
return "#{@note.account.account_folder}Previews/#{@uuid}.#{get_thumbnail_extension}" if !zgeneration
77-
return "#{@note.account.account_folder}Previews/#{zgeneration}#{@filename}"
106+
return "[Unknown Account]/Previews/#{@filename}" if !@note
107+
return "#{@note.account.account_folder}Previews/#{zgeneration_string}#{@filename}"
78108
end
79109

80110
##
81111
# As these are created by Notes, it is just the UUID. These are either
82-
# .png (apparently created by com.apple.notes.gallery) or .jpg (rest)
112+
# .png (apparently created by com.apple.notes.gallery) or .jpeg/.jpg (rest)
83113
# Encrypted thumbnails just have .encrypted added to the end.
84114
def get_media_filename
85-
return get_media_filename_ios17 if @note.notestore.version >= AppleNoteStore::IOS_VERSION_17
86-
return get_media_filename_ios16_and_earlier
115+
return get_media_filename_ios16_and_earlier if @version < AppleNoteStore::IOS_VERSION_17
116+
return get_media_filename_ios17
87117
end
88118

89119
##
@@ -96,20 +126,26 @@ def get_media_filename_ios16_and_earlier
96126
end
97127

98128
##
99-
# As of iOS 17, these appear to be called Preview.png if there is a zgeneration.
129+
# As of iOS 17, these appear to be called Preview.png if there is a zgeneration.
130+
# Examples of valid paths:
131+
# Accounts/{account_uuid}/Previews/{parent_uuid}-1-768x768-0.png.encrypted
132+
# Accounts/{account_uuid}/Previews/{parent_uuid}-1-2384x3360-0/{zgeneration}/OrientedPreview.jpeg
133+
# Accounts/{account_uuid}/Previews/{parent_uuid}-1-192x144-0/{zgeneration}/Preview.png
134+
# Accounts/{account_uuid}/Previews/{parent_uuid}-1-272x384-0.png
100135
def get_media_filename_ios17
101-
zgeneration = get_zgeneration_for_thumbnail
136+
#zgeneration = get_zgeneration_for_thumbnail
102137

103-
return "#{@uuid}.png.encrypted" if @is_password_protected
104-
return "Preview.#{get_thumbnail_extension_ios17}" if zgeneration
138+
#return "#{@uuid}.png.encrypted" if @is_password_protected
139+
return "#{@uuid}.#{get_thumbnail_extension_ios17}.encrypted" if @is_password_protected
140+
return "Preview.#{get_thumbnail_extension_ios17}" if @zgeneration
105141
return "#{@uuid}.#{get_thumbnail_extension_ios17}"
106142
end
107143

108144
##
109145
# This method fetches the appropriate ZFALLBACKGENERATION string to compute
110146
# media location for iOS 17 and later.
111147
def get_zgeneration_for_thumbnail
112-
return nil if @note.notestore.version < AppleNoteStore::IOS_VERSION_17
148+
return nil if @version < AppleNoteStore::IOS_VERSION_17 or !@database
113149
@database.execute("SELECT ZICCLOUDSYNCINGOBJECT.ZGENERATION " +
114150
"FROM ZICCLOUDSYNCINGOBJECT " +
115151
"WHERE ZICCLOUDSYNCINGOBJECT.ZIDENTIFIER=?",
@@ -122,42 +158,48 @@ def get_zgeneration_for_thumbnail
122158
# This method returns the thumbnail's extension. These are either
123159
# .jpg (apparently created by com.apple.notes.gallery) or .png (rest).
124160
def get_thumbnail_extension
125-
return get_thumbnail_extension_ios17 if @note.notestore.version >= AppleNoteStore::IOS_VERSION_17
126-
return get_thumbnail_extension_ios16_and_earlier
161+
return get_thumbnail_extension_ios16_and_earlier if @version < AppleNoteStore::IOS_VERSION_17
162+
return get_thumbnail_extension_ios17
127163
end
128164

129165
##
130166
# This method returns the thumbnail's extension. This is apparently png for iOS
131-
# 17 and later.
167+
# 17 and later and jpeg for Galleries.
132168
def get_thumbnail_extension_ios17
133-
return "jpeg" if (@parent.type == "com.apple.notes.gallery")
134-
return "jpeg" if (@parent.parent and @parent.parent.type == "com.apple.notes.gallery")
169+
return "jpeg" if (@parent and @parent.type == "com.apple.notes.gallery")
170+
return "jpeg" if (@parent and @parent.parent and @parent.parent.type == "com.apple.notes.gallery")
135171
return "png"
136172
end
137173

138174
##
139175
# This method returns the thumbnail's extension. These are either
140176
# .jpg (apparently created by com.apple.notes.gallery) or .png (rest) for iOS 16 and earlier.
141177
def get_thumbnail_extension_ios16_and_earlier
142-
return "jpg" if (@parent.type == "com.apple.notes.gallery")
143-
return "jpg" if (@parent.parent and @parent.parent.type == "com.apple.notes.gallery")
178+
return "jpg" if (@parent and @parent.type == "com.apple.notes.gallery")
179+
return "jpg" if (@parent and @parent.parent and @parent.parent.type == "com.apple.notes.gallery")
144180
return "png"
145181
end
146182

147183
##
148184
# This method generates the HTML necessary to display the image inline.
149185
def generate_html(individual_files)
150-
if (@parent.reference_location and @reference_location)
186+
if (@parent and @reference_location)
187+
188+
# We default to the thumbnail's location to link to...
189+
href_target = @reference_location
190+
# ...but if possible, we use the parent's location to get the real file
191+
href_target = @parent.reference_location if @parent.reference_location
192+
151193
root = @note.folder.to_relative_root(individual_files)
152194
builder = Nokogiri::HTML::Builder.new(encoding: "utf-8") do |doc|
153-
doc.a(href: "#{root}#{@parent.reference_location}") {
195+
doc.a(href: "#{root}#{href_target}") {
154196
doc.img(src: "#{root}#{@reference_location}")
155197
}.attr("data-apple-notes-zidentifier" => "#{@parent.uuid}")
156198
end
157199
return builder.doc.root
158200
end
159201

160-
return "{Image missing due to not having file reference point}"
202+
return "{Thumbnail missing due to not having file reference point}"
161203
end
162204

163205
##

0 commit comments

Comments
 (0)