scanny / python-pptx

Create Open XML PowerPoint documents in Python
MIT License
2.47k stars 530 forks source link

shapes.add_movie doesn't allow video to automatically start playing #427

Open Lileso opened 6 years ago

Lileso commented 6 years ago

And if possible there is a tag called play in full screen that would be awesome to be able to enable as well Edit: Worked out how to do the full screen part

if this helps here is the difference between a slide with a video set to on click and here is the same slide with a video set to automatically play

https://www.diffchecker.com/aSGgBiOS

UAEpro commented 5 years ago

@Lileso did you make any progress of auto play ? could you share it please ?

Lileso commented 5 years ago

I'm afraid not. I couldn't seem to get it to work no matter what I changed in the xml

Lileso commented 5 years ago

Here is a more up to date diff https://www.diffchecker.com/IuCdI8Rd The left handside is the on click and right handside is the auto run.

Looking through the source code I can't find how it edits the XML when it adds the movie but it should be just adding an event

<p:cond evt="onBegin" delay="0">
   <p:tn val="2"/>
</p:cond>

And then adjusting <p:cTn id="5" presetID="1" presetClass="mediacall" presetSubtype="0" fill="hold" nodeType="clickEffect"> to

<p:cTn id="5" presetID="1" presetClass="mediacall" presetSubtype="0" fill="hold" nodeType="afterEffect">

When manually editing the XML this works in auto playing video

Lileso commented 5 years ago

--------------Please ignore my previous post as it doesn't stick the to OOXML Standard.----------------- @UAEpro I've written some changes to the Scanny's python-pptx. This allows for autoplay but does have a small bug at the moment which I am still working on in a different branch https://github.com/Lileso/python-pptx

iota-pi commented 5 years ago

If anyone else is looking for a quick hack that doesn't require modifying the library code, here's what I did (it's ugly but it works)

from lxml import etree
movie = slide.shapes.add_movie(path, 0, 0, width, height, poster_frame_image=still, mime_type=mime)
tree = movie._element.getparent().getparent().getnext().getnext()
timing = [el for el in tree.iterdescendants() if etree.QName(el).localname == 'cond'][0]
timing.set('delay', '0')

I haven't tested it on a slide with any advanced timing or multiple shapes

Lileso commented 5 years ago

@iota-pi

This is what my code does so I'm afraid to say it suffers the same bug mine does. I'm currently working on adding the animation like PowerPoint does but am getting stuck in adding the to the XML parser library.

UAEpro commented 5 years ago

to make the video loop I did that

repeat = [el for el in tree.iterdescendants() if etree.QName(el).localname == 'cTn'][1]
repeat.set('repeatCount', 'indefinite')
repeat.set('fill', 'hold')
repeat.set('display', '0')

Thanks to @iota-pi code the purpose is to replace gif with mp4 which have smaller size

yoonghm commented 4 years ago

I have added a movie, but etree.QName(el).localname only shows

  1. Choice
  2. transition
  3. Fallback
  4. transition

I did not see cond

Do you have any help? Thanks.

yoonghm commented 4 years ago

Ok, I managed to fix it now. The xml has changed. My code is

        tree = movie._element.getparent().getparent().getnext().getnext().getnext()
        timing = [el for el in tree.iterdescendants() if etree.QName(el).localname == 'cond'][0]
        print(timing)
        timing.set('delay', '0')
Kayron013 commented 4 years ago

After trying the code from @iota-pi I believe the bug @Lileso is referring to, is only being able to autoplay the first media item. I took a look at the xml and found a solution that seems to work for autoplaying specific media.

from lxml import etree

def autoplay_media(media):
    el = media._element
    id = xpath(el, './/p:cNvPr')[0].attrib['id']
    sld = el.getparent().getparent().getparent()
    cond = xpath(sld, './/p:timing//p:video//p:cTn[@id="%s"]//p:cond' % (id))[0]
    cond.set('delay', '0')

def xpath(el, query):
    nsmap = {'p': 'http://schemas.openxmlformats.org/presentationml/2006/main'}
    return etree.ElementBase.xpath(el, query, namespaces=nsmap)
monstarnn commented 3 years ago

I've tried @Kayron013 code, but got errors with some presentations. So I've found that video's timing can be described as in this example:

<p:video>
    <p:cMediaNode vol="80000">
        <p:cTn id="2" fill="hold" display="0">
            <p:stCondLst>
                <p:cond delay="indefinite"/>
             </p:stCondLst>
           </p:cTn>
           <p:tgtEl>
               <p:spTgt spid="171"/>
           </p:tgtEl>
     </p:cMediaNode>
</p:video>

I've modified autoplay_media method to this one:

def autoplay_media(media):
    el_id = xpath(media.element, './/p:cNvPr')[0].attrib['id']
    el_cnt = xpath(
        media.element.getparent().getparent().getparent(),
        './/p:timing//p:video//p:spTgt[@spid="%s"]' % el_id,
    )[0]
    cond = xpath(el_cnt.getparent().getparent(), './/p:cond')[0]
    cond.set('delay', '0')

It works in all my test cases.

yc-cui commented 2 years ago

btw, this code can set time interval of a given slide (default 2s)

def auto_adjust_time(slide, ms=2000):
    p = "http://schemas.openxmlformats.org/presentationml/2006/main"
    ele = etree.Element(etree.QName(p, "transition"), attrib={'spd': 'med', 'advClick': '0', 'advTm': str(ms)})
    xpath(slide.element, './/p:clrMapOvr')[0].getparent().insert(-1, ele)
zookz commented 1 year ago

@monstarnn monstarnn

I have tried your example, and it works great, but I also need to set "loop until stopped" , do you know how to set that?

zookz commented 1 year ago

FYI .. I got it to work ... if anyone need to set the "loop until stopped" prop here is how.

def autoplay_media(media):
    el_id = xpath(media.element, './/p:cNvPr')[0].attrib['id']
    el_cnt = xpath(
        media.element.getparent().getparent().getparent(),
        './/p:timing//p:video//p:spTgt[@spid="%s"]' % el_id,
    )[0]

    ctn = xpath(el_cnt.getparent().getparent(), './/p:cTn')[0]
    ctn.set('repeatCount', 'indefinite')

    cond = xpath(el_cnt.getparent().getparent(), './/p:cond')[0]
    cond.set('delay', '0')
zervyou commented 1 year ago

If anyone else is looking for a quick hack that doesn't require modifying the library code, here's what I did (it's ugly but it works)

from lxml import etree
movie = slide.shapes.add_movie(path, 0, 0, width, height, poster_frame_image=still, mime_type=mime)
tree = movie._element.getparent().getparent().getnext().getnext()
timing = [el for el in tree.iterdescendants() if etree.QName(el).localname == 'cond'][0]
timing.set('delay', '0')

I haven't tested it on a slide with any advanced timing or multiple shapes

This worked great, thanks for sharing

dinhnguyens commented 9 months ago

FYI .. I got it to work ... if anyone need to set the "loop until stopped" prop here is how.

def autoplay_media(media):
    el_id = xpath(media.element, './/p:cNvPr')[0].attrib['id']
    el_cnt = xpath(
        media.element.getparent().getparent().getparent(),
        './/p:timing//p:video//p:spTgt[@spid="%s"]' % el_id,
    )[0]

    ctn = xpath(el_cnt.getparent().getparent(), './/p:cTn')[0]
    ctn.set('repeatCount', 'indefinite')

    cond = xpath(el_cnt.getparent().getparent(), './/p:cond')[0]
    cond.set('delay', '0')

@monstarnn It only works when the slides go forward, still not works when go backward. Any updates on the issue. Thanks,