Skip to content
Advertisement

How to call a method while another method is still running- Python

I’m working on a python program that extracts the sound from a given YouTube video url and plays it. I created a play_vid method to play the sound and a pause_vid method to pause the sound. The issue is that while the sound is still playing inside the play_vid method and I try to call the pause_vid method, the call gets ignored and the play_vid method keeps running. Example:

vid = Video("youtubelink")
vid.play_vid()
time.sleep(5)
# The sound keeps playing/this method call is being ignored
vid.pause_vid()

How do I terminate the play_vid method when the pause_vid method is called? I’ve been researching for a while and wasn’t able to find a solution to this problem. Full program:

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import time, os, vlc, requests
from mutagen.mp3 import MP3


class InvalidUrl(Exception):
    def __init__(self, message="Invalid video url!"):
        super().__init__(message)


class Video:
    def __init__(self, url):
        self.url = url
        self.paused = False
        self.player = ""
        self.is_valid()
        self.time_sleep = 0

    def play_vid(self):
        driver = webdriver.Chrome("C:chromedriver.exe")
        driver.get("https://ytmp3.cc/en14/")
        element = driver.find_element_by_id("input")
        element.send_keys(self.url)
        button = driver.find_element_by_id("submit")
        button.click()
        wait = WebDriverWait(driver, 10)
        button2 = wait.until(EC.visibility_of_element_located((By.XPATH, '//*[@id="buttons"]/a[1]')))
        button2.click()
        vid_name = driver.find_element_by_xpath('//*[@id="title"]').text
        vid_name = vid_name.replace("| ", "")
        file_name = os.path.expanduser(r"~Downloads{}.mp3".format(vid_name))

        while not os.path.exists(file_name):
            time.sleep(1)

        driver.close()
        self.player = vlc.MediaPlayer()
        media = vlc.Media(file_name)
        self.player.set_media(media)
        self.player.play()
        audio = MP3(file_name)
        audio_info = audio.info
        length_in_secs = int(audio_info.length)

        while self.time_sleep != length_in_secs:
            if self.paused is True:
                return

            time.sleep(1)
            self.time_sleep += 1

    def pause_vid(self):
        self.paused = True

    def is_valid(self):
        r = requests.get(self.url)

        if "Video unavailable" in r.text:
            raise InvalidUrl

A big thanks to Mark M for letting me know that I forgot to add self.player.set_pause(1) in the pause_vid method. He also advised me to remove the while loop, so I used time.sleep in my main program in order to decide how long the video will play for instead of the while loop. Updated code:

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import time, os, vlc, requests
from mutagen.mp3 import MP3


class InvalidYouTubeUrl(Exception):
    def __init__(self, message="Invalid YouTube url!"):
        super().__init__(message)


class Video:
    def __init__(self, url):
        self.url = url
        self.player = None
        self.is_valid()
        self.vid_len = None

    def play_vid(self):
        driver = webdriver.Chrome("C:chromedriver.exe")
        driver.get("https://ytmp3.cc/en14/")
        element = driver.find_element_by_id("input")
        element.send_keys(self.url)
        button = driver.find_element_by_id("submit")
        button.click()
        wait = WebDriverWait(driver, 10)
        button2 = wait.until(EC.visibility_of_element_located((By.XPATH, '//*[@id="buttons"]/a[1]')))
        button2.click()
        vid_name = driver.find_element_by_xpath('//*[@id="title"]').text
        vid_name = vid_name.replace("| ", "")
        file_name = os.path.expanduser(r"~Downloads{}.mp3".format(vid_name))

        while not os.path.exists(file_name):
            time.sleep(1)

        driver.close()
        self.player = vlc.MediaPlayer()
        media = vlc.Media(file_name)
        self.player.set_media(media)
        self.player.play()
        audio = MP3(file_name)
        audio_info = audio.info
        self.vid_len = int(audio_info.length)

    def pause_vid(self):
        self.player.set_pause(1)

    def is_valid(self):
        r = requests.get(self.url)

        if "Video unavailable" in r.text:
            raise InvalidYouTubeUrl

I’ve also found a way to play the whole sound in the main program:

import pyytmp3, time

vid = pyytmp3.Video("youtubelink")
vid.play_vid()
time.sleep(vid.vid_len)

I’m basically just delaying the program by the length of the video.

Advertisement

Answer

The problem is two-fold:

  1. You’re not actually pausing the audio playback, so the MediaPlayer will just continue to play the rest of it. According to the MediaPlayer docs it has a function pause() or even stop(). Call this from your pause_vid method.
  2. You’ll need to invoke the vid.pause_vid() method from another thread because the play_vid() method will just run till completion. You can create a background thread to play the video from. Some good examples here https://pymotw.com/3/threading/ On second thought, I think the MediaPlayer.play() method already runs in the background due to the wording in the documentation:

Play. @return: 0 if playback started (and was already started), or -1 on error.

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement