]> git.localhorst.tv Git - ffmpeg-test.git/commitdiff
add clip player
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Sat, 9 Nov 2024 14:26:39 +0000 (15:26 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Sat, 9 Nov 2024 14:26:39 +0000 (15:26 +0100)
src/app/Application.h
src/app/ClipPlayer.cpp [new file with mode: 0644]
src/app/ClipPlayer.h [new file with mode: 0644]
src/app/Media.h
src/app/Renderer.h
src/app/Shoutout.h
src/app/State.h

index b888ba52c65610467269617c9b9317e2e3e20a18..1d7d6b6e448ea1268343684b9a2370ccb0f8d50d 100644 (file)
@@ -6,6 +6,7 @@
 #include <json/value.h>
 
 #include "ChannelInfo.h"
+#include "ClipPlayer.h"
 #include "Config.h"
 #include "DrawingGame.h"
 #include "Mixer.h"
@@ -128,7 +129,6 @@ private:
                        ChannelInfo &info = state.GetChannelInfo(channel_id);
                        info.Update(channel);
                }
-               ShoutoutChannel(33);
        }
 
        void HandlePusherChannel(const Json::Value &json) {
@@ -153,7 +153,7 @@ private:
                if (went_live) {
                        // channel went live
                        std::cout << "channel " << channel.title << " went live" << std::endl;
-                       if (channel.chat) {
+                       if (channel.chat && channel.twitch_id != config.own_channel_id) {
                                ShoutoutChannel(channel_id);
                        }
                } else if (went_down) {
@@ -165,7 +165,7 @@ private:
        void ShoutoutChannel(int id) {
                ChannelInfo &channel = state.GetChannelInfo(id);
                Shoutout &shout = renderer.CreateShoutout(id, state);
-               if (!channel.twitch_id.empty() && channel.twitch_id != config.own_channel_id) {
+               if (!channel.twitch_id.empty()) {
                        if (config.shoutouts) {
                                twitch_conn.Shoutout(config.own_channel_id, channel.twitch_id);
                        }
@@ -204,6 +204,24 @@ private:
                        const ChannelInfo *channel = state.FindChannelInfo(name);
                        if (channel) {
                                ShoutoutChannel(channel->id);
+                       } else {
+                               SendIRCText("Channel nicht gefunden PoroSad");
+                       }
+               } else if (msg.StartsWith("!clip")) {
+                       const ChannelInfo *channel = nullptr;
+                       if (msg.GetText().length() > 6 && msg.GetText()[6] == '@') {
+                               std::string name = msg.GetText().substr(7);
+                               channel = state.FindChannelInfo(name);
+                       } else if (msg.GetText().length() > 5) {
+                               std::string name = msg.GetText().substr(6);
+                               channel = state.FindChannelInfo(name);
+                       } else {
+                               channel = state.GetRandomChannel();
+                       }
+                       if (channel) {
+                               EnqueueClip(channel->id);
+                       } else {
+                               SendIRCText("Channel nicht gefunden NotLikeThis");
                        }
                }
                if (state.HasGame()) {
@@ -211,6 +229,21 @@ private:
                }
        }
 
+       void EnqueueClip(int id) {
+               ChannelInfo &channel = state.GetChannelInfo(id);
+               ClipPlayer &player = renderer.CreateClip(id, state);
+               player.FetchClip(twitch_conn);
+       }
+
+       void SendIRCText(const std::string &text) {
+               twitch::IRCMessage msg;
+               msg.command = "PRIVMSG";
+               msg.params.push_back(config.chat_channel);
+               msg.params.push_back(text);
+               twitch_conn.SendMessage(msg);
+       }
+
+
 private:
        const Config &config;
        ffmpeg::Network net;
diff --git a/src/app/ClipPlayer.cpp b/src/app/ClipPlayer.cpp
new file mode 100644 (file)
index 0000000..988207a
--- /dev/null
@@ -0,0 +1,38 @@
+#include "ClipPlayer.h"
+
+#include <random>
+
+#include "Media.h"
+#include "State.h"
+
+namespace app {
+
+void ClipPlayer::LoadRandomClip(const Json::Value &json) {
+       const Json::Value &data = json["data"];
+       if (!data.isArray()) return;
+       if (data.empty()) return;
+       Json::ArrayIndex num = data.size();
+       std::uniform_int_distribution<std::mt19937::result_type> dist(0, num - 1);
+       Json::ArrayIndex choice = dist(state.GetRNG());
+       const Json::Value &clip_json = data[choice];
+       clip = twitch::Clip(clip_json);
+}
+
+void ClipPlayer::Start(const Clock &clock) {
+       start_time = clock;
+       running = true;
+       if (clip.HasVideo()) {
+               std::cout << "adding clip " << clip.GetVideoURL() << " at " << clock << std::endl;
+               Media &media = state.AddMedia(clip.GetVideoURL().c_str());
+               media.SetSyncPoint(clock);
+               media.AddWindow({ 0, 0, 1, 1 }, { 320, 150, 640, 360 });
+               media.OnComplete([this](Media *) -> void {
+                       done = true;
+               });
+       } else {
+               std::cout << "clip has no video (" << clip.GetVideoURL() << ")" << std::endl;
+               done = true;
+       }
+}
+
+}
diff --git a/src/app/ClipPlayer.h b/src/app/ClipPlayer.h
new file mode 100644 (file)
index 0000000..e201651
--- /dev/null
@@ -0,0 +1,76 @@
+#ifndef TEST_APP_CLIPPLAYER_H_
+#define TEST_APP_CLIPPLAYER_H_
+
+#include "ChannelInfo.h"
+#include "Clock.h"
+#include "../cairo/Context.h"
+#include "../twitch/Clip.h"
+#include "../ws/TwitchConnection.h"
+
+namespace app {
+
+class State;
+
+class ClipPlayer {
+
+public:
+       explicit ClipPlayer(const ChannelInfo &channel, cairo::Context &ctx, State &state)
+       : channel(channel)
+       , state(state)
+       , start_time()
+       , loading(false)
+       , running(false)
+       , done(false) {
+       }
+
+public:
+       bool Loading() const {
+               return loading;
+       }
+
+       bool Running() const {
+               return running;
+       }
+
+       bool Done() const {
+               return done;
+       }
+
+       void FetchClip(ws::TwitchConnection &twitch) {
+               loading = true;
+               twitch.FetchClips(channel.twitch_id)
+                       .Then([this](const Json::Value *rsp) -> void {
+                               loading = false;
+                               LoadRandomClip(*rsp);
+                       })
+                       .Catch([this](ws::HttpsConnection *rsp) -> void {
+                               loading = false;
+                               done = true;
+                               std::cout << "failed to fetch clips" << std::endl;
+                               std::cout << rsp->GetBody() << std::endl;
+                       });
+       }
+
+       void LoadRandomClip(const Json::Value &json);
+
+       void Start(const Clock &clock);
+
+       void Update(cairo::Context &ctx, const Clock &clock) {
+       }
+
+private:
+       const ChannelInfo &channel;
+       State &state;
+
+       twitch::Clip clip;
+
+       Clock start_time;
+       bool loading;
+       bool running;
+       bool done;
+
+};
+
+}
+
+#endif
index 7ecec173480b35f49ed263150e20020aecfcb3f2..50e54a08da827b2f651ec05bb433ae6a0e520ef4 100644 (file)
@@ -9,11 +9,15 @@
 #include "Window.h"
 #include "../cairo/Context.h"
 #include "../gfx/Rectangle.h"
+#include "../sys/Callbacks.h"
 
 namespace app {
 
 class Media {
 
+public:
+       typedef sys::Callbacks<Media *> CallbacksType;
+
 public:
        explicit Media(const char *url)
        : source(url)
@@ -61,11 +65,20 @@ public:
                return source.IsEOF();
        }
 
+       void OnComplete(CallbacksType::Callback cb) {
+               complete_callbacks.Listen(cb);
+       }
+
+       void HandleComplete() {
+               complete_callbacks.FireNothrow(this);
+       }
+
 private:
        Source source;
        cairo::Surface surface;
        Clock sync_point;
        std::vector<Window> windows;
+       CallbacksType complete_callbacks;
 
 };
 
index a6329096a88bce39c688dbe85b264e8c6fbe353e..bdc6d4c801d272b2dfa83ecb1fecaef19d969ee9 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef TEST_APP_RENDERER_H_
 #define TEST_APP_RENDERER_H_
 
+#include "ClipPlayer.h"
 #include "Shoutout.h"
 #include <cstdint>
 
@@ -57,6 +58,11 @@ public:
                surface.Flush();
        }
 
+       ClipPlayer &CreateClip(int channel_id, State &state) {
+               ClipPlayer &player = state.AddClip(channel_id, ctx);
+               return player;
+       }
+
        Message &CreateMessage(State &state) {
                Message &msg = state.AddMessage(ctx);
                msg.SetTextFont(text_font);
index 163778b98931a09c169a06a835d03e9f216ce9ce..0bd36947b15a392b44ab8d018f8f000fe8d550a9 100644 (file)
@@ -9,7 +9,6 @@
 #include "../gfx/Spacing.h"
 #include "../twitch/Clip.h"
 #include "../ws/TwitchConnection.h"
-#include "json/forwards.h"
 #include "json/value.h"
 #include <cstdint>
 
index a072662d5e824015654809fc66415e456b73cf93..fb24c2e05f3b698a657630b06e62408f43f584a5 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef TEST_APP_STATE_H_
 #define TEST_APP_STATE_H_
 
+#include <iterator>
 #include <list>
 #include <map>
 #include <ostream>
@@ -8,6 +9,7 @@
 #include <unicode/unistr.h>
 
 #include "ChannelInfo.h"
+#include "ClipPlayer.h"
 #include "Clock.h"
 #include "Game.h"
 #include "Media.h"
@@ -73,6 +75,17 @@ public:
                return nullptr;
        }
 
+       ChannelInfo *GetRandomChannel() {
+               if (channels.empty()) {
+                       return nullptr;
+               }
+               std::uniform_int_distribution<std::mt19937::result_type> dist(0, channels.size() - 1);
+               size_t n = dist(GetRNG());
+               auto iter = channels.begin();
+               std::advance(iter, n);
+               return &iter->second;
+       }
+
        const std::list<Media> &GetMedia() const {
                return media;
        }
@@ -85,7 +98,15 @@ public:
                return shoutouts;
        }
 
+       ClipPlayer &AddClip(int channel_id, cairo::Context &ctx) {
+               const ChannelInfo &info = GetChannelInfo(channel_id);
+               std::cout << "adding clip from channel " << info.title << std::endl;
+               clips.emplace_back(info, ctx, *this);
+               return clips.back();
+       }
+
        Media &AddMedia(const char *url) {
+               std::cout << "adding media " << url << std::endl;
                media.emplace_back(url);
                return media.back();
        }
@@ -96,7 +117,9 @@ public:
        }
 
        Shoutout &AddShoutout(int channel_id, cairo::Context &ctx) {
-               shoutouts.emplace_back(GetChannelInfo(channel_id), ctx, *this);
+               const ChannelInfo &info = GetChannelInfo(channel_id);
+               std::cout << "adding shoutout for channel " << info.title << std::endl;
+               shoutouts.emplace_back(info, ctx, *this);
                return shoutouts.back();
        }
 
@@ -136,6 +159,15 @@ public:
                if (HasGame()) {
                        GetGame().Update(ctx, clock);
                }
+               if (!clips.empty()) {
+                       if (clips.front().Done()) {
+                               clips.pop_front();
+                       } else if (clips.front().Running()) {
+                               clips.front().Update(ctx, clock);
+                       } else if (!clips.front().Loading()) {
+                               clips.front().Start(clock);
+                       }
+               }
                if (!shoutouts.empty()) {
                        if (shoutouts.front().Done()) {
                                shoutouts.pop_front();
@@ -151,6 +183,7 @@ public:
                for (auto m = media.begin(); m != media.end();) {
                        if (m->IsEOF()) {
                                std::cout << "removing EOF media" << std::endl;
+                               m->HandleComplete();
                                m = media.erase(m);
                        } else {
                                ++m;
@@ -169,6 +202,7 @@ private:
 
        std::map<int, ChannelInfo> channels;
 
+       std::list<ClipPlayer> clips;
        std::list<Media> media;
        std::list<Message> msgs;
        std::list<Shoutout> shoutouts;