Video Fast Forward for Renpy

A place to discuss things that aren't specific to any one creator or game.
Forum rules
Ren'Py specific questions should be posted in the Ren'Py Questions and Annoucements forum, not here.
Post Reply
Message
Author
MrAlgorithm
Newbie
Posts: 1
Joined: Tue May 24, 2022 1:14 pm
Contact:

Video Fast Forward for Renpy

#1 Post by MrAlgorithm »

Problem
I've struggled a lot with Renpy's lack of ability to navigate videos. When you click on the video, the game skips the entirety of it. This combined with my absolute lack of patience, caused me to skip most videos in Renpy based games and miss a lot of good parts.


Solution
It was really a huge hurdle to go through since there is no proper documentation and I wasn't able to find anyone providing something useful for this specific problem. But, I managed to develop a makeshift tool in python for implementing video fast forward (it is more like a jump forward rather than a fast forward) ability to renpy games. This method currently requires the game to be using "renpy.movie_cutscene()" for playing the videos. But if there are differences and you are familiar with python, you should be able to adjust it.


How to Use
Keep in mind that this tool is not optimized & prettified in terms of code and since it is a python script that you have to run the generate some files, it is not very end user friendly.


I've attached the scripts as a zip file. I've also created an example project that can be downloaded via these links:
ANONFILE-GOFILE-PIXELDRAIN


Overview
First script is called "get_durations.py" to be used for obtaining video duration file for the ".webm" videos in a folder. Code can be easily modified for other video extensions. It requires moviepy library which can be installed via "pip install moviepy". If you don't prefer to deal with the library requirements and if you have small number of videos, you can check "mdurations.txt" and manually create one for yourself in a similar format.

Second script is called "generate_code.py", this script uses the duration file generated by the previous script to generate a file called "moviepart.rpy". This is the file that allows the game to play the videos in parts and allows the player to skip forward. One large benefit of this method is the file being completely non-disruptive to the main game code. You can just delete the "moviepart.rpy" and your game will return back to normal. The portions that you would like to divide the video can be easily modified in the script by changing "divider" variable.

I divided it into two scripts since first script tends to take much longer compared to the second script. It was much easier to run first one only once then experiment with the second one using the file generated. I've tested the tool in a few games and it works well. But, there might still be bugs.


Steps
Move all webm videos into "videos" folder under "game" folder.
Move the "get_durations.py" into the folder you have videos and run it.
Move the generated "mdurations.txt" file to "game" folder.
Move the "generate_codes.py" into the "game" folder and run it.
Enjoy the fast/jump forward functionality for videos in your game!



Big Disclaimer: At the time I developed this tool, I personally wasn't able to find any proper solutions for this in the internet. It is likely that there will be or maybe already are alternative and/or better solutions out there that I'm not aware of. This post is just for desire to share the knowledge/experience and maybe make a few peoples work easier at the same time. If I made your work easier or your game more enjoyable, please feel free to buy me a coffee.



Technical Babblings for Anyone Interested
I've experimented with several methods. Other than obvious lack of functionality in renpy for video navigation, some of the biggest problems that I've faced:
  • Renpy doesn't allow you to dynamically define images within renpy game runtime. Therefore you can't get very creative with this method, unfortunately.
  • Renpy loads the videos in different times depending on the system speed and video resolution. If your method of approach is to divide the video into small parts and play one after another, this problem messes up your approach so bad. If you don't consider the load time, flickers happen between the parts. Solving this by trying to load one video just before hiding the previous one also messes up sound and causes video stutter since you can't perfectly predict the switch time.
