Add logging to file and stdout

This commit is contained in:
Xpl0itU 2023-07-21 15:40:27 +02:00
parent 7d0c7be191
commit fc50653f13
5 changed files with 136 additions and 49 deletions

1
.gitignore vendored
View file

@ -8,3 +8,4 @@ output/
main main
*.a *.a
out/ out/
log.txt

View file

@ -10,23 +10,28 @@ import (
) )
func main() { func main() {
logger, err := wiiudownloader.NewLogger("log.txt")
if err != nil {
fmt.Println("Error:", err)
return
}
// Check if user is running macOS // Check if user is running macOS
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
execPath, err := os.Executable() execPath, err := os.Executable()
if err != nil { if err != nil {
fmt.Println("Error:", err) logger.Error(err.Error())
return return
} }
bundlePath := filepath.Join(filepath.Dir(filepath.Dir(execPath))) bundlePath := filepath.Join(filepath.Dir(filepath.Dir(execPath)))
filePath := filepath.Join(bundlePath, "Resources/lib/share/glib-schemas") filePath := filepath.Join(bundlePath, "Resources/lib/share/glib-schemas")
if _, err := os.Stat(filePath); os.IsNotExist(err) { if _, err := os.Stat(filePath); os.IsNotExist(err) {
fmt.Println("glib-schemas not found") logger.Warning("glib-schemas not found")
} else { } else {
os.Setenv("GSETTINGS_SCHEMA_DIR", filePath) os.Setenv("GSETTINGS_SCHEMA_DIR", filePath)
} }
} }
win := NewMainWindow(wiiudownloader.GetTitleEntries(wiiudownloader.TITLE_CATEGORY_GAME)) win := NewMainWindow(wiiudownloader.GetTitleEntries(wiiudownloader.TITLE_CATEGORY_GAME), logger)
win.ShowAll() win.ShowAll()
Main() Main()

View file

