@@ -5,6 +5,8 @@ import { unsafeSVG } from "lit/directives/unsafe-svg.js";
55import "./argo-archive-list" ;
66import "@material/web/textfield/outlined-text-field.js" ;
77import "@material/web/icon/icon.js" ;
8+ import { ArgoArchiveList } from "./argo-archive-list" ;
9+ import { Downloader } from "./sw/downloader" ;
810
911import {
1012 getLocalOption ,
@@ -22,10 +24,14 @@ import {
2224import "@material/web/button/filled-button.js" ;
2325import "@material/web/button/outlined-button.js" ;
2426import "@material/web/divider/divider.js" ;
27+ import { CollectionLoader } from "@webrecorder/wabac/swlib" ;
28+ import WebTorrent from "webtorrent" ;
2529
2630document . adoptedStyleSheets . push ( typescaleStyles . styleSheet ! ) ;
2731
32+ const collLoader = new CollectionLoader ( ) ;
2833class ArgoViewer extends LitElement {
34+ private archiveList ! : ArgoArchiveList ;
2935 constructor ( ) {
3036 super ( ) ;
3137
@@ -101,7 +107,136 @@ class ArgoViewer extends LitElement {
101107 } ;
102108 }
103109
110+ private async onDownload ( ) {
111+ const selectedPages = this . archiveList ?. getSelectedPages ?.( ) || [ ] ;
112+ if ( ! selectedPages . length ) {
113+ alert ( "Please select some pages to share." ) ;
114+ return ;
115+ }
116+
117+ console . log ( "Selected pages to share:" , selectedPages ) ;
118+
119+ const defaultCollId = ( await getLocalOption ( "defaultCollId" ) ) || "" ;
120+ const coll = await collLoader . loadColl ( defaultCollId ) ;
121+
122+ const pageTsList = selectedPages . map ( ( p ) => p . id ) ;
123+ const format = "wacz" ;
124+ const filename = `archive-${ Date . now ( ) } .wacz` ;
125+
126+ // Webrecorder swlib API format for download:
127+ const downloader = new Downloader ( {
128+ coll,
129+ format,
130+ filename,
131+ pageList : pageTsList ,
132+ } ) ;
133+
134+ const response = await downloader . download ( ) ;
135+ if ( ! ( response instanceof Response ) ) {
136+ console . error ( "Download failed:" , response ) ;
137+ alert ( "Failed to download archive." ) ;
138+ return ;
139+ }
140+
141+ console . log ( "Download response:" , response ) ;
142+
143+ const blob = await response . blob ( ) ;
144+ const url = URL . createObjectURL ( blob ) ;
145+
146+ // Create temporary <a> to trigger download
147+ const a = document . createElement ( "a" ) ;
148+ a . href = url ;
149+ a . download = filename ;
150+ document . body . appendChild ( a ) ;
151+ a . click ( ) ;
152+
153+ // Cleanup
154+ URL . revokeObjectURL ( url ) ;
155+ document . body . removeChild ( a ) ;
156+
157+ console . log ( "WACZ file downloaded:" , filename ) ;
158+ }
159+
160+ private async onShare ( ) {
161+ const selectedPages = this . archiveList ?. getSelectedPages ?.( ) || [ ] ;
162+ if ( ! selectedPages . length ) {
163+ alert ( "Please select some pages to share." ) ;
164+ return ;
165+ }
166+
167+ console . log ( "Selected pages to share:" , selectedPages ) ;
168+
169+ const defaultCollId = ( await getLocalOption ( "defaultCollId" ) ) || "" ;
170+ const coll = await collLoader . loadColl ( defaultCollId ) ;
171+
172+ const pageTsList = selectedPages . map ( ( p ) => p . id ) ;
173+ const format = "wacz" ;
174+ const filename = `archive-${ Date . now ( ) } .wacz` ;
175+
176+ // Webrecorder swlib API format for download:
177+ const downloader = new Downloader ( {
178+ coll,
179+ format,
180+ filename,
181+ pageList : pageTsList ,
182+ } ) ;
183+
184+ const response = await downloader . download ( ) ;
185+ if ( ! ( response instanceof Response ) ) {
186+ console . error ( "Download failed:" , response ) ;
187+ alert ( "Failed to download archive." ) ;
188+ return ;
189+ }
190+
191+ const opfsRoot = await navigator . storage . getDirectory ( ) ;
192+ const waczFileHandle = await opfsRoot . getFileHandle ( filename , {
193+ create : true ,
194+ } ) ;
195+ const writable = await waczFileHandle . createWritable ( ) ;
196+
197+ const reader = response . body ! . getReader ( ) ;
198+ while ( true ) {
199+ const { done, value } = await reader . read ( ) ;
200+ if ( done ) break ;
201+ await writable . write ( value ) ;
202+ }
203+
204+ await writable . close ( ) ;
205+
206+ console . log ( "WACZ saved to OPFS as:" , filename ) ;
207+
208+ // Get a File object from OPFS
209+ const fileHandle = await opfsRoot . getFileHandle ( filename ) ;
210+ const file = await fileHandle . getFile ( ) ;
211+
212+ // Create a WebTorrent client if not already available
213+ const client = new ( window as any ) . WebTorrent ( ) ;
214+
215+ // Seed the file
216+ // @ts -expect-error
217+ client . seed ( file , ( torrent ) => {
218+ const magnetURI = torrent . magnetURI ;
219+ console . log ( "Seeding WACZ file via WebTorrent:" , magnetURI ) ;
220+
221+ // Copy to clipboard
222+ navigator . clipboard
223+ . writeText ( magnetURI )
224+ . then ( ( ) => {
225+ alert ( `Magnet link copied to clipboard:\n${ magnetURI } ` ) ;
226+ } )
227+ . catch ( ( err ) => {
228+ console . error ( "Failed to copy magnet link:" , err ) ;
229+ alert ( `Magnet Link Ready:\n${ magnetURI } ` ) ;
230+ } ) ;
231+ } ) ;
232+ }
233+
104234 firstUpdated ( ) {
235+ this . archiveList = document . getElementById (
236+ "archive-list" ,
237+ ) as ArgoArchiveList ;
238+
239+ console . log ( "Archive list:" , this . archiveList ) ;
105240 this . registerMessages ( ) ;
106241 }
107242
@@ -245,7 +380,10 @@ class ArgoViewer extends LitElement {
245380 this . replayUrl = this . getCollPage ( ) + "#" + params . toString ( ) ;
246381 }
247382
248- if ( changedProperties . has ( "pageUrl" ) || changedProperties . has ( "failureMsg" ) ) {
383+ if (
384+ changedProperties . has ( "pageUrl" ) ||
385+ changedProperties . has ( "failureMsg" )
386+ ) {
249387 // @ts -expect-error - TS2339 - Property 'canRecord' does not exist on type 'RecPopup'.
250388 this . canRecord =
251389 // @ts -expect-error - TS2339 - Property 'pageUrl' does not exist on type 'RecPopup'.
@@ -299,7 +437,9 @@ class ArgoViewer extends LitElement {
299437 render ( ) {
300438 return html `
301439 < md-divider > </ md-divider >
302- < div style ="padding:1rem; display:flex; align-items:center; justify-content:space-between; ">
440+ < div
441+ style ="padding:1rem; display:flex; align-items:center; justify-content:space-between; "
442+ >
303443 ${
304444 // @ts -expect-error - TS2339 - Property 'recording' does not exist on type 'RecPopup'.
305445 ! this . recording
@@ -316,6 +456,14 @@ class ArgoViewer extends LitElement {
316456 < md-icon slot ="icon " style ="color:white "> public</ md-icon >
317457 Resume Archiving
318458 </ md-filled-button >
459+
460+ < md-icon-button aria-label ="Download " @click =${ this . onDownload } >
461+ < md-icon style ="color: gray; "> download</ md-icon >
462+ </ md-icon-button >
463+
464+ < md-icon-button aria-label ="Share " @click =${ this . onShare } >
465+ < md-icon style ="color: gray; "> share</ md-icon >
466+ </ md-icon-button >
319467 `
320468 : html `
321469 < md-outlined-button
0 commit comments