-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add rss support * Fix the humanize episode length * Add rss local cache support * Use mplayer as an alternative player
- Loading branch information
1 parent
57a022d
commit 84de861
Showing
20 changed files
with
659 additions
and
1,166 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
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,154 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"github.com/AlecAivazis/survey/v2" | ||
"github.com/gdamore/tcell" | ||
"github.com/linuxsuren/goplay/pkg/advanced_ui" | ||
"github.com/linuxsuren/goplay/pkg/rss" | ||
exec2 "github.com/linuxsuren/http-downloader/pkg/exec" | ||
"github.com/spf13/cobra" | ||
"os/exec" | ||
"time" | ||
) | ||
|
||
func NewPlayCommand() (cmd *cobra.Command) { | ||
opt := &playOption{} | ||
|
||
cmd = &cobra.Command{ | ||
Use: "goplay", | ||
Example: "goplay opensource", | ||
Short: "Play podcast", | ||
Args: cobra.MinimumNArgs(1), | ||
RunE: opt.runE, | ||
} | ||
return | ||
} | ||
|
||
type playOption struct { | ||
} | ||
|
||
func (o *playOption) runE(cmd *cobra.Command, args []string) (err error) { | ||
keyword := args[0] | ||
|
||
feeds, titles := rss.FindMapByKeyword(keyword) | ||
if len(titles) == 0 { | ||
err = fmt.Errorf("no podcast found by keyword: %s", keyword) | ||
return | ||
} | ||
selector := &survey.Select{ | ||
Message: "Select a podcast", | ||
Options: titles, | ||
} | ||
var choose string | ||
if err = survey.AskOne(selector, &choose); err != nil { | ||
return | ||
} | ||
|
||
feed := feeds[choose] | ||
var episodes map[string]rss.Episode | ||
episodes, titles = rss.ConvertEpisodeMap(feed.Items) | ||
if len(titles) == 0 { | ||
err = fmt.Errorf("no episode found by keyword: %s", keyword) | ||
return | ||
} | ||
selector = &survey.Select{ | ||
Message: "Select an episode", | ||
Options: titles, | ||
} | ||
if err = survey.AskOne(selector, &choose); err != nil { | ||
return | ||
} | ||
|
||
episode := episodes[choose] | ||
if !isSupport(episode) { | ||
var ok bool | ||
if ok, err = playWithPotentialTools(episode); !ok || err != nil { | ||
err = fmt.Errorf("currently, only support mp3") | ||
} | ||
} else { | ||
err = play(episode) | ||
} | ||
return | ||
} | ||
|
||
func isSupport(episode rss.Episode) bool { | ||
return episode.Type == "audio/mpeg" | ||
} | ||
|
||
func playWithPotentialTools(episode rss.Episode) (ok bool, err error) { | ||
var mplayer string | ||
if mplayer, err = exec.LookPath("mplayer"); err == nil { | ||
selector := &survey.Confirm{ | ||
Message: fmt.Sprintf("Do you want to play '%s' with mplayer?", episode.Title), | ||
} | ||
|
||
err = survey.AskOne(selector, &ok) | ||
if err == nil && ok { | ||
var audioCacheFile string | ||
if audioCacheFile, err = advanced_ui.LoadAudioFile(episode.AudioLink); err == nil { | ||
err = exec2.RunCommand(mplayer, audioCacheFile) | ||
} | ||
} | ||
} | ||
return | ||
} | ||
|
||
func play(episode rss.Episode) (err error) { | ||
if _, err = advanced_ui.LoadAudioFile(episode.AudioLink); err != nil { | ||
err = fmt.Errorf("failed to load audio file from: %s, error: %v", episode.AudioLink, err) | ||
return | ||
} | ||
|
||
screen, err := tcell.NewScreen() | ||
if err != nil { | ||
panic(err) | ||
} | ||
err = screen.Init() | ||
if err != nil { | ||
panic(err) | ||
} | ||
defer screen.Fini() | ||
|
||
var player *advanced_ui.TrackAudioPanel | ||
if player, err = advanced_ui.NewTrackAudioPanel(episode); err != nil { | ||
err = fmt.Errorf("failed to play '%s', error: %v", episode.Title, err) | ||
return | ||
} | ||
|
||
screen.Clear() | ||
player.Draw(screen) | ||
screen.Show() | ||
|
||
_ = player.Start() | ||
|
||
seconds := time.Tick(time.Second) | ||
events := make(chan tcell.Event) | ||
go func() { | ||
for { | ||
events <- screen.PollEvent() | ||
} | ||
}() | ||
|
||
loop: | ||
for { | ||
select { | ||
case event := <-events: | ||
changed, quit := player.Handle(event) | ||
if quit { | ||
player.Stop() | ||
break loop | ||
} | ||
if changed { | ||
screen.Clear() | ||
player.Draw(screen) | ||
screen.Show() | ||
} | ||
case <-seconds: | ||
screen.Clear() | ||
player.Draw(screen) | ||
screen.Show() | ||
} | ||
} | ||
return | ||
} |
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,11 +1,17 @@ | ||
module github.com/linuxsuren/goplay | ||
|
||
go 1.14 | ||
go 1.16 | ||
|
||
require ( | ||
github.com/AlecAivazis/survey/v2 v2.3.2 | ||
github.com/SlyMarbo/rss v1.0.1 | ||
github.com/faiface/beep v1.1.0 | ||
github.com/gdamore/tcell v1.3.0 | ||
github.com/json-iterator/go v1.1.11 | ||
github.com/ghodss/yaml v1.0.0 | ||
github.com/linuxsuren/http-downloader v0.0.50 | ||
github.com/spf13/cobra v1.2.1 | ||
github.com/spf13/viper v1.9.0 | ||
github.com/stretchr/testify v1.7.0 | ||
) | ||
|
||
replace github.com/SlyMarbo/rss v1.0.1 => github.com/LinuxSuRen/rss v1.0.2-0.20211120161457-3f8efe372d7a |
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,130 +1,9 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"github.com/AlecAivazis/survey/v2" | ||
"github.com/gdamore/tcell" | ||
"github.com/linuxsuren/goplay/pkg/advanced_ui" | ||
"github.com/linuxsuren/goplay/pkg/broadcast" | ||
"os" | ||
"time" | ||
"github.com/linuxsuren/goplay/cmd" | ||
) | ||
|
||
func main() { | ||
if len(os.Args) < 2 { | ||
fmt.Println("Usage: goplay keyword") | ||
return | ||
} | ||
keyword := os.Args[1] | ||
|
||
var albumID int | ||
if result, err := broadcast.Search(keyword); err != nil { | ||
panic(err) | ||
} else if len(result.Data.AlbumViews.Albums) == 0 { | ||
fmt.Println("not found any albums by keyword:", keyword) | ||
return | ||
} else { | ||
albumTitle := make([]string, 0) | ||
albumMap := make(map[string]int, 0) | ||
for _, v := range result.Data.AlbumViews.Albums { | ||
albumTitle = append(albumTitle, v.AlbumInfo.Title) | ||
albumMap[v.AlbumInfo.Title] = v.AlbumInfo.ID | ||
} | ||
|
||
selector := &survey.Select{ | ||
Message: "Select a album", | ||
Options: albumTitle, | ||
} | ||
var choose string | ||
if err = survey.AskOne(selector, &choose); err != nil { | ||
return | ||
} | ||
albumID = albumMap[choose] | ||
} | ||
|
||
tracks, err := broadcast.GetTrackList(albumID, 1, false) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
trackTitle := make([]string, 0) | ||
trackMap := make(map[string]*broadcast.TrackInfo, 0) | ||
for _, v := range tracks.Data.List { | ||
trackTitle = append(trackTitle, v.Title) | ||
trackMap[v.Title] = v | ||
} | ||
|
||
selector := &survey.Select{ | ||
Message: "Select a track", | ||
Options: trackTitle, | ||
} | ||
|
||
var choose string | ||
if err = survey.AskOne(selector, &choose); err == nil { | ||
fmt.Println("start to play", choose) | ||
trackInfo := trackMap[choose] | ||
play(trackInfo) | ||
} | ||
} | ||
|
||
func play(trackInfo *broadcast.TrackInfo) { | ||
screen, err := tcell.NewScreen() | ||
if err != nil { | ||
panic(err) | ||
} | ||
err = screen.Init() | ||
if err != nil { | ||
panic(err) | ||
} | ||
defer screen.Fini() | ||
|
||
var player *advanced_ui.TrackAudioPanel | ||
if player, err = advanced_ui.NewTrackAudioPanel(trackInfo); err != nil { | ||
panic(err) | ||
} | ||
|
||
//streamer, format, err := mp3.Decode(reader) | ||
//if err != nil { | ||
// panic(err) | ||
//} | ||
//defer streamer.Close() | ||
// | ||
//speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/30)) | ||
|
||
//ap := ui.NewAudioPanel(format.SampleRate, streamer) | ||
|
||
screen.Clear() | ||
player.Draw(screen) | ||
screen.Show() | ||
|
||
_ = player.Start() | ||
|
||
seconds := time.Tick(time.Second) | ||
events := make(chan tcell.Event) | ||
go func() { | ||
for { | ||
events <- screen.PollEvent() | ||
} | ||
}() | ||
|
||
loop: | ||
for { | ||
select { | ||
case event := <-events: | ||
changed, quit := player.Handle(event) | ||
if quit { | ||
player.Stop() | ||
break loop | ||
} | ||
if changed { | ||
screen.Clear() | ||
player.Draw(screen) | ||
screen.Show() | ||
} | ||
case <-seconds: | ||
screen.Clear() | ||
player.Draw(screen) | ||
screen.Show() | ||
} | ||
} | ||
_ = cmd.NewPlayCommand().Execute() | ||
} |
Oops, something went wrong.