181 lines
4.8 KiB
Go
181 lines
4.8 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/confusedpolarbear/intro_skipper_verifier/structs"
|
|
)
|
|
|
|
var spinners []string
|
|
var spinnerIndex int
|
|
|
|
func generateReport(hostAddress, apiKey, reportDestination string, keepTimestamps bool, pollInterval time.Duration) {
|
|
start := time.Now()
|
|
|
|
// Setup the spinner
|
|
spinners = strings.Split("⣷⣯⣟⡿⢿⣻⣽⣾", "")
|
|
spinnerIndex = -1 // start the spinner on the first graphic
|
|
|
|
// Setup the filename to save intros to
|
|
if reportDestination == "" {
|
|
reportDestination = fmt.Sprintf("intros-%s-%d.json", hostAddress, time.Now().Unix())
|
|
reportDestination = strings.ReplaceAll(reportDestination, "http://", "")
|
|
reportDestination = strings.ReplaceAll(reportDestination, "https://", "")
|
|
}
|
|
|
|
// Ensure the file is writable
|
|
if err := os.WriteFile(reportDestination, nil, 0600); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
fmt.Printf("Started at: %s\n", start.Format(time.RFC1123))
|
|
fmt.Printf("Address: %s\n", hostAddress)
|
|
fmt.Printf("Destination: %s\n", reportDestination)
|
|
fmt.Println()
|
|
|
|
// Get Jellyfin server information and plugin configuration
|
|
info := GetServerInfo(hostAddress, apiKey)
|
|
config := GetPluginConfiguration(hostAddress, apiKey)
|
|
fmt.Println()
|
|
|
|
fmt.Printf("Jellyfin OS: %s\n", info.OperatingSystem)
|
|
fmt.Printf("Jellyfin version: %s\n", info.Version)
|
|
fmt.Printf("Analysis settings: %s\n", config.AnalysisSettings())
|
|
fmt.Printf("Introduction reqs: %s\n", config.IntroductionRequirements())
|
|
fmt.Printf("Erase timestamps: %t\n", !keepTimestamps)
|
|
fmt.Println()
|
|
|
|
// If not keeping timestamps, run the fingerprint task.
|
|
// Otherwise, log that the task isn't being run
|
|
if !keepTimestamps {
|
|
runAnalysisAndWait(hostAddress, apiKey, pollInterval)
|
|
} else {
|
|
fmt.Println("[+] Using previously discovered intros")
|
|
}
|
|
fmt.Println()
|
|
|
|
// Save all intros from the server
|
|
fmt.Println("[+] Saving intros")
|
|
|
|
var report structs.Report
|
|
rawIntros := SendRequest("GET", hostAddress+"/Intros/All", apiKey)
|
|
if err := json.Unmarshal(rawIntros, &report.Intros); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Calculate the durations of all intros
|
|
for i := range report.Intros {
|
|
intro := report.Intros[i]
|
|
intro.Duration = intro.IntroEnd - intro.IntroStart
|
|
report.Intros[i] = intro
|
|
}
|
|
|
|
fmt.Println()
|
|
fmt.Println("[+] Saving report")
|
|
|
|
// Store timing data, server information, and plugin configuration
|
|
report.StartedAt = start
|
|
report.FinishedAt = time.Now()
|
|
report.Runtime = report.FinishedAt.Sub(report.StartedAt)
|
|
report.ServerInfo = info
|
|
report.PluginConfig = config
|
|
|
|
// Marshal the report
|
|
marshalled, err := json.Marshal(report)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err := os.WriteFile(reportDestination, marshalled, 0600); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Change report permissions
|
|
exec.Command("chown", "1000:1000", reportDestination).Run()
|
|
|
|
fmt.Println("[+] Done")
|
|
}
|
|
|
|
func runAnalysisAndWait(hostAddress, apiKey string, pollInterval time.Duration) {
|
|
var taskId string = ""
|
|
|
|
type taskInfo struct {
|
|
State string
|
|
CurrentProgressPercentage int
|
|
}
|
|
|
|
fmt.Println("[+] Erasing previously discovered intros")
|
|
SendRequest("POST", hostAddress+"/Intros/EraseTimestamps", apiKey)
|
|
fmt.Println()
|
|
|
|
var taskIds = []string{
|
|
"f64d8ad58e3d7b98548e1a07697eb100", // v0.1.8
|
|
"8863329048cc357f7dfebf080f2fe204",
|
|
"6adda26c5261c40e8fa4a7e7df568be2"}
|
|
|
|
fmt.Println("[+] Starting analysis task")
|
|
for _, id := range taskIds {
|
|
body := SendRequest("POST", hostAddress+"/ScheduledTasks/Running/"+id, apiKey)
|
|
fmt.Println()
|
|
|
|
// If the scheduled task was found, store the task ID for later
|
|
if !strings.Contains(string(body), "Not Found") {
|
|
taskId = id
|
|
break
|
|
}
|
|
}
|
|
|
|
if taskId == "" {
|
|
panic("unable to find scheduled task")
|
|
}
|
|
|
|
fmt.Println("[+] Waiting for analysis task to complete")
|
|
fmt.Print("[+] Episodes analyzed: 0%")
|
|
|
|
var info taskInfo // Last known scheduled task state
|
|
var lastQuery time.Time // Time the task info was last updated
|
|
|
|
for {
|
|
time.Sleep(500 * time.Millisecond)
|
|
|
|
// Update the spinner
|
|
if spinnerIndex++; spinnerIndex >= len(spinners) {
|
|
spinnerIndex = 0
|
|
}
|
|
|
|
fmt.Printf("\r[%s] Episodes analyzed: %d%%", spinners[spinnerIndex], info.CurrentProgressPercentage)
|
|
|
|
if info.CurrentProgressPercentage == 100 {
|
|
fmt.Printf("\r[+]") // reset the spinner
|
|
fmt.Println()
|
|
break
|
|
}
|
|
|
|
// Get the latest task state & unmarshal (only if enough time has passed since the last update)
|
|
if time.Since(lastQuery) <= pollInterval {
|
|
continue
|
|
}
|
|
|
|
lastQuery = time.Now()
|
|
|
|
raw := SendRequest("GET", hostAddress+"/ScheduledTasks/"+taskId+"?hideUrl=1", apiKey)
|
|
|
|
if err := json.Unmarshal(raw, &info); err != nil {
|
|
fmt.Printf("[!] Unable to unmarshal response into taskInfo struct: %s\n", err)
|
|
fmt.Printf("%s\n", raw)
|
|
continue
|
|
}
|
|
|
|
// Print the latest task state
|
|
switch info.State {
|
|
case "Idle":
|
|
info.CurrentProgressPercentage = 100
|
|
}
|
|
}
|
|
}
|