Ren'Py How to get the length of a video from the game directory [Solved]

Lou111

Active Member
Jun 2, 2018
543
686
It's been a while... Still love you guys, and thanks in advance!

I need a way to log the length of video clips before they are played on a channel. These videos can vary wildly, as they originate from a mod folder where users can drop anything they want in it so long as it is compatible with RenPy...
The only way I know how to return the length of a clip is by using:
$ renpy.music.get_duration(channel="channel_name")

I have two issues with using that.
Firstly:
Python:
show expression getattr(store, some_clip) as sexy_clip
default timeout = renpy.music.get_duration(channel="CH_clip")
timer timeout action SetVariable("time_counter", timeout), Jump("To_A_Label")
this, for example, will record the length as 0 (zero) seconds.
The only way I can get it to work is to put a short pause after playing the clip, then get the length. Like this:
Python:
show expression getattr(store, some_clip) as sexy_clip
pause 0.3
default timeout = renpy.music.get_duration(channel="CH_clip")
timer timeout action SetVariable("time_counter", timeout), Jump("To_A_Label")
For several reasons, this causes issues for me....

Secondly:
If I wanted to create an instance where playback halts after the sum of some randomly played clips reaches a preset amount of time, I think this is also the best way to do that.


What I'm looking for is something like this:
Python:
init python:
    list_of_videos = []
    length_of_videos = {}
    for f in renpy.list_files():
        if not f.startswith("Clips/"): continue
        n, e = os.path.splitext(os.path.basename(f))
        if not e.lower() in [".webm", ".ogv", ".avi"]: continue
        setattr(store, f, Movie(size=(1280,720), play="{}".format(f), channel="channel_name"))
        list_of_videos.append(f)
        length_of_videos[f] = Some.Shit.That.Works(f)

Of course what I'm looking for is to replace Some.Shit.That.Works with some shit that works...
Then we be like:
Python:
$ sexy_clip = renpy.random.choice(list_of_vidoes)
default timeout = length_of_videos[sexy_clip]
timer timeout action SetVariable("time_counter", timeout), Jump("To_A_Label")

Can anyone help? :unsure::p
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,978
16,234
this, for example, will record the length as 0 (zero) seconds.
It's because renpy.music.get_duration() can only return the duration of something currently playing.
Therefore your second code is the good way to do, what issue did it cause you ?


Secondly:
If I wanted to create an instance where playback halts after the sum of some randomly played clips reaches a preset amount of time, I think this is also the best way to do that.
One heavy way to do could be to have this kind of screen:
Python:
default length_of_videos = {}

screen lengthGetter( videos ):

   #  Currently played video, by default pop one from the list
   default video = videos.pop()

   # Display the video clearly outside of the screen, so the player will see nothing at all.
   add video pos( 3000, 3000)

   # Repeat at short interval to be as fast as possible
   timer 0.1:
       repeat True
       # If you achieve to get a duration...
       action If( renpy.music.get_duration() != 0,
            # Store the duration
            [ SetDict( length_of_videos, video, renpy.music.get_duration() ), 
              # Then if there's still videos to test
              If( len( videos ) > 0, 
                 #  pass to the next video.
                 SetScreenVariable( "video", videos.pop() ) ],
                 # else quit the screen
                 Return() ) ],
            # Else do nothing
            NullAction() )

label splashscreen:
    call screen lengthGetter( list_of_videos[:] )
    # It will be blocking, so add a nice image to the screen ;)
/!\ There's perhaps few typos, and I don't guaranty that the action would accept that mix of indentation ; I used it to help separate the different parts since it's a complex one. /!\

I'm not even totally that it would effectively works ; can you effectively call a screen from "splashscreen" ? It's a label, so it should be possible, but I'm not sure.
Else there's still the possibility to have a show screen in the " " label, and a Hide( "lengthGetter" ) in place of the Return(). The screen being only shown, it wouldn't block Ren'Py and with chance all the videos would be processed before you need their length.

After, there's still the possibility to rely on a third party module that could read and understand the files header. But it would probably itself rely on other third party modules, you would have to , what isn't really complicated but can be a bit difficult if you never done this before. And it would also be blocking, what mean that its place is either in an init block, or in the "splashscreen" label.
I don't know how much time it would need to process all the videos, but the player will have to wait during all this time. So if it's fast is should be good, but if it's slow and there's a lot of videos, it's something else.
 

