Show crawl replay for running crawls (#235)
* show replay and watch at same time * add separate section for watch * only show replay if crawl has files, otherwise show 'no files' message
This commit is contained in:
		
							parent
							
								
									6a78bcd4aa
								
							
						
					
					
						commit
						0c1dc2a1d1
					
				| @ -9,7 +9,7 @@ import LiteElement, { html } from "../../utils/LiteElement"; | |||||||
| import { CopyButton } from "../../components/copy-button"; | import { CopyButton } from "../../components/copy-button"; | ||||||
| import type { Crawl } from "./types"; | import type { Crawl } from "./types"; | ||||||
| 
 | 
 | ||||||
| type SectionName = "overview" | "watch" | "download" | "logs"; | type SectionName = "overview" | "watch" | "replay" | "files" | "logs"; | ||||||
| 
 | 
 | ||||||
| const POLL_INTERVAL_SECONDS = 10; | const POLL_INTERVAL_SECONDS = 10; | ||||||
| 
 | 
 | ||||||
| @ -65,6 +65,13 @@ export class CrawlDetail extends LiteElement { | |||||||
|     return this.crawl.state === "running" || this.crawl.state === "starting"; |     return this.crawl.state === "running" || this.crawl.state === "starting"; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   private get hasFiles(): boolean | null { | ||||||
|  |     if (!this.crawl) return null; | ||||||
|  |     if (!this.crawl.resources) return false; | ||||||
|  | 
 | ||||||
|  |     return this.crawl.resources.length > 0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   async firstUpdated() { |   async firstUpdated() { | ||||||
|     if (!this.crawlsBaseUrl) { |     if (!this.crawlsBaseUrl) { | ||||||
|       throw new Error("Crawls base URL not defined"); |       throw new Error("Crawls base URL not defined"); | ||||||
| @ -76,7 +83,7 @@ export class CrawlDetail extends LiteElement { | |||||||
|   connectedCallback(): void { |   connectedCallback(): void { | ||||||
|     // Set initial active section based on URL #hash value
 |     // Set initial active section based on URL #hash value
 | ||||||
|     const hash = window.location.hash.slice(1); |     const hash = window.location.hash.slice(1); | ||||||
|     if (["overview", "watch", "download", "logs"].includes(hash)) { |     if (["overview", "watch", "replay", "files", "logs"].includes(hash)) { | ||||||
|       this.sectionName = hash as SectionName; |       this.sectionName = hash as SectionName; | ||||||
|     } |     } | ||||||
|     super.connectedCallback(); |     super.connectedCallback(); | ||||||
| @ -93,11 +100,7 @@ export class CrawlDetail extends LiteElement { | |||||||
|     switch (this.sectionName) { |     switch (this.sectionName) { | ||||||
|       case "watch": { |       case "watch": { | ||||||
|         if (this.crawl) { |         if (this.crawl) { | ||||||
|           if (this.isRunning) { |           sectionContent = this.renderWatch(); | ||||||
|             sectionContent = this.renderWatch(); |  | ||||||
|           } else { |  | ||||||
|             sectionContent = this.renderReplay(); |  | ||||||
|           } |  | ||||||
|         } else { |         } else { | ||||||
|           // TODO loading indicator?
 |           // TODO loading indicator?
 | ||||||
|           return ""; |           return ""; | ||||||
| @ -105,8 +108,10 @@ export class CrawlDetail extends LiteElement { | |||||||
| 
 | 
 | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
| 
 |       case "replay": | ||||||
|       case "download": |         sectionContent = this.renderReplay(); | ||||||
|  |         break; | ||||||
|  |       case "files": | ||||||
|         sectionContent = this.renderFiles(); |         sectionContent = this.renderFiles(); | ||||||
|         break; |         break; | ||||||
|       case "logs": |       case "logs": | ||||||
| @ -193,8 +198,14 @@ export class CrawlDetail extends LiteElement { | |||||||
|       <nav class="border-b md:border-b-0"> |       <nav class="border-b md:border-b-0"> | ||||||
|         <ul class="flex flex-row md:flex-col" role="menu"> |         <ul class="flex flex-row md:flex-col" role="menu"> | ||||||
|           ${renderNavItem({ section: "overview", label: msg("Overview") })} |           ${renderNavItem({ section: "overview", label: msg("Overview") })} | ||||||
|           ${renderNavItem({ section: "watch", label: msg("View Crawl") })} |           ${this.isRunning | ||||||
|           ${renderNavItem({ section: "download", label: msg("Download") })} |             ? renderNavItem({ | ||||||
|  |                 section: "watch", | ||||||
|  |                 label: msg("Watch Crawl"), | ||||||
|  |               }) | ||||||
|  |             : ""} | ||||||
|  |           ${renderNavItem({ section: "replay", label: msg("Replay") })} | ||||||
|  |           ${renderNavItem({ section: "files", label: msg("Files") })} | ||||||
|           ${renderNavItem({ section: "logs", label: msg("Logs") })} |           ${renderNavItem({ section: "logs", label: msg("Logs") })} | ||||||
|         </ul> |         </ul> | ||||||
|       </nav> |       </nav> | ||||||
| @ -400,14 +411,14 @@ export class CrawlDetail extends LiteElement { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private renderWatch() { |   private renderWatch() { | ||||||
|     if (!this.authState) return ""; |     if (!this.authState || !this.crawl) return ""; | ||||||
| 
 | 
 | ||||||
|     const authToken = this.authState.headers.Authorization.split(" ")[1]; |     const authToken = this.authState.headers.Authorization.split(" ")[1]; | ||||||
| 
 | 
 | ||||||
|     return html` |     return html` | ||||||
|       <header class="flex justify-between"> |       <header class="flex justify-between"> | ||||||
|         <h3 class="text-lg font-medium mb-2">${msg("Watch Crawl")}</h3> |         <h3 class="text-lg font-medium mb-2">${msg("Watch Crawl")}</h3> | ||||||
|         ${document.fullscreenEnabled |         ${this.isRunning && document.fullscreenEnabled | ||||||
|           ? html` |           ? html` | ||||||
|               <sl-icon-button |               <sl-icon-button | ||||||
|                 name="arrows-fullscreen" |                 name="arrows-fullscreen" | ||||||
| @ -418,22 +429,36 @@ export class CrawlDetail extends LiteElement { | |||||||
|           : ""} |           : ""} | ||||||
|       </header> |       </header> | ||||||
| 
 | 
 | ||||||
|       ${this.crawl |       ${this.isRunning | ||||||
|         ? html` <div id="screencast-crawl">
 |         ? html` | ||||||
|             <btrix-screencast |             <div id="screencast-crawl"> | ||||||
|               authToken=${authToken} |               <btrix-screencast | ||||||
|               archiveId=${this.crawl.aid} |                 authToken=${authToken} | ||||||
|               crawlId=${this.crawlId!} |                 archiveId=${this.crawl.aid} | ||||||
|               .watchIPs=${this.crawl.watchIPs || []} |                 crawlId=${this.crawlId!} | ||||||
|             ></btrix-screencast> |                 .watchIPs=${this.crawl.watchIPs || []} | ||||||
|           </div>` |               ></btrix-screencast> | ||||||
|         : ""} |             </div> | ||||||
|  |           ` | ||||||
|  |         : html` | ||||||
|  |             <div class="rounded border bg-neutral-50 p-3"> | ||||||
|  |               <p class="text-sm text-neutral-600"> | ||||||
|  |                 ${msg( | ||||||
|  |                   html`Crawl is not running.
 | ||||||
|  |                     <a | ||||||
|  |                       href=${`${this.crawlsBaseUrl}/crawl/${this.crawlId}#replay`} | ||||||
|  |                       class="text-primary hover:underline" | ||||||
|  |                       @click=${() => (this.sectionName = "replay")} | ||||||
|  |                       >View replay</a | ||||||
|  |                     >` | ||||||
|  |                 )} | ||||||
|  |               </p> | ||||||
|  |             </div> | ||||||
|  |           `}
 | ||||||
|     `;
 |     `;
 | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private renderReplay() { |   private renderReplay() { | ||||||
|     const isRunning = this.isRunning; |  | ||||||
| 
 |  | ||||||
|     const bearer = this.authState?.headers?.Authorization?.split(" ", 2)[1]; |     const bearer = this.authState?.headers?.Authorization?.split(" ", 2)[1]; | ||||||
| 
 | 
 | ||||||
|     // for now, just use the first file until multi-wacz support is fully implemented
 |     // for now, just use the first file until multi-wacz support is fully implemented
 | ||||||
| @ -454,21 +479,16 @@ export class CrawlDetail extends LiteElement { | |||||||
|           : ""} |           : ""} | ||||||
|       </header> |       </header> | ||||||
| 
 | 
 | ||||||
|       <div |       <div id="replay-crawl" class="aspect-4/3 rounded border overflow-hidden"> | ||||||
|         id="replay-crawl" |  | ||||||
|         class="aspect-4/3 rounded border ${isRunning |  | ||||||
|           ? "border-purple-200" |  | ||||||
|           : "border-slate-100"}" |  | ||||||
|       > |  | ||||||
|         <!-- https://github.com/webrecorder/browsertrix-crawler/blob/9f541ab011e8e4bccf8de5bd7dc59b632c694bab/screencast/index.html -->
 |         <!-- https://github.com/webrecorder/browsertrix-crawler/blob/9f541ab011e8e4bccf8de5bd7dc59b632c694bab/screencast/index.html -->
 | ||||||
|         ${replaySource |         ${replaySource && this.hasFiles | ||||||
|           ? html`<replay-web-page
 |           ? html`<replay-web-page
 | ||||||
|               source="${replaySource}" |               source="${replaySource}" | ||||||
|               coll="${ifDefined(this.crawl?.id)}" |               coll="${ifDefined(this.crawl?.id)}" | ||||||
|               replayBase="/replay/" |               replayBase="/replay/" | ||||||
|               noSandbox="true" |               noSandbox="true" | ||||||
|             ></replay-web-page>` |             ></replay-web-page>` | ||||||
|           : ``} |           : this.renderNoFilesMessage()} | ||||||
|       </div> |       </div> | ||||||
|     `;
 |     `;
 | ||||||
|   } |   } | ||||||
| @ -600,36 +620,38 @@ export class CrawlDetail extends LiteElement { | |||||||
|     return html` |     return html` | ||||||
|       <h3 class="text-lg font-medium my-2">${msg("Download Files")}</h3> |       <h3 class="text-lg font-medium my-2">${msg("Download Files")}</h3> | ||||||
| 
 | 
 | ||||||
|       ${this.crawl |       ${this.hasFiles | ||||||
|         ? this.crawl.resources && this.crawl.resources.length |         ? html` | ||||||
|           ? html` |             <ul class="border rounded text-sm"> | ||||||
|               <ul class="border rounded text-sm"> |               ${this.crawl!.resources!.map( | ||||||
|                 ${this.crawl.resources.map( |                 (file) => html` | ||||||
|                   (file) => html` |                   <li | ||||||
|                     <li |                     class="flex justify-between p-3 border-t first:border-t-0" | ||||||
|                       class="flex justify-between p-3 border-t first:border-t-0" |                   > | ||||||
|                     > |                     <div class="whitespace-nowrap truncate"> | ||||||
|                       <div class="whitespace-nowrap truncate"> |                       <a | ||||||
|                         <a |                         class="text-primary hover:underline" | ||||||
|                           class="text-primary hover:underline" |                         href=${file.path} | ||||||
|                           href=${file.path} |                         download | ||||||
|                           download |                         title=${file.name} | ||||||
|                           title=${file.name} |                         >${file.name.slice(file.name.lastIndexOf("/") + 1)} | ||||||
|                           >${file.name.slice(file.name.lastIndexOf("/") + 1)} |                       </a> | ||||||
|                         </a> |                     </div> | ||||||
|                       </div> |                     <div class="whitespace-nowrap"> | ||||||
|                       <div class="whitespace-nowrap"> |                       <sl-format-bytes value=${file.size}></sl-format-bytes> | ||||||
|                         <sl-format-bytes value=${file.size}></sl-format-bytes> |                     </div> | ||||||
|                       </div> |                   </li> | ||||||
|                     </li> |                 ` | ||||||
|                   ` |               )} | ||||||
|                 )} |             </ul> | ||||||
|               </ul> |           ` | ||||||
|             ` |         : this.renderNoFilesMessage()} | ||||||
|           : html` |     `;
 | ||||||
|               <p class="text-neutral-400">${msg("No files to download.")}</p> |   } | ||||||
|             ` | 
 | ||||||
|         : ""} |   private renderNoFilesMessage() { | ||||||
|  |     return html` | ||||||
|  |       <p class="text-sm text-neutral-400">${msg("No files yet.")}</p> | ||||||
|     `;
 |     `;
 | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -783,7 +783,7 @@ export class CrawlTemplatesDetail extends LiteElement { | |||||||
|                         class="text-primary font-medium hover:underline text-sm p-1" |                         class="text-primary font-medium hover:underline text-sm p-1" | ||||||
|                         href=${`/archives/${this.archiveId}/crawls/crawl/${this.crawlTemplate.currCrawlId}#watch`} |                         href=${`/archives/${this.archiveId}/crawls/crawl/${this.crawlTemplate.currCrawlId}#watch`} | ||||||
|                         @click=${this.navLink} |                         @click=${this.navLink} | ||||||
|                         >${msg("View crawl")}</a |                         >${msg("Watch crawl")}</a | ||||||
|                       >` |                       >` | ||||||
|                     : this.crawlTemplate.inactive |                     : this.crawlTemplate.inactive | ||||||
|                     ? "" |                     ? "" | ||||||
| @ -837,7 +837,7 @@ export class CrawlTemplatesDetail extends LiteElement { | |||||||
|                     class="text-primary font-medium hover:underline text-sm p-1" |                     class="text-primary font-medium hover:underline text-sm p-1" | ||||||
|                     href=${`/archives/${this.archiveId}/crawls/crawl/${this.crawlTemplate.lastCrawlId}#watch`} |                     href=${`/archives/${this.archiveId}/crawls/crawl/${this.crawlTemplate.lastCrawlId}#watch`} | ||||||
|                     @click=${this.navLink} |                     @click=${this.navLink} | ||||||
|                     >${msg("View crawl")}</a |                     >${msg("Watch crawl")}</a | ||||||
|                   > |                   > | ||||||
|                 ` |                 ` | ||||||
|               : html`<span class="text-0-400 text-sm p-1"
 |               : html`<span class="text-0-400 text-sm p-1"
 | ||||||
| @ -1201,7 +1201,7 @@ export class CrawlTemplatesDetail extends LiteElement { | |||||||
|               href="/archives/${this |               href="/archives/${this | ||||||
|                 .archiveId}/crawls/crawl/${data.started}#watch" |                 .archiveId}/crawls/crawl/${data.started}#watch" | ||||||
|               @click=${this.navLink.bind(this)} |               @click=${this.navLink.bind(this)} | ||||||
|               >View crawl</a |               >Watch crawl</a | ||||||
|             >` |             >` | ||||||
|         ), |         ), | ||||||
|         type: "success", |         type: "success", | ||||||
|  | |||||||
| @ -537,7 +537,7 @@ export class CrawlTemplatesList extends LiteElement { | |||||||
|           }} |           }} | ||||||
|         > |         > | ||||||
|           <span class="whitespace-nowrap"> |           <span class="whitespace-nowrap"> | ||||||
|             ${this.runningCrawlsMap[t.id] ? msg("View crawl") : msg("Run now")} |             ${this.runningCrawlsMap[t.id] ? msg("Watch crawl") : msg("Run now")} | ||||||
|           </span> |           </span> | ||||||
|         </button> |         </button> | ||||||
|       </div> |       </div> | ||||||
| @ -680,7 +680,7 @@ export class CrawlTemplatesList extends LiteElement { | |||||||
|               href="/archives/${this |               href="/archives/${this | ||||||
|                 .archiveId}/crawls/crawl/${data.started}#watch" |                 .archiveId}/crawls/crawl/${data.started}#watch" | ||||||
|               @click=${this.navLink.bind(this)} |               @click=${this.navLink.bind(this)} | ||||||
|               >View crawl</a |               >Watch crawl</a | ||||||
|             >` |             >` | ||||||
|         ), |         ), | ||||||
|         type: "success", |         type: "success", | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user