diff --git a/assets/js/code-copy.js b/assets/js/code-copy.js index f55f123..c7c8c04 100644 --- a/assets/js/code-copy.js +++ b/assets/js/code-copy.js @@ -6,6 +6,11 @@ document.addEventListener("DOMContentLoaded", function () { codeBlocks.forEach((codeBlock) => { const pre = codeBlock.parentElement; + // Skip if data-lang is "nocopy" + if (codeBlock.dataset.lang === "nocopy") { + return; + } + // Create wrapper for positioning const wrapper = document.createElement("div"); wrapper.style.position = "relative"; diff --git a/content/blog/video-to-gif-converter/files/hotfuzz_1 2.gif b/content/blog/video-to-gif-converter/files/hotfuzz_1 2.gif new file mode 100644 index 0000000..f6c760b Binary files /dev/null and b/content/blog/video-to-gif-converter/files/hotfuzz_1 2.gif differ diff --git a/content/blog/video-to-gif-converter/files/hotfuzz_1 3.gif b/content/blog/video-to-gif-converter/files/hotfuzz_1 3.gif new file mode 100644 index 0000000..9963f3c Binary files /dev/null and b/content/blog/video-to-gif-converter/files/hotfuzz_1 3.gif differ diff --git a/content/blog/video-to-gif-converter/files/hotfuzz_1 4.gif b/content/blog/video-to-gif-converter/files/hotfuzz_1 4.gif new file mode 100644 index 0000000..ec8ff4a Binary files /dev/null and b/content/blog/video-to-gif-converter/files/hotfuzz_1 4.gif differ diff --git a/content/blog/video-to-gif-converter/files/hotfuzz_1.gif b/content/blog/video-to-gif-converter/files/hotfuzz_1.gif new file mode 100644 index 0000000..1f58cc2 Binary files /dev/null and b/content/blog/video-to-gif-converter/files/hotfuzz_1.gif differ diff --git a/content/blog/video-to-gif-converter/files/hotfuzz_1_10fps.gif b/content/blog/video-to-gif-converter/files/hotfuzz_1_10fps.gif new file mode 100644 index 0000000..f975e4e Binary files /dev/null and b/content/blog/video-to-gif-converter/files/hotfuzz_1_10fps.gif differ diff --git a/content/blog/video-to-gif-converter/hotfuzz_1.gif b/content/blog/video-to-gif-converter/hotfuzz_1.gif new file mode 100644 index 0000000..1f58cc2 Binary files /dev/null and b/content/blog/video-to-gif-converter/hotfuzz_1.gif differ diff --git a/content/blog/video-to-gif-converter/hotfuzz_1_10fps.gif b/content/blog/video-to-gif-converter/hotfuzz_1_10fps.gif new file mode 100644 index 0000000..f975e4e Binary files /dev/null and b/content/blog/video-to-gif-converter/hotfuzz_1_10fps.gif differ diff --git a/content/blog/video-to-gif-converter/hotfuzz_1_5fps.gif b/content/blog/video-to-gif-converter/hotfuzz_1_5fps.gif new file mode 100644 index 0000000..9963f3c Binary files /dev/null and b/content/blog/video-to-gif-converter/hotfuzz_1_5fps.gif differ diff --git a/content/blog/video-to-gif-converter/hotfuzz_2.gif b/content/blog/video-to-gif-converter/hotfuzz_2.gif new file mode 100644 index 0000000..f6c760b Binary files /dev/null and b/content/blog/video-to-gif-converter/hotfuzz_2.gif differ diff --git a/content/blog/video-to-gif-converter/hotfuzz_3.gif b/content/blog/video-to-gif-converter/hotfuzz_3.gif new file mode 100644 index 0000000..ec8ff4a Binary files /dev/null and b/content/blog/video-to-gif-converter/hotfuzz_3.gif differ diff --git a/content/blog/video-to-gif-converter/index.md b/content/blog/video-to-gif-converter/index.md new file mode 100644 index 0000000..754d443 --- /dev/null +++ b/content/blog/video-to-gif-converter/index.md @@ -0,0 +1,141 @@ +--- +title: "Snipping a subtitled GIF from a video file" +date: 2026-01-16T10:25:28Z +tags: + - coding + - python +draft: false +--- + +_This post contains large GIF files, apologies._ + +A passing comment resulted in me falling down a bit of a rabbit hole recently. I couldn’t find a GIF of one particular line from 1999’s _The Mummy_. The line is “apparently he had a very good time” and it was perfect for the situation at the time… but it was nowhere to be found. + +Fast forward two days and I had hacked together a script that would accept a legally back up video file and a string, and clip you the GIF. There are, of course, caveats to that… It only works on standard subtitles, not the fancy graphical ones that would require the addition of an OCR to extract[^1] - though most of the other issues I have worked through and got something as close to complete as I’m prepared to make it. + +You can find the source on [GitHub](https://github.com/unbolt/VidToSubGif). + +## How it works + +The script checks for subtitles first as an external file alongside the video, and then by checking the video and extracting them manually if required. + +I had written code that would use OpenSubtitles to try and find any missing subtitles, but it is limited to 5 requests per IP per day, and when the retrieved subtitles are more often than not completely out of sync for the purposes of this project that made them quite useless. In order to use your legally backed up physical media with this project I would advise making sure you extract the original subtitles with your video files - preferably as SRT files. + +If the script has to extract the subtitles, it looks for the first remotely English appearing stream it can. Sorry about that for any other language users, feel free to edit the script to meet your needs, though. + +Next, if we aren’t using the `--include-surrounding-subtitles` flag, we create a temporary copy of the subtitles file with just the line we’re trying to GIF. + +It was around this point I realised I was going to need to tune in the clipping of the GIF. By default it uses the time-frame the subtitle appears in the video, which was fine for some, but often resulted in slightly too short GIFs, or including cuts that felt jarring - so I added in the `--context-before` and `--context-after` variables which allows you to tweak the clip length of the GIF. + +Once we know the section of video we want to output we make a trimmed down copy of the original video source before converting it to a GIF. Originally I wasn’t doing this step and trying to get the GIF right out of the full length video - at one point that resulted in the script using 40gb of RAM and taking a very, very long time…. So this was an obvious optimisation as it’s essentially free to trim the video to a specific length first. + +Finally, the snippet is converted into a GIF using `ffmpeg`. Easy. + +I did also add in the option to loop through the subtitles and extract every instance of a phrase from the video file if you so wish, but this doesn’t play well with the context adjustments so is a little bit pointless. If there is a common phrase you are trying to extract, I would advise looking for the surrounding dialogue and using that to dial in your GIF. + +## Example Usage and Output + +The first example shows what happens if there are no external subtitles already existing, we are able to extract the subs from the video file. + +Note that the context is also adjusted to line up with the scene, and we are including surrounding subtitles. + +#### Script + +```nocopy +$ video-subtitle-gif Hot\ Fuzz\ \(2007\).mkv "shortcut" --context-before 1.2 --context-after 3 --include-surrounding-subtitles --output-prefix hotfuzz +🔍 No external subtitles found. Checking for embedded subtitles... +📺 Found 1 subtitle stream(s): + 📝 Stream 0: index=2, codec=ass, language=eng, title= + ✅ Selected English subtitle stream 0 (codec: ass) +✅ Extracted embedded subtitles to: Hot Fuzz (2007).srt + +✨ Found 1 matching subtitle(s): + 1. [00:30:00.890 - 00:30:02.200]: You never taken a shortcut before.... + +🎬 Generating GIFs... + +🎬 Generating hotfuzz_1.gif... + ⏱️ Time range: 00:29:59.690 - 00:30:05.200 + ✂️ Extracting clip... + 🎨 Converting to GIF... + ✅ Created: hotfuzz_1.gif (4.24 MB) + +🎉 Successfully created 1 GIF(s)! +``` + +#### Result + +
+
+Hot Fuzz, Directed by Edgar Wright (Universal Pictures, 2007), DVD
+
+
+Hot Fuzz, Directed by Edgar Wright (Universal Pictures, 2007), DVD
+
+
+
+Hot Fuzz, Directed by Edgar Wright (Universal Pictures, 2007), DVD
+
+
+Hot Fuzz, Directed by Edgar Wright (Universal Pictures, 2007), DVD
+