-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #70 from the-eduardo/dev
(Quality of Life) Moved many blocks of code from player.go to new files
- Loading branch information
Showing
10 changed files
with
624 additions
and
774 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,3 @@ | ||
|
||
|
||
|
||
#!/bin/bash | ||
|
||
# Exit immediately if a command exits with a non-zero status | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,3 @@ | ||
|
||
|
||
|
||
|
||
#!/bin/bash | ||
|
||
# Exit immediately if a command exits with a non-zero status | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,3 @@ | ||
|
||
|
||
#!/bin/bash | ||
|
||
# Sai imediatamente se um comando falhar | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
package player | ||
|
||
import ( | ||
"fmt" | ||
"github.com/alvarorichard/Goanime/internal/api" | ||
"github.com/alvarorichard/Goanime/internal/util" | ||
"github.com/hugolgst/rich-go/client" | ||
"log" | ||
"sync" | ||
"time" | ||
) | ||
|
||
type RichPresenceUpdater struct { | ||
anime *api.Anime | ||
isPaused *bool | ||
animeMutex *sync.Mutex | ||
updateFreq time.Duration | ||
done chan bool | ||
wg sync.WaitGroup | ||
startTime time.Time // Start time of playback | ||
episodeDuration time.Duration // Total duration of the episode | ||
episodeStarted bool // Whether the episode has started | ||
socketPath string // Path to mpv IPC socket | ||
} | ||
|
||
func NewRichPresenceUpdater(anime *api.Anime, isPaused *bool, animeMutex *sync.Mutex, updateFreq time.Duration, episodeDuration time.Duration, socketPath string) *RichPresenceUpdater { | ||
return &RichPresenceUpdater{ | ||
anime: anime, | ||
isPaused: isPaused, | ||
animeMutex: animeMutex, | ||
updateFreq: updateFreq, // Make sure updateFreq is actually used in the struct | ||
done: make(chan bool), | ||
startTime: time.Now(), | ||
episodeDuration: episodeDuration, | ||
episodeStarted: false, | ||
socketPath: socketPath, | ||
} | ||
} | ||
|
||
func (rpu *RichPresenceUpdater) GetCurrentPlaybackPosition() (time.Duration, error) { | ||
position, err := mpvSendCommand(rpu.socketPath, []interface{}{"get_property", "time-pos"}) | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
// Convert position to float64 and then to time.Duration | ||
posSeconds, ok := position.(float64) | ||
if !ok { | ||
return 0, fmt.Errorf("failed to parse playback position") | ||
} | ||
|
||
return time.Duration(posSeconds) * time.Second, nil | ||
} | ||
|
||
// Start begins the periodic Rich Presence updates. | ||
func (rpu *RichPresenceUpdater) Start() { | ||
rpu.wg.Add(1) | ||
go func() { | ||
defer rpu.wg.Done() | ||
ticker := time.NewTicker(rpu.updateFreq) | ||
defer ticker.Stop() | ||
|
||
for { | ||
select { | ||
case <-ticker.C: | ||
go rpu.updateDiscordPresence() // Run update asynchronously | ||
case <-rpu.done: | ||
if util.IsDebug { | ||
log.Println("Rich Presence updater received stop signal.") | ||
} | ||
return | ||
} | ||
} | ||
}() | ||
if util.IsDebug { | ||
log.Println("Rich Presence updater started.") | ||
} | ||
} | ||
|
||
// Stop signals the updater to stop and waits for the goroutine to finish. | ||
func (rpu *RichPresenceUpdater) Stop() { | ||
close(rpu.done) | ||
rpu.wg.Wait() | ||
if util.IsDebug { | ||
log.Println("Rich Presence updater stopped.") | ||
|
||
} | ||
} | ||
|
||
func (rpu *RichPresenceUpdater) updateDiscordPresence() { | ||
rpu.animeMutex.Lock() | ||
defer rpu.animeMutex.Unlock() | ||
|
||
currentPosition, err := rpu.GetCurrentPlaybackPosition() | ||
if err != nil { | ||
if util.IsDebug { | ||
log.Printf("Error fetching playback position: %v\n", err) | ||
} | ||
return | ||
} | ||
|
||
// Debug log to check episode duration | ||
if util.IsDebug { | ||
log.Printf("Episode Duration in updateDiscordPresence: %v seconds (%v minutes)\n", rpu.episodeDuration.Seconds(), rpu.episodeDuration.Minutes()) | ||
|
||
} | ||
|
||
// Convert episode duration to minutes and seconds format | ||
totalMinutes := int(rpu.episodeDuration.Minutes()) | ||
totalSeconds := int(rpu.episodeDuration.Seconds()) % 60 // Remaining seconds after full minutes | ||
|
||
// Format the current playback position as minutes and seconds | ||
timeInfo := fmt.Sprintf("%02d:%02d / %02d:%02d", | ||
int(currentPosition.Minutes()), int(currentPosition.Seconds())%60, | ||
totalMinutes, totalSeconds, | ||
) | ||
|
||
// Create the activity with updated Details | ||
activity := client.Activity{ | ||
Details: fmt.Sprintf("%s | Episode %s | %s / %d min", rpu.anime.Details.Title.Romaji, rpu.anime.Episodes[0].Number, timeInfo, totalMinutes), | ||
State: "Watching", | ||
LargeImage: rpu.anime.ImageURL, | ||
LargeText: rpu.anime.Details.Title.Romaji, | ||
Buttons: []*client.Button{ | ||
{Label: "View on AniList", Url: fmt.Sprintf("https://anilist.co/anime/%d", rpu.anime.AnilistID)}, | ||
{Label: "View on MAL", Url: fmt.Sprintf("https://myanimelist.net/anime/%d", rpu.anime.MalID)}, | ||
}, | ||
} | ||
|
||
// Set the activity in Discord Rich Presence | ||
if err := client.SetActivity(activity); err != nil { | ||
if util.IsDebug { | ||
log.Printf("Error updating Discord Rich Presence: %v\n", err) | ||
} else { | ||
log.Printf("Discord Rich Presence updated with elapsed time: %s\n", timeInfo) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package player | ||
|
||
import ( | ||
"github.com/charmbracelet/bubbles/key" | ||
"github.com/charmbracelet/bubbles/progress" | ||
tea "github.com/charmbracelet/bubbletea" | ||
"github.com/charmbracelet/lipgloss" | ||
"strings" | ||
"time" | ||
) | ||
|
||
// Update handles updates to the Bubble Tea model. | ||
// | ||
// This function processes incoming messages (`tea.Msg`) and updates the model's state accordingly. | ||
// It locks the model's mutex to ensure thread safety, especially when modifying shared data like | ||
// `m.received`, `m.totalBytes`, and other stateful properties. | ||
// | ||
// The function processes different message types, including: | ||
// | ||
// 1. `tickMsg`: A periodic message that triggers the progress update. If the download is complete | ||
// (`m.done` is `true`), the program quits. Otherwise, it calculates the percentage of bytes received | ||
// and updates the progress bar. It then schedules the next tick. | ||
// | ||
// 2. `statusMsg`: Updates the status string in the model, which can be used to display custom messages | ||
// to the user, such as "Downloading..." or "Download complete". | ||
// | ||
// 3. `progress.FrameMsg`: Handles frame updates for the progress bar. It delegates the update to the | ||
// internal `progress.Model` and returns any commands necessary to refresh the UI. | ||
// | ||
// 4. `tea.KeyMsg`: Responds to key events, such as quitting the program when "Ctrl+C" is pressed. | ||
// If the user requests to quit, the program sets `m.done` to `true` and returns the quit command. | ||
// | ||
// For unhandled message types, it returns the model unchanged. | ||
// | ||
// Returns: | ||
// - Updated `tea.Model` representing the current state of the model. | ||
// - A `tea.Cmd` that specifies the next action the program should perform. | ||
func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { | ||
m.mu.Lock() | ||
defer m.mu.Unlock() | ||
|
||
switch msg := msg.(type) { | ||
case tickMsg: | ||
if m.done { | ||
return m, tea.Quit | ||
} | ||
if m.totalBytes > 0 { | ||
cmd := m.progress.SetPercent(float64(m.received) / float64(m.totalBytes)) | ||
return m, tea.Batch(cmd, tickCmd()) | ||
} | ||
return m, tickCmd() | ||
|
||
case statusMsg: | ||
m.status = string(msg) | ||
return m, nil | ||
|
||
case progress.FrameMsg: | ||
var cmd tea.Cmd | ||
var newModel tea.Model | ||
newModel, cmd = m.progress.Update(msg) | ||
m.progress = newModel.(progress.Model) | ||
return m, cmd | ||
|
||
case tea.KeyMsg: | ||
if key.Matches(msg, m.keys.quit) { | ||
m.done = true | ||
return m, tea.Quit | ||
} | ||
return m, nil | ||
|
||
default: | ||
return m, nil | ||
} | ||
} | ||
|
||
// View renders the Bubble Tea model | ||
// View renders the user interface for the Bubble Tea model. | ||
// | ||
// This function generates the visual output that is displayed to the user. It includes the status message, | ||
// the progress bar, and a quit instruction. The layout is formatted with padding for proper alignment. | ||
// | ||
// Steps: | ||
// 1. Adds padding to each line using spaces. | ||
// 2. Styles the status message (m.status) with an orange color (#FFA500). | ||
// 3. Displays the progress bar using the progress model. | ||
// 4. Shows a message instructing the user to press "Ctrl+C" to quit. | ||
// | ||
// Returns: | ||
// - A formatted string that represents the UI for the current state of the model. | ||
func (m *model) View() string { | ||
// Creates padding spaces for consistent layout | ||
pad := strings.Repeat(" ", padding) | ||
|
||
// Styles the status message with an orange color | ||
statusStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#FFA500")) | ||
|
||
// Returns the UI layout: status message, progress bar, and quit instruction | ||
return "\n" + | ||
pad + statusStyle.Render(m.status) + "\n\n" + // Render the styled status message | ||
pad + m.progress.View() + "\n\n" + // Render the progress bar | ||
pad + "Press Ctrl+C to quit" // Show quit instruction | ||
} | ||
|
||
// tickCmd returns a command that triggers a "tick" every 100 milliseconds. | ||
// | ||
// This function sets up a recurring event (tick) that fires every 100 milliseconds. | ||
// Each tick sends a `tickMsg` with the current time (`t`) as a message, which can be | ||
// handled by the update function to trigger actions like updating the progress bar. | ||
// | ||
// Returns: | ||
// - A `tea.Cmd` that schedules a tick every 100 milliseconds and sends a `tickMsg`. | ||
func tickCmd() tea.Cmd { | ||
return tea.Tick(time.Millisecond*100, func(t time.Time) tea.Msg { | ||
return tickMsg(t) | ||
}) | ||
} |
Oops, something went wrong.