diff --git a/pkg/api/webPlayer.go b/pkg/api/webPlayer.go index 13baa8b..32d3f28 100644 --- a/pkg/api/webPlayer.go +++ b/pkg/api/webPlayer.go @@ -32,6 +32,7 @@ func (wb *WebPlayer) Play(url string, start int) error { wb.running = false return nil } + func (wb *WebPlayer) Progress() media.MediaDuration { return wb.progress } diff --git a/pkg/api/websocket.go b/pkg/api/websocket.go index ac69f5c..755a75f 100644 --- a/pkg/api/websocket.go +++ b/pkg/api/websocket.go @@ -40,6 +40,7 @@ func (c *Client) Read() { _, msg, err := c.socket.ReadMessage() if err != nil { fmt.Printf("ERROR decoding %v", err) + c.contoller.RemoveListner(c.id) return } var event controllers.Event diff --git a/pkg/controllers/controller.go b/pkg/controllers/controller.go index a7e10dc..78ae422 100644 --- a/pkg/controllers/controller.go +++ b/pkg/controllers/controller.go @@ -136,8 +136,10 @@ func (c *Controller) Update(state PlayerState, user string) { } func (c *Controller) Join(player Player, user string) { - c.players.Add(player) - c.Notify(PLAYER_ACTION, user) + if _, ok := c.players.players[player.Type()]; !ok { + c.players.Add(player) + c.Notify(PLAYER_ACTION, user) + } } func (c *Controller) Leave(pType PlayerType, user string) { diff --git a/pkg/discord/players/discord.go b/pkg/discord/players/discord.go index 4dc0eab..1d4c829 100644 --- a/pkg/discord/players/discord.go +++ b/pkg/discord/players/discord.go @@ -139,12 +139,7 @@ func (player *DiscordPlayer) Play(url string, startTime int) error { opts.Application = "audio" opts.PacketLoss = 10 player.startTime = startTime - if err := player.ParseDuration(url); err != nil { - player.progress = media.MediaDuration{ - Duration: 0, - Progress: 0, - } - } + player.ParseDuration(url) encodeSession, err := dca.EncodeFile(url, opts) if err != nil { return fmt.Errorf("failed creating an encoding session: %v", err) diff --git a/pkg/media/RadioGarden.go b/pkg/media/RadioGarden.go index 49663b9..47396f8 100644 --- a/pkg/media/RadioGarden.go +++ b/pkg/media/RadioGarden.go @@ -1,6 +1,7 @@ package media import ( + "crypto/tls" "encoding/json" "fmt" "log" @@ -39,7 +40,10 @@ type RadioGarden struct { } func init() { - RadioGardenClient := &RadioGarden{client: &http.Client{Timeout: 10 * time.Second}} + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + RadioGardenClient := &RadioGarden{client: &http.Client{Timeout: 10 * time.Second, Transport: tr}} MediaFactory.Register(RadioGardenClient) } @@ -55,7 +59,13 @@ func (radio *RadioGarden) getRadioInfo(id string) (RadioGardenInfo, error) { } func (radio *RadioGarden) getAudioURL(id string) string { - return fmt.Sprintf("http://radio.garden/api/ara/content/listen/%s/channel.mp3", id) + resp, err := radio.client.Get(fmt.Sprintf("http://radio.garden/api/ara/content/listen/%s/channel.mp3", id)) + if err != nil { + return fmt.Sprintf("http://radio.garden/api/ara/content/listen/%s/channel.mp3", id) + } + // Your magic function. The Request in the Response is the last URL the + // client tried to access. + return resp.Request.URL.String() } func (radio *RadioGarden) getIDFromURL(targetUrl string) string { @@ -80,7 +90,7 @@ func (radio *RadioGarden) GetMedia(url string, username string) ([]Media, error) Type: VIDEO_TYPE_RG, Title: info.Data.Title, Progress: MediaDuration{ - Duration: 0, + Duration: -1, }, Thumbnail: "https://play-lh.googleusercontent.com/07lewhVI4GklVBi_ehhOXxmB_bPaWWTiyqHAlQP6VsYD7h9R4d8hskNAy4SCOx0leNx-=s180", AudioUrl: audioUrl, diff --git a/pkg/media/Youtube.go b/pkg/media/Youtube.go index 4e6e31c..a5d9b0c 100644 --- a/pkg/media/Youtube.go +++ b/pkg/media/Youtube.go @@ -146,22 +146,15 @@ func (yt *Youtube) GetMedia(url string, username string) ([]Media, error) { if err == nil { video := Media{ - ID: ksuid.New().String(), - Url: url, - User: username, - Type: isLive(ytVideo), - Title: ytVideo.Title, - Progress: MediaDuration{ - Duration: ytVideo.Duration, - }, + ID: ksuid.New().String(), + Url: url, + User: username, + Type: isLive(ytVideo), + Title: ytVideo.Title, + Progress: getDuration(ytVideo), Thumbnail: ytVideo.Thumbnails[0].URL, ChannelName: ytVideo.Author, } - // audio, err := yt.GetAudioUrl(url) - // if err != nil { - // return []Media{}, err - // } - // video.AudioUrl = audio return []Media{video}, nil } return []Media{}, fmt.Errorf("Unable find valid audio for this url, %v", err) @@ -189,3 +182,14 @@ func isLive(yt *youtube.Video) MediaType { } return VIDEO_TYPE_YT } + +func getDuration(ytVideo *youtube.Video) MediaDuration { + if len(ytVideo.HLSManifestURL) > 1 { + return MediaDuration{ + Duration: -1, + } + } + return MediaDuration{ + Duration: ytVideo.Duration, + } +} diff --git a/ui/package-lock.json b/ui/package-lock.json index 010ef82..e206e70 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -11,6 +11,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-hot-toast": "^2.4.1", + "react-hotkeys-hook": "^4.4.3", "react-player": "2.13.0", "react-router-dom": "^6.21.0", "react-swipeable": "^7.0.1" @@ -5708,6 +5709,15 @@ "react-dom": ">=16" } }, + "node_modules/react-hotkeys-hook": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-4.4.3.tgz", + "integrity": "sha512-G6psp7OUm9xxY4G2vL48tBwWUVJLvD/PeInaPdPvqRJ8GoXBu6Djqr6WIw5gu1M0SbR1epNUlvpccxu2ZzmtFQ==", + "peerDependencies": { + "react": ">=16.8.1", + "react-dom": ">=16.8.1" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/ui/package.json b/ui/package.json index 61d933c..5ec080d 100644 --- a/ui/package.json +++ b/ui/package.json @@ -13,6 +13,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-hot-toast": "^2.4.1", + "react-hotkeys-hook": "^4.4.3", "react-player": "2.13.0", "react-router-dom": "^6.21.0", "react-swipeable": "^7.0.1" diff --git a/ui/src/pages/app/components/header.jsx b/ui/src/pages/app/components/header.jsx index 2877010..72715e2 100644 --- a/ui/src/pages/app/components/header.jsx +++ b/ui/src/pages/app/components/header.jsx @@ -14,7 +14,7 @@ export const Header = ({ state }) => { export const VideoHeader = ({ state, connection }) => { return ( -
+
diff --git a/ui/src/pages/app/components/player.jsx b/ui/src/pages/app/components/player.jsx index 63bddba..2f2be8c 100644 --- a/ui/src/pages/app/components/player.jsx +++ b/ui/src/pages/app/components/player.jsx @@ -111,6 +111,9 @@ const Player = ({ state }) => { return iso.substring(11, iso.length - 5); } const playerProgress = (current, total) => { + if (total === -1){ + return 100 + } let pct = current / total * 100 return Math.min(Math.max(pct, 0), 100) } @@ -140,7 +143,6 @@ const Player = ({ state }) => {
-
- {state.status === "PLAY" ? } -
- {state.current.id &&
- {formatTime(state.current.time.progress)} -
-
+ {state.current.id && +
+ {formatTime(state.current.time.progress)} +
+
+
+ { + state.current.time.duration > -1 ? + {formatTime(state.current.time.duration)} + : +
+ live +
+ }
- {formatTime(state.current.time.duration)} -
} + }
: diff --git a/ui/src/pages/app/components/videoPlayer.jsx b/ui/src/pages/app/components/videoPlayer.jsx index c22790f..c89d269 100644 --- a/ui/src/pages/app/components/videoPlayer.jsx +++ b/ui/src/pages/app/components/videoPlayer.jsx @@ -2,6 +2,7 @@ import React, { useContext, useEffect } from 'react'; import ReactPlayer from 'react-player' import { getRoomId, playVideoController, skipVideoController } from '../watch2gether'; import { VolumeContext } from './providers'; +import waveImge from "./wave-signal.svg" export const VideoPlayer = ({state, connection}) => { const playerRef = React.useRef(null); @@ -33,7 +34,6 @@ export const VideoPlayer = ({state, connection}) => { Current: s.current } } - console.log("SENDING", JSON.stringify(evt)) connection.send(JSON.stringify(evt)) }; @@ -42,7 +42,7 @@ export const VideoPlayer = ({state, connection}) => {
{ onEnded={onEnded} onProgress={handleProgress} playing={state.status === "PLAY" } + style={{ + backgroundImage:`url(${waveImge})`, + backgroundPosition: "center", + backgroundSize: "100% 50%", + backgroundRepeat: "no-repeat" + }} loop={state.loop} />
diff --git a/ui/src/pages/app/components/wave-signal.svg b/ui/src/pages/app/components/wave-signal.svg new file mode 100644 index 0000000..c282be4 --- /dev/null +++ b/ui/src/pages/app/components/wave-signal.svg @@ -0,0 +1,23 @@ + + + + + Wave-signal-2 + Created with Sketch. + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ui/src/pages/app/controller.jsx b/ui/src/pages/app/controller.jsx index 043638d..aa4e9f1 100644 --- a/ui/src/pages/app/controller.jsx +++ b/ui/src/pages/app/controller.jsx @@ -5,11 +5,10 @@ import Player from "./components/player"; import { addVideoController, getChannelPlaylists, getSocket, updateQueueController, getController, createController } from "./watch2gether"; import { Header, VideoHeader } from "./components/header"; import { useNavigate } from "react-router-dom"; -import { NotificationMessages } from "./components/notifications"; import { PlaylistBtn } from "./playlist"; import { PlayerContext } from "./components/providers"; import { Loading } from "./components/loading"; -const debug = true +import { useHotkeys } from 'react-hotkeys-hook' export const AddVideoCtrl = ({ onAddVideo }) => { const [video, setVideo] = useState(""); @@ -45,7 +44,11 @@ export const AppController = () => { const [loading, setLoading] = useState(true) const [playlists, setPlaylists] = useState([]) const [notificationURL, setNotificationURL] = useState(null) + const [debug, setDebug] = useState(false) + const { showVideo } = useContext(PlayerContext) + + useHotkeys('ctrl+shift+b', () => setDebug(!debug), [debug]) const updatePlaylists = async () => { try { @@ -151,10 +154,10 @@ export const AppController = () => { - {debug &&
-
+            {debug && 
+
                     
-                        `{JSON.stringify(state, null, 2)}`
+                        {JSON.stringify(state, null, 2)}
                     
                 
}