@ -2,7 +2,6 @@ package main
import ( import (
"fmt" "fmt"
"log"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -32,14 +31,15 @@ type MainWindow struct {
decryptContents bool decryptContents bool
currentRegion uint8 currentRegion uint8
deleteEncryptedContentsCheckbox *gtk.CheckButton deleteEncryptedContentsCheckbox *gtk.CheckButton
logger *wiiudownloader.Logger
} }
func NewMainWindow(entries []wiiudownloader.TitleEntry) *MainWindow { func NewMainWindow(entries []wiiudownloader.TitleEntry, logger *wiiudownloader.Logger) *MainWindow {
gtk.Init(nil) gtk.Init(nil)
win, err := gtk.WindowNew(gtk.WINDOW_TOPLEVEL) win, err := gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
if err != nil { if err != nil {
log.Fatal("Unable to create window:", err) logger.Fatal("Unable to create window:", err)
} }
win.SetTitle("WiiUDownloader") win.SetTitle("WiiUDownloader")
@ -50,7 +50,7 @@ func NewMainWindow(entries []wiiudownloader.TitleEntry) *MainWindow {
searchEntry, err := gtk.EntryNew() searchEntry, err := gtk.EntryNew()
if err != nil { if err != nil {
log.Fatal("Unable to create search entry:", err) logger.Fatal("Unable to create search entry:", err)
} }
mainWindow := MainWindow{ mainWindow := MainWindow{
@ -58,6 +58,7 @@ func NewMainWindow(entries []wiiudownloader.TitleEntry) *MainWindow {
titles: entries, titles: entries,
searchEntry: searchEntry, searchEntry: searchEntry,
currentRegion: wiiudownloader.MCP_REGION_EUROPE | wiiudownloader.MCP_REGION_JAPAN | wiiudownloader.MCP_REGION_USA, currentRegion: wiiudownloader.MCP_REGION_EUROPE | wiiudownloader.MCP_REGION_JAPAN | wiiudownloader.MCP_REGION_USA,
logger: logger,
} }
searchEntry.Connect("changed", mainWindow.onSearchEntryChanged) searchEntry.Connect("changed", mainWindow.onSearchEntryChanged)
@ -68,7 +69,7 @@ func NewMainWindow(entries []wiiudownloader.TitleEntry) *MainWindow {
func (mw *MainWindow) updateTitles(titles []wiiudownloader.TitleEntry) { func (mw *MainWindow) updateTitles(titles []wiiudownloader.TitleEntry) {
store, err := gtk.ListStoreNew(glib.TYPE_BOOLEAN, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_STRING) store, err := gtk.ListStoreNew(glib.TYPE_BOOLEAN, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_STRING)
if err != nil { if err != nil {
log.Fatal("Unable to create list store:", err) mw.logger.Fatal("Unable to create list store:", err)
} }
for _, entry := range titles { for _, entry := range titles {
@ -81,7 +82,7 @@ func (mw *MainWindow) updateTitles(titles []wiiudownloader.TitleEntry) {
[]interface{}{mw.isTitleInQueue(entry), entry.Name, wiiudownloader.GetFormattedKind(entry.TitleID), fmt.Sprintf("%016x", entry.TitleID), wiiudownloader.GetFormattedRegion(entry.Region)}, []interface{}{mw.isTitleInQueue(entry), entry.Name, wiiudownloader.GetFormattedKind(entry.TitleID), fmt.Sprintf("%016x", entry.TitleID), wiiudownloader.GetFormattedRegion(entry.Region)},
) )
if err != nil { if err != nil {
log.Fatal("Unable to set values:", err) mw.logger.Fatal("Unable to set values:", err)
} }
} }
mw.treeView.SetModel(store) mw.treeView.SetModel(store)
@ -90,7 +91,7 @@ func (mw *MainWindow) updateTitles(titles []wiiudownloader.TitleEntry) {
func (mw *MainWindow) ShowAll() { func (mw *MainWindow) ShowAll() {
store, err := gtk.ListStoreNew(glib.TYPE_BOOLEAN, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_STRING) store, err := gtk.ListStoreNew(glib.TYPE_BOOLEAN, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_STRING)
if err != nil { if err != nil {
log.Fatal("Unable to create list store:", err) mw.logger.Fatal("Unable to create list store:", err)
} }
for _, entry := range mw.titles { for _, entry := range mw.titles {
@ -103,18 +104,18 @@ func (mw *MainWindow) ShowAll() {
[]interface{}{mw.isTitleInQueue(entry), entry.Name, wiiudownloader.GetFormattedKind(entry.TitleID), fmt.Sprintf("%016x", entry.TitleID), wiiudownloader.GetFormattedRegion(entry.Region)}, []interface{}{mw.isTitleInQueue(entry), entry.Name, wiiudownloader.GetFormattedKind(entry.TitleID), fmt.Sprintf("%016x", entry.TitleID), wiiudownloader.GetFormattedRegion(entry.Region)},
) )
if err != nil { if err != nil {
log.Fatal("Unable to set values:", err) mw.logger.Fatal("Unable to set values:", err)
} }
} }
mw.treeView, err = gtk.TreeViewNew() mw.treeView, err = gtk.TreeViewNew()
if err != nil { if err != nil {
log.Fatal("Unable to create tree view:", err) mw.logger.Fatal("Unable to create tree view:", err)
} }
selection, err := mw.treeView.GetSelection() selection, err := mw.treeView.GetSelection()
if err != nil { if err != nil {
log.Fatal("Unable to get selection:", err) mw.logger.Fatal("Unable to get selection:", err)
} }
selection.SetMode(gtk.SELECTION_MULTIPLE) selection.SetMode(gtk.SELECTION_MULTIPLE)
@ -122,53 +123,53 @@ func (mw *MainWindow) ShowAll() {
toggleRenderer, err := gtk.CellRendererToggleNew() toggleRenderer, err := gtk.CellRendererToggleNew()
if err != nil { if err != nil {
log.Fatal("Unable to create cell renderer toggle:", err) mw.logger.Fatal("Unable to create cell renderer toggle:", err)
} }
column, err := gtk.TreeViewColumnNewWithAttribute("Queue", toggleRenderer, "active", IN_QUEUE_COLUMN) column, err := gtk.TreeViewColumnNewWithAttribute("Queue", toggleRenderer, "active", IN_QUEUE_COLUMN)
if err != nil { if err != nil {
log.Fatal("Unable to create tree view column:", err) mw.logger.Fatal("Unable to create tree view column:", err)
} }
mw.treeView.AppendColumn(column) mw.treeView.AppendColumn(column)
renderer, err := gtk.CellRendererTextNew() renderer, err := gtk.CellRendererTextNew()
if err != nil { if err != nil {
log.Fatal("Unable to create cell renderer:", err) mw.logger.Fatal("Unable to create cell renderer:", err)
} }
column, err = gtk.TreeViewColumnNewWithAttribute("Name", renderer, "text", NAME_COLUMN) column, err = gtk.TreeViewColumnNewWithAttribute("Name", renderer, "text", NAME_COLUMN)
if err != nil { if err != nil {
log.Fatal("Unable to create tree view column:", err) mw.logger.Fatal("Unable to create tree view column:", err)
} }
mw.treeView.AppendColumn(column) mw.treeView.AppendColumn(column)
renderer, err = gtk.CellRendererTextNew() renderer, err = gtk.CellRendererTextNew()
if err != nil { if err != nil {
log.Fatal("Unable to create cell renderer:", err) mw.logger.Fatal("Unable to create cell renderer:", err)
} }
column, err = gtk.TreeViewColumnNewWithAttribute("Kind", renderer, "text", KIND_COLUMN) column, err = gtk.TreeViewColumnNewWithAttribute("Kind", renderer, "text", KIND_COLUMN)
if err != nil { if err != nil {
log.Fatal("Unable to create tree view column:", err) mw.logger.Fatal("Unable to create tree view column:", err)
} }
mw.treeView.AppendColumn(column) mw.treeView.AppendColumn(column)
renderer, err = gtk.CellRendererTextNew() renderer, err = gtk.CellRendererTextNew()
if err != nil { if err != nil {
log.Fatal("Unable to create cell renderer:", err) mw.logger.Fatal("Unable to create cell renderer:", err)
} }
column, err = gtk.TreeViewColumnNewWithAttribute("Title ID", renderer, "text", TITLE_ID_COLUMN) column, err = gtk.TreeViewColumnNewWithAttribute("Title ID", renderer, "text", TITLE_ID_COLUMN)
if err != nil { if err != nil {
log.Fatal("Unable to create tree view column:", err) mw.logger.Fatal("Unable to create tree view column:", err)
} }
mw.treeView.AppendColumn(column) mw.treeView.AppendColumn(column)
column, err = gtk.TreeViewColumnNewWithAttribute("Region", renderer, "text", REGION_COLUMN) column, err = gtk.TreeViewColumnNewWithAttribute("Region", renderer, "text", REGION_COLUMN)
if err != nil { if err != nil {
log.Fatal("Unable to create tree view column:", err) mw.logger.Fatal("Unable to create tree view column:", err)
} }
mw.treeView.AppendColumn(column) mw.treeView.AppendColumn(column)
mainvBox, err := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) mainvBox, err := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
if err != nil { if err != nil {
log.Fatal("Unable to create box:", err) mw.logger.Fatal("Unable to create box:", err)
} }
menuBar, _ := gtk.MenuBarNew() menuBar, _ := gtk.MenuBarNew()
toolsSubMenu, _ := gtk.MenuNew() toolsSubMenu, _ := gtk.MenuNew()
@ -182,7 +183,7 @@ func (mw *MainWindow) ShowAll() {
} }
dialog, err := gtk.FileChooserNativeDialogNew("Select the path to decrypt", mw.window, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, "Select", "Cancel") dialog, err := gtk.FileChooserNativeDialogNew("Select the path to decrypt", mw.window, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, "Select", "Cancel")
if err != nil { if err != nil {
log.Fatal("Unable to create dialog:", err) mw.logger.Fatal("Unable to create dialog:", err)
} }
res := dialog.Run() res := dialog.Run()
if res != int(gtk.RESPONSE_ACCEPT) { if res != int(gtk.RESPONSE_ACCEPT) {
@ -208,14 +209,14 @@ func (mw *MainWindow) ShowAll() {
mainvBox.PackStart(menuBar, false, false, 0) mainvBox.PackStart(menuBar, false, false, 0)
tophBox, err := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) tophBox, err := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
if err != nil { if err != nil {
log.Fatal("Unable to create box:", err) mw.logger.Fatal("Unable to create box:", err)
} }
mw.categoryButtons = make([]*gtk.ToggleButton, 0) mw.categoryButtons = make([]*gtk.ToggleButton, 0)
for _, cat := range []string{"Game", "Update", "DLC", "Demo", "All"} { for _, cat := range []string{"Game", "Update", "DLC", "Demo", "All"} {
button, err := gtk.ToggleButtonNewWithLabel(cat) button, err := gtk.ToggleButtonNewWithLabel(cat)
if err != nil { if err != nil {
log.Fatal("Unable to create toggle button:", err) mw.logger.Fatal("Unable to create toggle button:", err)
continue continue
} }
tophBox.PackStart(button, false, false, 0) tophBox.PackStart(button, false, false, 0)
@ -232,7 +233,7 @@ func (mw *MainWindow) ShowAll() {
scrollable, err := gtk.ScrolledWindowNew(nil, nil) scrollable, err := gtk.ScrolledWindowNew(nil, nil)
if err != nil { if err != nil {
log.Fatal("Unable to create scrolled window:", err) mw.logger.Fatal("Unable to create scrolled window:", err)
} }
scrollable.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) scrollable.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
selection.Connect("changed", mw.onSelectionChanged) selection.Connect("changed", mw.onSelectionChanged)
@ -242,27 +243,27 @@ func (mw *MainWindow) ShowAll() {
bottomhBox, err := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) bottomhBox, err := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
if err != nil { if err != nil {
log.Fatal("Unable to create box:", err) mw.logger.Fatal("Unable to create box:", err)
} }
mw.addToQueueButton, err = gtk.ButtonNewWithLabel("Add to queue") mw.addToQueueButton, err = gtk.ButtonNewWithLabel("Add to queue")
if err != nil { if err != nil {
log.Fatal("Unable to create button:", err) mw.logger.Fatal("Unable to create button:", err)
} }
downloadQueueButton, err := gtk.ButtonNewWithLabel("Download queue") downloadQueueButton, err := gtk.ButtonNewWithLabel("Download queue")
if err != nil { if err != nil {
log.Fatal("Unable to create button:", err) mw.logger.Fatal("Unable to create button:", err)
} }
decryptContentsCheckbox, err := gtk.CheckButtonNewWithLabel("Decrypt contents") decryptContentsCheckbox, err := gtk.CheckButtonNewWithLabel("Decrypt contents")
if err != nil { if err != nil {
log.Fatal("Unable to create button:", err) mw.logger.Fatal("Unable to create button:", err)
} }
mw.deleteEncryptedContentsCheckbox, err = gtk.CheckButtonNewWithLabel("Delete encrypted contents after decryption") mw.deleteEncryptedContentsCheckbox, err = gtk.CheckButtonNewWithLabel("Delete encrypted contents after decryption")
if err != nil { if err != nil {
log.Fatal("Unable to create button:", err) mw.logger.Fatal("Unable to create button:", err)
} }
mw.deleteEncryptedContentsCheckbox.SetSensitive(false) mw.deleteEncryptedContentsCheckbox.SetSensitive(false)
@ -274,7 +275,7 @@ func (mw *MainWindow) ShowAll() {
} }
dialog, err := gtk.FileChooserNativeDialogNew("Select a path to save the games to", mw.window, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, "Select", "Cancel") dialog, err := gtk.FileChooserNativeDialogNew("Select a path to save the games to", mw.window, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, "Select", "Cancel")
if err != nil { if err != nil {
log.Fatal("Unable to create dialog:", err) mw.logger.Fatal("Unable to create dialog:", err)
} }
res := dialog.Run() res := dialog.Run()
if res != int(gtk.RESPONSE_ACCEPT) { if res != int(gtk.RESPONSE_ACCEPT) {
@ -307,7 +308,7 @@ func (mw *MainWindow) ShowAll() {
japanButton, err := gtk.CheckButtonNewWithLabel("Japan") japanButton, err := gtk.CheckButtonNewWithLabel("Japan")
japanButton.SetActive(true) japanButton.SetActive(true)
if err != nil { if err != nil {
log.Fatal("Unable to create button:", err) mw.logger.Fatal("Unable to create button:", err)
} }
japanButton.Connect("clicked", func() { japanButton.Connect("clicked", func() {
mw.onRegionChange(japanButton, wiiudownloader.MCP_REGION_JAPAN) mw.onRegionChange(japanButton, wiiudownloader.MCP_REGION_JAPAN)
@ -317,7 +318,7 @@ func (mw *MainWindow) ShowAll() {
usaButton, err := gtk.CheckButtonNewWithLabel("USA") usaButton, err := gtk.CheckButtonNewWithLabel("USA")
usaButton.SetActive(true) usaButton.SetActive(true)
if err != nil { if err != nil {
log.Fatal("Unable to create button:", err) mw.logger.Fatal("Unable to create button:", err)
} }
usaButton.Connect("clicked", func() { usaButton.Connect("clicked", func() {
mw.onRegionChange(usaButton, wiiudownloader.MCP_REGION_USA) mw.onRegionChange(usaButton, wiiudownloader.MCP_REGION_USA)
@ -327,7 +328,7 @@ func (mw *MainWindow) ShowAll() {
europeButton, err := gtk.CheckButtonNewWithLabel("Europe") europeButton, err := gtk.CheckButtonNewWithLabel("Europe")
europeButton.SetActive(true) europeButton.SetActive(true)
if err != nil { if err != nil {
log.Fatal("Unable to create button:", err) mw.logger.Fatal("Unable to create button:", err)
} }
europeButton.Connect("clicked", func() { europeButton.Connect("clicked", func() {
mw.onRegionChange(europeButton, wiiudownloader.MCP_REGION_EUROPE) mw.onRegionChange(europeButton, wiiudownloader.MCP_REGION_EUROPE)
@ -363,7 +364,7 @@ func (mw *MainWindow) onSearchEntryChanged() {
func (mw *MainWindow) filterTitles(filterText string) { func (mw *MainWindow) filterTitles(filterText string) {
store, err := mw.treeView.GetModel() store, err := mw.treeView.GetModel()
if err != nil { if err != nil {
log.Fatal("Unable to get tree view model:", err) mw.logger.Fatal("Unable to get tree view model:", err)
} }
storeRef := store.(*gtk.ListStore) storeRef := store.(*gtk.ListStore)
@ -381,7 +382,7 @@ func (mw *MainWindow) filterTitles(filterText string) {
[]interface{}{entry.Name, wiiudownloader.GetFormattedKind(entry.TitleID), fmt.Sprintf("%016x", entry.TitleID), wiiudownloader.GetFormattedRegion(entry.Region)}, []interface{}{entry.Name, wiiudownloader.GetFormattedKind(entry.TitleID), fmt.Sprintf("%016x", entry.TitleID), wiiudownloader.GetFormattedRegion(entry.Region)},
) )
if err != nil { if err != nil {
log.Fatal("Unable to set values:", err) mw.logger.Fatal("Unable to set values:", err)
} }
} }
} }
@ -406,11 +407,11 @@ func (mw *MainWindow) onDecryptContentsMenuItemClicked(selectedPath string) erro
func (mw *MainWindow) isSelectionInQueue() bool { func (mw *MainWindow) isSelectionInQueue() bool {
selection, err := mw.treeView.GetSelection() selection, err := mw.treeView.GetSelection()
if err != nil { if err != nil {
log.Fatal("Unable to get selection:", err) mw.logger.Fatal("Unable to get selection:", err)
} }
treeModel, err := mw.treeView.GetModel() treeModel, err := mw.treeView.GetModel()
if err != nil { if err != nil {
log.Fatal("Unable to get model:", err) mw.logger.Fatal("Unable to get model:", err)
} }
allTitlesInQueue := true allTitlesInQueue := true
pathlist := selection.GetSelectedRows(treeModel) pathlist := selection.GetSelectedRows(treeModel)
@ -468,7 +469,7 @@ func (mw *MainWindow) isTitleInQueue(title wiiudownloader.TitleEntry) bool {
func (mw *MainWindow) addToQueue(tid string, name string) { func (mw *MainWindow) addToQueue(tid string, name string) {
titleID, err := strconv.ParseUint(tid, 16, 64) titleID, err := strconv.ParseUint(tid, 16, 64)
if err != nil { if err != nil {
log.Fatal("Unable to parse title ID:", err) mw.logger.Fatal("Unable to parse title ID:", err)
} }
mw.titleQueue = append(mw.titleQueue, wiiudownloader.TitleEntry{TitleID: titleID, Name: name}) mw.titleQueue = append(mw.titleQueue, wiiudownloader.TitleEntry{TitleID: titleID, Name: name})
} }
@ -485,11 +486,11 @@ func (mw *MainWindow) removeFromQueue(tid string) {
func (mw *MainWindow) onAddToQueueClicked() { func (mw *MainWindow) onAddToQueueClicked() {
selection, err := mw.treeView.GetSelection() selection, err := mw.treeView.GetSelection()
if err != nil { if err != nil {
log.Fatal("Unable to get selection:", err) mw.logger.Fatal("Unable to get selection:", err)
} }
treeModel, err := mw.treeView.GetModel() treeModel, err := mw.treeView.GetModel()
if err != nil { if err != nil {
log.Fatal("Unable to get model:", err) mw.logger.Fatal("Unable to get model:", err)
} }
pathlist := selection.GetSelectedRows(treeModel) pathlist := selection.GetSelectedRows(treeModel)
addToQueue := !mw.isSelectionInQueue() addToQueue := !mw.isSelectionInQueue()
@ -528,7 +529,7 @@ func (mw *MainWindow) onAddToQueueClicked() {
func (mw *MainWindow) updateTitlesInQueue() { func (mw *MainWindow) updateTitlesInQueue() {
store, err := mw.treeView.GetModel() store, err := mw.treeView.GetModel()
if err != nil { if err != nil {
log.Fatal("Unable to get tree view model:", err) mw.logger.Fatal("Unable to get tree view model:", err)
} }
storeRef := store.(*gtk.ListStore) storeRef := store.(*gtk.ListStore)
@ -574,7 +575,7 @@ func (mw *MainWindow) onDownloadQueueClicked(selectedPath string) error {
tidStr := fmt.Sprintf("%016x", title.TitleID) tidStr := fmt.Sprintf("%016x", title.TitleID)
titlePath := fmt.Sprintf("%s/%s [%s] [%s]", selectedPath, title.Name, wiiudownloader.GetFormattedKind(title.TitleID), tidStr) titlePath := fmt.Sprintf("%s/%s [%s] [%s]", selectedPath, title.Name, wiiudownloader.GetFormattedKind(title.TitleID), tidStr)
if err := wiiudownloader.DownloadTitle(tidStr, titlePath, mw.decryptContents, progressWindow, mw.getDeleteEncryptedContents()); err != nil { if err := wiiudownloader.DownloadTitle(tidStr, titlePath, mw.decryptContents, progressWindow, mw.getDeleteEncryptedContents(), mw.logger); err != nil {
queueCancelled = true queueCancelled = true
errorHappened = true errorHappened = true
ch <- err ch <- err

View file

@ -123,11 +123,10 @@ func downloadFile(progressWindow *ProgressWindow, client *grab.Client, downloadU
case <-resp.Done: case <-resp.Done:
if err := resp.Err(); err != nil { if err := resp.Err(); err != nil {
if doRetries && attempt < maxRetries { if doRetries && attempt < maxRetries {
fmt.Printf("[Error] Download attempt %d failed: %+v\n", attempt, err)
time.Sleep(retryDelay) time.Sleep(retryDelay)
break Loop break Loop
} }
return fmt.Errorf("download error: %+v", err) return fmt.Errorf("download error after %d attempts: %+v", attempt, err)
} }
break Loop break Loop
} }
@ -137,7 +136,7 @@ func downloadFile(progressWindow *ProgressWindow, client *grab.Client, downloadU
return nil return nil
} }
func DownloadTitle(titleID string, outputDirectory string, doDecryption bool, progressWindow *ProgressWindow, deleteEncryptedContents bool) error { func DownloadTitle(titleID string, outputDirectory string, doDecryption bool, progressWindow *ProgressWindow, deleteEncryptedContents bool, logger *Logger) error {
progressWindow.cancelButton.Connect("clicked", func() { progressWindow.cancelButton.Connect("clicked", func() {
progressWindow.cancelled = true progressWindow.cancelled = true
}) })
@ -222,7 +221,7 @@ func DownloadTitle(titleID string, outputDirectory string, doDecryption bool, pr
return err return err
} }
defer certFile.Close() defer certFile.Close()
fmt.Printf("[Info] Certificate saved to ./%v \n", certPath) logger.Info("Certificate saved to ./%v \n", certPath)
c, err := aes.NewCipher(commonKey) c, err := aes.NewCipher(commonKey)
if err != nil { if err != nil {

81
logger.go Normal file
View file

@ -0,0 +1,81 @@
package wiiudownloader
import (
"io"
"log"
"os"
)
type LogLevel int
const (
Info LogLevel = iota
Warning
Error
Fatal
)
var logLevelStrings = map[LogLevel]string{
Info: "[Info]",
Warning: "[Warning]",
Error: "[Error]",
Fatal: "[Fatal]",
}
type Logger struct {
logFile *os.File
logger *log.Logger
}
func NewLogger(logFilePath string) (*Logger, error) {
var logFile *os.File
var err error
// If logFilePath is empty, log only to stdout
if logFilePath == "" {
return &Logger{
logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
}, nil
}
// Open the log file for writing, truncating it if it exists
logFile, err = os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
if err != nil {
// If unable to open the log file, log the error to stdout
log.New(os.Stdout, "", log.Ldate|log.Ltime).Printf("[Error] Unable to open log file: %v\n", err)
return &Logger{
logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
}, nil
}
// Create the logger that writes to both stdout and the file
logger := log.New(io.MultiWriter(os.Stdout, logFile), "", log.Ldate|log.Ltime)
return &Logger{
logFile: logFile,
logger: logger,
}, nil
}
func (l *Logger) log(level LogLevel, format string, v ...interface{}) {
if prefix, ok := logLevelStrings[level]; ok {
l.logger.Printf(prefix+" "+format, v...)
}
}
func (l *Logger) Info(format string, v ...interface{}) {
l.log(Info, format, v...)
}
func (l *Logger) Warning(format string, v ...interface{}) {
l.log(Warning, format, v...)
}
func (l *Logger) Error(format string, v ...interface{}) {
l.log(Error, format, v...)
}
func (l *Logger) Fatal(format string, v ...interface{}) {
l.log(Fatal, format, v...)
os.Exit(1)
}