Is my solution perfect? Ofc not. Script code is obviously a mess (I don't have much free time for this). There are some points that I observed might be problematic in some rare cases. There are also more functionality/QoL that can be quickly added if I had more free time. I probably won't spend any more time on this but feel free to share any ideas and to use the methods in the tool or modify the tool for your purposes.
Attachments
vff_tool.zip
(1.29 KiB) Downloaded 39 times

User avatar
Andredron
Miko-Class Veteran
Posts: 700
Joined: Thu Dec 28, 2017 2:37 pm
Location: Russia
Contact:

Re: Video Fast Forward for Renpy

#2 Post by Andredron »

To make the code visible, I've published it from the archive.

get_durations.py

Code: Select all

from moviepy.editor import VideoFileClip
import os

durfile = open("mdurations.txt","w")

for f in os.listdir(os.getcwd()):
    if "webm" in f:
        clip = VideoFileClip(f)
        durfile.write(f + ":" + str(clip.duration)+"\n")

durfile.close()

generate_codes.py

Code: Select all

durfile = open("mdurations.txt")
durs = durfile.readlines()
durfile.close()

movieinit = """init python:
    import time
    def csreplace(filename):
        mvlabel = filename.split(".")[0] + "show"
        renpy.call(mvlabel)

    renpy.movie_cutscene = csreplace

"""

fmovie = open("moviepart.rpy","w")
fmovie.write(movieinit)


for f in durs:
    moviefile = f.split(':')[0]
    duration = float(f.split(':')[1])
    divider = 5
    loadtime = 0.35


    imagelines = []
    showlines = []

    tag = moviefile.split('.')[0]
    i = 0
    for i in range(int(duration/divider)):
        imageline = "image "+ tag + str(i+1) + "=Movie(play=\"<from " + str(i*divider) + " to " + str(duration) + ">videos/" + moviefile + "\", loop=False, channel=u\"movsound\")"
        imagelines.append(imageline)

    if duration>=5:
        imageline = "image "+ tag + str(i+2) + "=Movie(play=\"<from " + str((i+1)*divider) + " to " + str(duration) + ">videos/" + moviefile + "\", loop=False, channel=u\"movsound\")"      
    else:
        imageline = "image "+ tag + str(i+1) + "=Movie(play=\"<from " + str((i)*divider) + " to " + str(duration) + ">videos/" + moviefile + "\", loop=False, channel=u\"movsound\")"      
    imagelines.append(imageline)


    if duration>=5:
        showline = "label " + tag + "show():\n    $" + tag +"dur = " + str(duration)+ "\n    $stime = time.clock()\n    $passtime = 0\n    window hide\n    show " + tag + "1\n    pause " + str(duration) +"\n    $passtime += time.clock()-stime + " + str(divider) +"\n    $stime = time.clock()\n    $jtime = min(int(" + tag + "dur-" + tag + "dur%" + str(divider) + "+" + str(divider) + "),int(passtime-passtime%" + str(divider) +"))\n    $camefrom = \"1\"\n    $renpy.jump(\"" + tag +"cur\" + str(jtime))"
        showlines.append(showline)
    else:
        showline = "label " + tag + "show():\n    window hide\n    show " + tag + str(1)
        showlines.append(showline)
        
    showline = ""
    i = 0
    for i in range(int(duration/divider)):
        showline = "    label " + tag +"cur" + str((i+1)*divider) + ":\n        show " + tag + str(i+2) + "\n        pause " + str(loadtime) + "\n        $renpy.hide(\""+ tag +"\"+camefrom)"
        if i!=int(duration/divider)-1:
            showline += "\n        pause " + str(duration - (i+1)*divider-loadtime) + "\n    $passtime += time.clock()-stime + " + str(divider) +"\n    $stime = time.clock()\n    $jtime = min(int(" + tag + "dur-" + tag + "dur%" + str(divider) + "+" + str(divider) + "),int(passtime-passtime%" + str(divider) +"))\n    $camefrom = \"" + str(i+2) +"\"\n    $renpy.jump(\"" + tag +"cur\" + str(jtime))"
            showlines.append(showline)
    if duration>=5:
        showline += "\n        pause "+ str(max(duration%divider-loadtime,0)) +"\n    hide " + tag + str(i+2) + "\n    label " + tag +"cur"+ str(int(duration-duration%divider+divider)) + ":\n    $renpy.hide(\""+ tag +"\"+camefrom)\n    window show\n    return"
    else:
        showline += "    pause "+ str(duration) +"\n    hide " + tag + str(i+1) + "\n    window show\n    return"
    showlines.append(showline)

    for iline in imagelines:
        fmovie.write(iline)
        fmovie.write("\n")


    fmovie.write("\n")

    for sline in showlines:
        fmovie.write(sline)
        fmovie.write("\n")

    fmovie.write("\n\n")

fmovie.close()


Post Reply

Who is online

Users browsing this forum: No registered users