neon.nul

New Member
Mar 31, 2023
1
2
I recommend using pip to install the moviepy package and then doing this:

Python:
# import
from moviepy.editor import *
 
# load video
clip = VideoFileClip("video.mp4")
 
# get duration
duration = clip.duration
 
# display
print("clip duration : " + str(duration))
 
  • Like
Reactions: osanaiko

Lou111

Active Member
Jun 2, 2018
543
686
You guys are so freakin cool. Thank you both for giving me so much to work with to find the best solution.

neon.nul
As Anne mentioned, I don't have any experience using third-party modules, but hell, it's a good time to give it a shot.

anne O'nymous
I wonder about using the on-load label to place this screen after setting this up initially. Using the screen you suggested is something I can understand and I know I can make it work in the end.

Thank you!!!
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,978
16,234
I wonder about using the on-load label to place this screen after setting this up initially. Using the screen you suggested is something I can understand and I know I can make it work in the end.
Even if it can looks strange as answer, then it's a reason more for you to try the third party module.

As I implied it's more intimidating that difficult, but what will intimidate you here ? You already know that if you fail at this, you've another solution you can rely on. Therefore you'll do it with no pressure, and discover that you can.
 

Lou111

Active Member
Jun 2, 2018
543
686
Yes, thank you! I also meant to answer this question:

Therefore your second code is the good way to do, what issue did it cause you ?
Most of the issues were due to my lack of knowledge. After improving the code to accommodate the pause after playing the video, the remaining issue was that it caused the displayed screens (The UI) to blink. So I guess if there is a way to use a soft pause without hiding screens, that could also be a working solution.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,978
16,234
So I guess if there is a way to use a soft pause without hiding screens, that could also be a working solution.
Hmm, you can force it that way:
Python:
screen myPause( delay ):
    timer delay action Return()

label whatever:
    call screen myPause( 1.5 )
The screen being called, Ren'Py will wait that it return, and like the screen is also totally blank there's no way for it to return before the timer tell it to.

But I'm not sure that it would solve your issue in that particular case.
 

Lou111

Active Member
Jun 2, 2018
543
686
Hmm, you can force it that way:
Python:
screen myPause( delay ):
    timer delay action Return()

label whatever:
    call screen myPause( 1.5 )
The screen being called, Ren'Py will wait that it return, and like the screen is also totally blank there's no way for it to return before the timer tell it to.

But I'm not sure that it would solve your issue in that particular case.
YES!
This will solve everything! Thanks to you we were able to isolate what the real issue was. Thank you for your responses!
 

Lou111

Active Member
Jun 2, 2018
543
686
I recommend using pip to install the moviepy package and then doing this:

Python:
# import
from moviepy.editor import *

# load video
clip = VideoFileClip("video.mp4")

# get duration
duration = clip.duration

# display
print("clip duration : " + str(duration))
I've been trying to install this for hours now I still can't seem to get Ren'Py to recognized that it's installed.
Python:
I'm sorry, but an uncaught exception occurred.

While running game code:
  File "game/initialize.rpy", line 2, in script
    init python:
  File "game/initialize.rpy", line 2, in script
    init python:
  File "game/initialize.rpy", line 4, in <module>
    from moviepy.editor import *
ModuleNotFoundError: No module named 'moviepy'
I used py -m pip install "moviepy" for the command prompt and confirmed that imageio, numpy, and moviepy were installed in my local files.

Do you happen to know how to properly install moviepy for Ren'Py?
I'm using VS Code (which seemed to matter in some of the offered solutions)

Thanks :giggle:
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,978
16,234
I used py -m pip install "moviepy" for the command prompt and confirmed that imageio, numpy, and moviepy were installed in my local files.
What is the issue. It's installed for "Python in your computer", not for "Python used by Ren'Py".
The doc say:
For example, to install the requests package, one can change into the game's base directory, and run the command:
pip install --target game/python-packages requests
Therefore, you need to cd to you project "game" directory, then use pip install --target game/python-packages moviepy.
And now it should works.
 

Lou111

Active Member
Jun 2, 2018
543
686
What is the issue. It's installed for "Python in your computer", not for "Python used by Ren'Py".
The doc say:


