2bc4 / streamlink-ttvlol

Streamlink Twitch plugin modified to work with the TTV.LOL API
BSD 2-Clause "Simplified" License
158 stars 6 forks source link

Saving stream with `--twitch-reexec-on-ad` overwrites/tries to overwrite initial portion #33

Closed sushilicious closed 1 year ago

sushilicious commented 1 year ago

When running streamlink with -o or -r arguments and --twitch-reexec-on-ad, encountering an ad segment will try to overwrite the initial saved portion (since it's just calling the same streamlink command with the same output file argument).

This is a simple hack which appends a counter to the output file to avoid the problem:

diff --git a/twitch.py b/twitch.py
index 82ff3a4..e05522e 100644
--- a/twitch.py
+++ b/twitch.py
@@ -177,16 +177,30 @@ class TwitchHLSStreamWriter(HLSStreamWriter):
     reader: "TwitchHLSStreamReader"
     stream: "TwitchHLSStream"

+    def __init__(self, *args, **kwargs) -> None:
+        self._reexec_counter = 0
+        super().__init__(*args, **kwargs)
+
     def should_filter_sequence(self, sequence: TwitchSequence):  # type: ignore[override]
         if self.stream.reexec_on_ad and sequence.segment.ad:
             log.info("Encountered an ad segment, re-execing to retrieve a new playlist")
+            self._reexec_counter += 1
+
+            args = sys.argv[:]
+            for output_flag in ("-o", "--output", "-r" ,"--record", "-R", "--record-and-pipe"):
+                if output_flag in args:
+                    flag_idx = args.index(output_flag)
+                    if flag_idx + 1 < len(args):
+                        stem, ext = os.path.splitext(args[flag_idx + 1])
+                        args[flag_idx + 1] = f"{stem}-{self._reexec_counter}{ext}"
+
             if sys.platform == "win32":
                 # Python Win32 bug https://bugs.python.org/issue436259

                 from subprocess import list2cmdline
-                os.execl(sys.executable, list2cmdline([sys.executable] + sys.argv))
+                os.execl(sys.executable, list2cmdline([sys.executable] + args))
             else:
-                os.execv(sys.argv[0], sys.argv)
+                os.execv(sys.argv[0], args)

         return self.stream.disable_ads and sequence.segment.ad

Also, thanks for this fantastic plugin.

2bc4 commented 1 year ago

Nice solution! Would you be willing to make a PR so I can give you credit?

sushilicious commented 1 year ago

As @2bc4 pointed out, the above patch doesn't work when reexecuting more than once.

Instead, it is recommended that you add a timestamp to the end of your output file using streamlink's meta variables to ensure you don't overwrite the previous output segements. For example (untested):

-o '{author} - {title} - {time:%Y-%m-%dT%H%M%S%z}.ts'