@@ -7,7 +7,28 @@ import YAML from 'yaml'
7
7
import { Readable } from 'stream'
8
8
import { ActorProfileOptions } from './index.d'
9
9
10
- export function exportActorProfile ( {
10
+ const downloadMedia = async ( mediaUrl : string ) => {
11
+ if ( ! mediaUrl ) {
12
+ return null
13
+ }
14
+
15
+ try {
16
+ const response = await fetch ( mediaUrl )
17
+ if ( ! response . ok ) {
18
+ throw new Error ( `Failed to fetch media: ${ response . statusText } ` )
19
+ }
20
+
21
+ return {
22
+ buffer : await response . arrayBuffer ( ) ,
23
+ contentType : response . headers . get ( 'Content-Type' )
24
+ } // Binary data
25
+ } catch ( error ) {
26
+ console . error ( `Error downloading media from ${ mediaUrl } :` , error )
27
+ return null
28
+ }
29
+ }
30
+
31
+ export async function exportActorProfile ( {
11
32
actorProfile,
12
33
outbox,
13
34
followers,
@@ -18,16 +39,14 @@ export function exportActorProfile({
18
39
blockedAccounts,
19
40
blockedDomains,
20
41
mutedAccounts
21
- } : ActorProfileOptions ) : tar . Pack {
22
- const pack : Pack = tar . pack ( ) // pack is a stream
42
+ } : ActorProfileOptions & { media ?: File [ ] } ) : Promise < tar . Pack > {
43
+ const pack : Pack = tar . pack ( )
23
44
24
45
const manifest : any = {
25
- // Universal Backup Container spec version
26
46
'ubc-version' : '0.1' ,
27
47
meta : {
28
48
createdBy : {
29
49
client : {
30
- // URL to the client that generated this export file
31
50
url : 'https://github.com/interop-alliance/wallet-export-ts'
32
51
}
33
52
}
@@ -36,17 +55,19 @@ export function exportActorProfile({
36
55
'manifest.yml' : {
37
56
url : 'https://w3id.org/fep/6fcd#manifest-file'
38
57
} ,
39
- // Directory with ActivityPub-relevant exports
40
58
activitypub : {
41
59
contents : { }
60
+ } ,
61
+ media : {
62
+ contents : { }
42
63
}
43
64
}
44
65
}
45
66
46
67
pack . entry ( { name : 'activitypub' , type : 'directory' } )
68
+ pack . entry ( { name : 'media' , type : 'directory' } )
47
69
48
70
if ( actorProfile ) {
49
- // Serialized ActivityPub Actor profile
50
71
manifest . contents . activitypub . contents [ 'actor.json' ] = {
51
72
url : 'https://www.w3.org/TR/activitypub/#actor-objects'
52
73
}
@@ -57,7 +78,6 @@ export function exportActorProfile({
57
78
}
58
79
59
80
if ( outbox ) {
60
- // ActivityStreams OrderedCollection representing the contents of the actor's Outbox
61
81
manifest . contents . activitypub . contents [ 'outbox.json' ] = {
62
82
url : 'https://www.w3.org/TR/activitystreams-core/#collections'
63
83
}
@@ -68,7 +88,6 @@ export function exportActorProfile({
68
88
}
69
89
70
90
if ( followers ) {
71
- // ActivityStreams OrderedCollection representing the actor's Followers
72
91
manifest . contents . activitypub . contents [ 'followers.json' ] = {
73
92
url : 'https://www.w3.org/TR/activitystreams-core/#collections'
74
93
}
@@ -79,7 +98,6 @@ export function exportActorProfile({
79
98
}
80
99
81
100
if ( likes ) {
82
- // ActivityStreams OrderedCollection representing Activities and Objects the actor liked
83
101
manifest . contents . activitypub . contents [ 'likes.json' ] = {
84
102
url : 'https://www.w3.org/TR/activitystreams-core/#collections'
85
103
}
@@ -90,7 +108,6 @@ export function exportActorProfile({
90
108
}
91
109
92
110
if ( bookmarks ) {
93
- // ActivityStreams OrderedCollection representing the actor's Bookmarks
94
111
manifest . contents . activitypub . contents [ 'bookmarks.json' ] = {
95
112
url : 'https://www.w3.org/TR/activitystreams-core/#collections'
96
113
}
@@ -150,6 +167,46 @@ export function exportActorProfile({
150
167
)
151
168
}
152
169
170
+ if ( outbox ) {
171
+ for ( const post of outbox ) {
172
+ if ( ! post . media_attachments ) continue
173
+
174
+ for ( const media of post . media_attachments ) {
175
+ try {
176
+ const mediaData = await downloadMedia ( media . url )
177
+ if ( ! mediaData ) {
178
+ console . warn ( `Skipping media due to download failure: ${ media . url } ` )
179
+ continue
180
+ }
181
+
182
+ const extension = mediaData . contentType ?. split ( '/' ) [ 1 ]
183
+
184
+ const mediaName = `${ media . id } .${ extension } `
185
+ const mediaType = media . type
186
+
187
+ pack . entry (
188
+ {
189
+ name : `media/${ mediaName } ` ,
190
+ size : mediaData . buffer . byteLength
191
+ } ,
192
+ Buffer . from ( mediaData . buffer )
193
+ )
194
+
195
+ // Step 6: Add metadata to manifest
196
+ manifest . contents . media . contents [ mediaName ] = {
197
+ type : mediaType ,
198
+ size : mediaData . buffer . byteLength ,
199
+ lastModified : new Date ( ) . toISOString ( ) ,
200
+ meta : media . meta || { } ,
201
+ description : media . description || null
202
+ }
203
+ } catch ( error ) {
204
+ console . error ( `Failed to process media: ${ media . url } ` , error )
205
+ }
206
+ }
207
+ }
208
+ }
209
+
153
210
pack . entry ( { name : 'manifest.yaml' } , YAML . stringify ( manifest ) )
154
211
pack . finalize ( )
155
212
0 commit comments