Therefore, you need to cd to you project "game" directory, then use pip install --target game/python-packages moviepy.
And now it should works.
Thanks again.
In all my research I never came across this. :cry: I appreciate it.
 

Tribe

Member
Game Developer
May 5, 2021
226
492
When trying to import moviepy I'm getting:
ImportError: attempted relative import with no known parent package

I've reinstalled python
Installed pip
Installed moviepy directly into game/Python-packages' from the command prompt
(along with numpy, imageio & ffmpeg)

I don't know anything about computers, guys. Please help :cry:
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,978
16,234
When trying to import moviepy I'm getting:
ImportError: attempted relative import with no known parent package
This is a Python 3.x error (so I guess you're using Ren'Py 8.x), due to the new way Python handle modules. Normally, importing the module itself, before trying to import a specific file from it, should solve the issue:
Python:
init python:
    import moviespy
    from moviepy.editor import *
 

Tribe

Member
Game Developer
May 5, 2021
226
492
This is a Python 3.x error (so I guess you're using Ren'Py 8.x), due to the new way Python handle modules. Normally, importing the module itself, before trying to import a specific file from it, should solve the issue:
Python:
init python:
    import moviespy
    from moviepy.editor import *
Thank you for the response.
Unfortunately import moviepy gives the same error when typed before from moviepy.editor import *.
I am, in fact, using Ren'Py 8.1.2

My research on this error keeps bringing me to pages telling me about creating .py files and moving around directories.

The command line reads:
C:/Users/[user]/>pip install --target /[os path]/game/python-packages moviepy

All the files show up in the game's directory, so I have to assume that part is working.

I'm importing it just as it is written above.
My computer is plugged in.
I'm at a total loss here.
 

Tribe

Member
Game Developer
May 5, 2021
226
492
It's a bug that's fixed in current .
Alrighty. Thanks for getting me that info.
I've updated Ren'Py and reinstalled everything, now I'm getting:

no module named 'numpy.core._multiarray_umath'

The only information I can find regarding this is 4 years old, telling me to update numpy.
I've reinstalled numpy in the directory and updated it.
I'm using python 3.11.5
Numpy version is 1.25.2
I've uninstalled python and tried it with python 3.9.18
 
Last edited:

gojira667

Member
Sep 9, 2019
325
320
I'm using python 3.11.5
Numpy version is 1.25.2
I've uninstalled python and tried it with python 3.9.18
Numpy uses which need to match the Python version used to install it.
For latest Ren'Py it's specifically Python 3.9.10 though any 3.9.x likely works.

With numpy sorted it runs into:
Code:
ModuleNotFoundError: No module named 'pkg_resources
Which is setuptools. But then it errors for me with:
You don't have permission to view the spoiler content. Log in or register now.
That's above my Python level. I know python3 is stricter with , but I'm not sure that's even the issue here.
 
  • Like
Reactions: Tribe

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,978
16,234
Numpy uses which need to match the Python version used to install it.
What can be a problem here since he seem to use the Python installed on his computer to install it, and not the one embedded in Ren'Py.


TypeError: can't concat str to bytes
With Python 3, strings can be either a literal string (str) or a byte array (bytes). Strictly speaking for a human they are the same, but since their behavior do not strictly match, Python will refuse to concatenate one with the other.
Therefore, there's somewhere a x = "blabla", coupled to a y = b"blabla", followed by a z = x + y. It's in the last line that the error is triggered, and since it's a nightly version (that I remind to the readers is an ALPHA version), I guess it will be fixed relatively quickly.
 
  • Like
Reactions: Tribe and gojira667

gojira667

Member
Sep 9, 2019
325
320
Therefore, there's somewhere a x = "blabla", coupled to a y = b"blabla", followed by a z = x + y. It's in the last line that the error is triggered, and since it's a nightly version (that I remind to the readers is an ALPHA version), I guess it will be fixed relatively quickly.
It's from the fix branch, but still definitely not stable. I wasn't sure if the byte array was coming from, *looks at traceback*, pkg_resources? But I suppose Ren'Py should handle that anyway? Tribe, I don't see any about this...

Import errors aside, moviepy seems like a heavy solution just to get the video duration. Think I would look at if I needed it for a project. You need to supply the assorted binaries as well ( ) or complain if it's not installed system-wide. Even then this is simpler than moviepy case with it's multiple binaries.
 
  • Star-struck
Reactions: Tribe