Ejemplo n.º 1
0
class YouTubeUploader:
    """A class for uploading videos on YouTube via Selenium using metadata JSON file
    to extract its title, description etc"""
    def __init__(self,
                 video_path: str,
                 metadata_json_path: Optional[str] = None) -> None:
        self.video_path = video_path

        metadata_dict = load_metadata(metadata_json_path)
        self.meta_title = metadata_dict[Constant.VIDEO_TITLE]
        self.meta_description = metadata_dict[Constant.VIDEO_DESCRIPTION]

        current_working_dir = str(Path.cwd())
        self.browser = Firefox(current_working_dir, current_working_dir)
        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(logging.DEBUG)
        self.__validate_inputs()

    def __validate_inputs(self):
        if not self.meta_title:
            self.logger.warning(
                "The video title was not found in a metadata file")
            self.meta_title = Path(self.video_path).stem
            self.logger.warning("The video title was set to {}".format(
                Path(self.video_path).stem))
        if not self.meta_description:
            self.logger.warning(
                "The video description was not found in a metadata file")

    def upload(self):
        try:
            self.__login()
            return self.__upload()
        except Exception as e:
            print(e)
            self.__quit()
            raise

    def __login(self):
        self.browser.get(Constant.YOUTUBE_URL)
        time.sleep(Constant.USER_WAITING_TIME)

        if self.browser.has_cookies_for_current_website():
            self.browser.load_cookies()
            time.sleep(Constant.USER_WAITING_TIME)
            self.browser.refresh()
        else:
            self.logger.info('Please sign in and then press enter')
            input()
            self.browser.get(Constant.YOUTUBE_URL)
            time.sleep(Constant.USER_WAITING_TIME)
            self.browser.save_cookies()

    def __upload(self) -> (bool, Optional[str]):
        self.browser.get(Constant.YOUTUBE_URL)
        time.sleep(Constant.USER_WAITING_TIME)
        self.browser.get(Constant.YOUTUBE_UPLOAD_URL)
        time.sleep(Constant.USER_WAITING_TIME)
        absolute_video_path = str(Path.cwd() / self.video_path)
        self.browser.find(
            By.XPATH, Constant.INPUT_FILE_VIDEO).send_keys(absolute_video_path)
        self.logger.debug('Attached video {}'.format(self.video_path))
        title_field = self.browser.find(By.ID, Constant.TEXTBOX, timeout=10)
        title_field.click()
        time.sleep(Constant.USER_WAITING_TIME)
        title_field.clear()
        time.sleep(Constant.USER_WAITING_TIME)
        title_field.send_keys(Keys.COMMAND + 'a')
        time.sleep(Constant.USER_WAITING_TIME)
        title_field.send_keys(self.meta_title)
        self.logger.debug('The video title was set to \"{}\"'.format(
            self.meta_title))

        video_description = self.meta_description
        if video_description:
            description_container = self.browser.find(
                By.XPATH, Constant.DESCRIPTION_CONTAINER)
            description_field = self.browser.find(
                By.ID, Constant.TEXTBOX, element=description_container)
            description_field.click()
            time.sleep(Constant.USER_WAITING_TIME)
            description_field.clear()
            time.sleep(Constant.USER_WAITING_TIME)
            description_field.send_keys(self.meta_description)
            self.logger.debug('The video description was set to \"{}\"'.format(
                self.meta_description))

        kids_section = self.browser.find(By.NAME,
                                         Constant.NOT_MADE_FOR_KIDS_LABEL)
        self.browser.find(By.ID, Constant.RADIO_LABEL, kids_section).click()
        self.logger.debug('Selected \"{}\"'.format(
            Constant.NOT_MADE_FOR_KIDS_LABEL))

        self.browser.find(By.ID, Constant.NEXT_BUTTON).click()
        self.logger.debug('Clicked {}'.format(Constant.NEXT_BUTTON))

        self.browser.find(By.ID, Constant.NEXT_BUTTON).click()
        self.logger.debug('Clicked another {}'.format(Constant.NEXT_BUTTON))

        public_main_button = self.browser.find(By.NAME, Constant.PUBLIC_BUTTON)
        self.browser.find(By.ID, Constant.RADIO_LABEL,
                          public_main_button).click()
        self.logger.debug('Made the video {}'.format(Constant.PUBLIC_BUTTON))

        video_id = self.__get_video_id()

        status_container = self.browser.find(By.XPATH,
                                             Constant.STATUS_CONTAINER)
        while True:
            in_process = status_container.text.find(Constant.UPLOADED) != -1
            if in_process:
                time.sleep(Constant.USER_WAITING_TIME)
            else:
                break

        done_button = self.browser.find(By.ID, Constant.DONE_BUTTON)

        # Catch such error as
        # "File is a duplicate of a video you have already uploaded"
        if done_button.get_attribute('aria-disabled') == 'true':
            error_message = self.browser.find(By.XPATH,
                                              Constant.ERROR_CONTAINER).text
            self.logger.error(error_message)
            return False, None

        done_button.click()
        self.logger.debug(
            "Published the video with video_id = {}".format(video_id))
        time.sleep(Constant.USER_WAITING_TIME)
        self.browser.get(Constant.YOUTUBE_URL)
        self.__quit()
        return True, video_id

    def __get_video_id(self) -> Optional[str]:
        video_id = None
        try:
            video_url_container = self.browser.find(
                By.XPATH, Constant.VIDEO_URL_CONTAINER)
            video_url_element = self.browser.find(By.XPATH,
                                                  Constant.VIDEO_URL_ELEMENT,
                                                  element=video_url_container)
            video_id = video_url_element.get_attribute(
                Constant.HREF).split('/')[-1]
        except:
            self.logger.warning(Constant.VIDEO_NOT_FOUND_ERROR)
            pass
        return video_id

    def __quit(self):
        self.browser.driver.quit()
class YouTubeUploader:
    """A class for uploading videos on YouTube via Selenium using metadata JSON file
    to extract its title, description etc"""
    def __init__(self,
                 video_path: str,
                 metadata_json_path: Optional[str] = None,
                 thumbnail_path: Optional[str] = None) -> None:
        self.video_path = video_path
        self.thumbnail_path = thumbnail_path
        self.metadata_dict = load_metadata(metadata_json_path)
        current_working_dir = str(Path.cwd())
        self.browser = Firefox(current_working_dir, current_working_dir)
        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(logging.DEBUG)
        self.__validate_inputs()

    def __validate_inputs(self):
        if not self.metadata_dict[Constant.VIDEO_TITLE]:
            self.logger.warning(
                "The video title was not found in a metadata file")
            self.metadata_dict[Constant.VIDEO_TITLE] = Path(
                self.video_path).stem
            self.logger.warning("The video title was set to {}".format(
                Path(self.video_path).stem))
        if not self.metadata_dict[Constant.VIDEO_DESCRIPTION]:
            self.logger.warning(
                "The video description was not found in a metadata file")

    def upload(self):
        try:
            self.__login()
            return self.__upload()
        except Exception as e:
            print(e)
            # self.__quit()
            # raise

    def __login(self):
        self.browser.get(Constant.YOUTUBE_URL)
        time.sleep(Constant.USER_WAITING_TIME)

        if self.browser.has_cookies_for_current_website():
            self.browser.load_cookies()
            time.sleep(Constant.USER_WAITING_TIME)
            self.browser.refresh()
        else:
            self.logger.info('Please sign in and then press enter')
            input()
            self.browser.get(Constant.YOUTUBE_URL)
            time.sleep(Constant.USER_WAITING_TIME)
            self.browser.save_cookies()

    def __write_in_field(self, field, string, select_all=False):
        field.click()
        time.sleep(Constant.USER_WAITING_TIME)
        if select_all:
            field.send_keys(Keys.COMMAND + 'a')
            time.sleep(Constant.USER_WAITING_TIME)
        field.send_keys(string)

    def __upload(self) -> (bool, Optional[str]):
        self.browser.get(Constant.YOUTUBE_URL)
        time.sleep(Constant.USER_WAITING_TIME)
        self.browser.get(Constant.YOUTUBE_UPLOAD_URL)
        time.sleep(Constant.USER_WAITING_TIME)
        absolute_video_path = str(Path.cwd() / self.video_path)
        self.browser.find(
            By.XPATH, Constant.INPUT_FILE_VIDEO).send_keys(absolute_video_path)
        self.logger.debug('Attached video {}'.format(self.video_path))

        if self.thumbnail_path is not None:
            absolute_thumbnail_path = str(Path.cwd() / self.thumbnail_path)
            self.browser.find(By.XPATH,
                              Constant.INPUT_FILE_THUMBNAIL).send_keys(
                                  absolute_thumbnail_path)
            change_display = "document.getElementById('file-loader').style = 'display: block! important'"
            self.browser.driver.execute_script(change_display)
            self.logger.debug('Attached thumbnail {}'.format(
                self.thumbnail_path))

        title_field = self.browser.find(By.ID, Constant.TEXTBOX, timeout=10)
        self.__write_in_field(title_field,
                              self.metadata_dict[Constant.VIDEO_TITLE],
                              select_all=True)
        self.logger.debug('The video title was set to \"{}\"'.format(
            self.metadata_dict[Constant.VIDEO_TITLE]))

        video_description = self.metadata_dict[Constant.VIDEO_DESCRIPTION]
        if video_description:
            description_container = self.browser.find(
                By.XPATH, Constant.DESCRIPTION_CONTAINER)
            # description_field = self.browser.find(By.ID, Constant.TEXTBOX, element=description_container)
            # NOTE: description_field we fixed!
            description_field = self.browser.find(
                By.XPATH,
                '//*[@id="details" and @class="style-scope ytcp-uploads-dialog"]//*[@id="textbox" and contains(@aria-label, "Tell viewers")]'
            )
            self.__write_in_field(
                description_field,
                self.metadata_dict[Constant.VIDEO_DESCRIPTION])
            self.logger.debug('The video description was set to \"{}\"'.format(
                self.metadata_dict[Constant.VIDEO_DESCRIPTION]))

        kids_section = self.browser.find(By.NAME,
                                         Constant.NOT_MADE_FOR_KIDS_LABEL)
        self.browser.find(By.ID, Constant.RADIO_LABEL, kids_section).click()
        self.logger.debug('Selected \"{}\"'.format(
            Constant.NOT_MADE_FOR_KIDS_LABEL))

        # Advanced options
        self.browser.find(By.XPATH, Constant.MORE_BUTTON).click()
        self.logger.debug('Clicked MORE OPTIONS')

        # tags_container = self.browser.find(By.XPATH, Constant.TAGS_INPUT_CONTAINER)
        # tags_field = self.browser.find(By.ID, Constant.TAGS_INPUT, element=tags_container)
        # NOTE: Seems like we fixed tags_field!
        tags_field = self.browser.find(
            By.XPATH, '//*[@id="text-input" and @aria-label="Tags"]')
        self.__write_in_field(
            tags_field, ','.join(self.metadata_dict[Constant.VIDEO_TAGS]))
        self.logger.debug('The tags were set to \"{}\"'.format(
            self.metadata_dict[Constant.VIDEO_TAGS]))

        self.browser.find(By.ID, Constant.NEXT_BUTTON).click()
        self.logger.debug('Clicked {}'.format(Constant.NEXT_BUTTON))

        self.browser.find(By.ID, Constant.NEXT_BUTTON).click()
        self.logger.debug('Clicked another {}'.format(Constant.NEXT_BUTTON))

        self.browser.find(By.ID, Constant.NEXT_BUTTON).click()
        self.logger.debug('Clicked another {}'.format(Constant.NEXT_BUTTON))

        # public_main_button = self.browser.find(By.NAME, Constant.PUBLIC_BUTTON)
        # self.browser.find(By.ID, Constant.RADIO_LABEL, public_main_button).click()
        # NOTE: We fixed pathf or visibility (PUBLIC)
        self.browser.find(
            By.XPATH, '//*[@id="privacy-radios"]/*[@name="PUBLIC"]').click()
        self.logger.debug('Made the video {}'.format(Constant.PUBLIC_BUTTON))

        video_id = self.__get_video_id()

        status_container = self.browser.find(By.XPATH,
                                             Constant.STATUS_CONTAINER)
        while True:
            in_process = status_container.text.find(Constant.UPLOADED) != -1
            if in_process:
                time.sleep(Constant.USER_WAITING_TIME)
            else:
                break

        done_button = self.browser.find(By.ID, Constant.DONE_BUTTON)

        # Catch such error as
        # "File is a duplicate of a video you have already uploaded"
        if done_button.get_attribute('aria-disabled') == 'true':
            error_message = self.browser.find(By.XPATH,
                                              Constant.ERROR_CONTAINER).text
            self.logger.error(error_message)
            return False, None

        done_button.click()
        self.logger.debug(
            "Published the video with video_id = {}".format(video_id))
        time.sleep(Constant.USER_WAITING_TIME)
        self.browser.get(Constant.YOUTUBE_URL)
        self.__quit()
        return True, video_id

    def __get_video_id(self) -> Optional[str]:
        video_id = None
        try:
            video_url_container = self.browser.find(
                By.XPATH, Constant.VIDEO_URL_CONTAINER)
            video_url_element = self.browser.find(By.XPATH,
                                                  Constant.VIDEO_URL_ELEMENT,
                                                  element=video_url_container)
            video_id = video_url_element.get_attribute(
                Constant.HREF).split('/')[-1]
        except:
            self.logger.warning(Constant.VIDEO_NOT_FOUND_ERROR)
            pass
        return video_id

    def __quit(self):
        self.browser.driver.quit()
Ejemplo n.º 3
0
class YouTubeUploader:
    """A class for uploading videos on YouTube via Selenium using metadata dict"""
    def __init__(self, username, cookies_path="") -> None:
        # Optional cookies_path to override username
        # for debugging purposes
        if cookies_path == "":
            cookies_path = YouTubeLogin.get_cookie_path_from_username(username)
        self.username = username
        self.browser = Firefox(full_screen=False,
                               cookies_folder_path=cookies_path,
                               default_find_func_timeout=10)
        self.logger = logging.getLogger()

    def __validate_inputs(self):
        if Constant.VIDEO_TITLE not in self.metadata_dict:
            self.logger.warning("The video title was not found in metadata")
            self.metadata_dict[Constant.VIDEO_TITLE] = Path(
                self.video_path).stem
            self.logger.warning("The video title was set to {}".format(
                Path(self.video_path).stem))
        for key in (Constant.VIDEO_DESCRIPTION, Constant.PLAYLIST,
                    Constant.TAGS):
            if key not in self.metadata_dict:
                self.metadata_dict[key] = ""

        title = self.metadata_dict[Constant.VIDEO_TITLE]
        if len(title) > Constant.MAX_TITLE_LENGTH:
            self.logger.warning("Truncating title to {} characters".format(
                Constant.MAX_TITLE_LENGTH))
            self.metadata_dict[
                Constant.VIDEO_TITLE] = title[:Constant.MAX_TITLE_LENGTH]

        description = self.metadata_dict[Constant.VIDEO_DESCRIPTION]
        if len(description) > Constant.MAX_DESCRIPTION_LENGTH:
            self.logger.warning(
                "Truncating description to {} characters".format(
                    Constant.MAX_DESCRIPTION_LENGTH))
            self.metadata_dict[
                Constant.
                VIDEO_DESCRIPTION] = description[:Constant.
                                                 MAX_DESCRIPTION_LENGTH]

        tags = self.metadata_dict[Constant.TAGS]
        if len(tags) > Constant.MAX_TAGS_LENGTH:
            self.logger.warning("Truncating tags to {} characters".format(
                Constant.MAX_TAGS_LENGTH))
            self.metadata_dict[Constant.TAGS] = tags[:Constant.MAX_TAGS_LENGTH]

    def upload(self, video_path, metadata) -> (bool, Optional[str]):
        try:
            self.video_path = video_path
            self.metadata_dict = metadata
            self.__validate_inputs()
            self.__login()
            return self.__upload()
        except Exception as e:
            self.__quit()
            raise

    def __login(self):
        self.browser.get(Constant.YOUTUBE_URL)
        time.sleep(Constant.USER_WAITING_TIME)

        if self.browser.find(By.XPATH, Constant.USER_AVATAR_XPATH,
                             timeout=5) is not None:
            # already logged in
            return

        if self.browser.has_cookies_for_current_website():
            self.browser.load_cookies()
            time.sleep(Constant.USER_WAITING_TIME)
            self.browser.refresh()
        else:
            raise Exception("Could not find cookies at path {}".format(
                self.browser.cookies_folder_path))

    def __find_playlist_checkbox_no_search(self, name):
        for element in self.browser.find_all(By.XPATH,
                                             Constant.PLAYLIST_LABEL):
            name_element = element.find_element_by_xpath(
                ".//span/span[@class='label label-text style-scope ytcp-checkbox-group']"
            )
            if name_element.text == name:
                return element.find_element_by_xpath(".//ytcp-checkbox-lit")
        return None

    def __find_playlist_checkbox(self, name):
        try:
            checkbox = self.__find_playlist_checkbox_no_search(name)

            if not checkbox:
                # sometimes a newly created playlist will not show up in the list of playlists
                # we can search for it to update the list
                search = self.browser.find(By.XPATH, Constant.PLAYLIST_SEARCH)

                # search behaves weird with opening brackets / parentheses,
                # possibly other characters as well
                # need to investigate this further:
                # Uncaught SyntaxError: unterminated character class
                # Uncaught SyntaxError: unterminated parenthetical
                phrases = re.split(r"[\[(]", name)
                phrases.sort(key=lambda p: len(p))
                search.click()
                time.sleep(Constant.USER_WAITING_TIME)
                search.clear()
                time.sleep(Constant.USER_WAITING_TIME)
                search.send_keys(phrases[-1])
                checkbox = self.__find_playlist_checkbox_no_search(name)
                # clear search so we can create new playlist
                self.browser.find(
                    By.XPATH, Constant.PLAYLIST_SEARCH_CLEAR_BUTTON).click()

            return checkbox

        except Exception as e:
            logging.error(e)
            return None

    def __upload(self) -> (bool, Optional[str]):
        self.browser.get(Constant.YOUTUBE_URL)
        time.sleep(Constant.USER_WAITING_TIME)
        self.browser.get(Constant.YOUTUBE_UPLOAD_URL)
        time.sleep(Constant.USER_WAITING_TIME)
        absolute_video_path = str(Path.cwd() / self.video_path)
        self.browser.find(
            By.XPATH, Constant.INPUT_FILE_VIDEO).send_keys(absolute_video_path)
        self.logger.debug('Attached video {}'.format(self.video_path))
        time.sleep(Constant.USER_WAITING_TIME)
        if (title_field := self.browser.find(
                By.ID, Constant.TEXTBOX, timeout=30)) is None:
            self.logger.error('Could not find title field')
            return False, None
        title_field.click()
        time.sleep(Constant.USER_WAITING_TIME)
        title_field.clear()
        time.sleep(Constant.USER_WAITING_TIME)
        if sys.platform == 'darwin':
            title_field.send_keys(Keys.COMMAND + 'a')
        else:
            title_field.send_keys(Keys.CONTROL + 'a')
        time.sleep(Constant.USER_WAITING_TIME)
        title_field.send_keys(self.metadata_dict[Constant.VIDEO_TITLE])
        self.logger.debug('The video title was set to \"{}\"'.format(
            self.metadata_dict[Constant.VIDEO_TITLE]))

        video_description = self.metadata_dict[Constant.VIDEO_DESCRIPTION]
        tags = self.metadata_dict[Constant.TAGS]
        playlist = self.metadata_dict[Constant.PLAYLIST]
        if video_description:
            description_container = self.browser.find(
                By.XPATH, Constant.DESCRIPTION_CONTAINER)
            description_field = self.browser.find(
                By.ID, Constant.TEXTBOX, element=description_container)
            description_field.click()
            time.sleep(Constant.USER_WAITING_TIME)
            description_field.clear()
            time.sleep(Constant.USER_WAITING_TIME)
            description_field.send_keys(video_description)
            self.logger.debug('The video description was set to \"{}\"'.format(
                video_description))
        if playlist:
            self.browser.find(By.XPATH, Constant.PLAYLIST_CONTAINER).click()
            time.sleep(Constant.USER_WAITING_TIME)
            checkbox = self.__find_playlist_checkbox(playlist)
            if checkbox is None:
                self.logger.info(
                    "Could not find playlist checkbox, attempting to create new playlist"
                )
                playlist_new_button = self.browser.find(
                    By.XPATH, Constant.PLAYLIST_NEW_BUTTON)
                self.browser.move_to_element(playlist_new_button)
                time.sleep(Constant.USER_WAITING_TIME)
                playlist_new_button.click()
                time.sleep(Constant.USER_WAITING_TIME)
                playlist_title = self.browser.find(By.XPATH,
                                                   Constant.PLAYLIST_NEW_TITLE)
                if playlist_title is None:
                    logging.error("Could not find playlist title field")
                    return False, None
                playlist_title.click()
                time.sleep(Constant.USER_WAITING_TIME)
                playlist_title.send_keys(playlist)
                time.sleep(Constant.USER_WAITING_TIME)

                # Set playlist visibility
                self.browser.find(
                    By.XPATH, Constant.PLAYLIST_VISIBILITY_DROPDOWN).click()
                time.sleep(Constant.USER_WAITING_TIME)
                playlist_visibility = self.browser.find(
                    By.XPATH, '//*[@test-id="{}"]'.format(
                        self.metadata_dict['visibility']))
                if playlist_visibility is None:
                    logging.error(
                        "Could not find playlist visibility option {}".format(
                            self.metadata_dict['visibility']))
                    return False, None
                playlist_visibility.click()
                time.sleep(Constant.USER_WAITING_TIME)

                self.browser.find(By.XPATH,
                                  Constant.PLAYLIST_CREATE_BUTTON).click()
                time.sleep(Constant.USER_WAITING_TIME)
                checkbox = self.__find_playlist_checkbox(playlist)
            if checkbox is None:
                logging.error("Could not find playlist: {}".format(playlist))
                return False, None
            else:
                checkbox.click()
                time.sleep(Constant.USER_WAITING_TIME)
                self.browser.find(By.XPATH,
                                  Constant.PLAYLIST_DONE_BUTTON).click()
                time.sleep(Constant.USER_WAITING_TIME)

        # hide tooltips which can obscure buttons
        tooltips = self.browser.find_all(By.XPATH, Constant.TOOLTIP)
        if tooltips is not None:
            for element in tooltips:
                self.browser.execute_script_on_element(
                    "arguments[0].style.display = 'none'", element)

        if tags:
            self.browser.find(By.XPATH,
                              Constant.MORE_OPTIONS_CONTAINER).click()
            time.sleep(Constant.USER_WAITING_TIME)
            self.browser.find(By.XPATH,
                              Constant.TAGS_TEXT_INPUT).send_keys(tags)
            time.sleep(Constant.USER_WAITING_TIME)

        time.sleep(Constant.USER_WAITING_TIME)
        kids_section = self.browser.find(By.NAME,
                                         Constant.NOT_MADE_FOR_KIDS_LABEL)
        self.browser.scroll_to_element(kids_section)
        time.sleep(Constant.USER_WAITING_TIME)
        self.browser.find(By.ID, Constant.RADIO_LABEL, kids_section).click()
        self.logger.debug('Selected \"{}\"'.format(
            Constant.NOT_MADE_FOR_KIDS_LABEL))

        self.browser.find(By.ID, Constant.NEXT_BUTTON).click()
        self.logger.debug('Clicked {}'.format(Constant.NEXT_BUTTON))

        self.browser.find(By.ID, Constant.NEXT_BUTTON).click()
        self.logger.debug('Clicked another {}'.format(Constant.NEXT_BUTTON))

        visibility_button = self.browser.find(By.NAME,
                                              self.metadata_dict['visibility'])
        self.browser.find(By.ID, Constant.RADIO_LABEL,
                          visibility_button).click()
        self.logger.debug('Made the video {}'.format(
            self.metadata_dict['visibility']))

        video_id = self.__get_video_id()

        status_container = self.browser.find(By.XPATH,
                                             Constant.STATUS_CONTAINER)
        while True:
            in_process = status_container.text.find(Constant.UPLOADED) != -1
            if in_process:
                time.sleep(Constant.USER_WAITING_TIME)
            else:
                break

        done_button = self.browser.find(By.ID, Constant.DONE_BUTTON)

        # Catch such error as
        # "File is a duplicate of a video you have already uploaded"
        if done_button.get_attribute('aria-disabled') == 'true':
            error_message = self.browser.find(By.XPATH,
                                              Constant.ERROR_CONTAINER).text
            self.logger.error(error_message)
            return False, None

        done_button.click()
        self.logger.debug(
            "Published the video with video_id = {}".format(video_id))
        # wait for youtube to save the video info
        while self.browser.find(By.XPATH,
                                Constant.VIDEO_PUBLISHED_DIALOG) is None:
            time.sleep(1)
        return True, video_id
Ejemplo n.º 4
0
class Pinterest:
    def __init__(self,
                 cookies_folder_path: str,
                 extensions_folder_path: str,
                 host: Optional[str] = None,
                 port: Optional[int] = None,
                 headless: bool = False):
        self.browser = Firefox(cookies_folder_path,
                               extensions_folder_path,
                               host=host,
                               port=port,
                               headless=headless)

        try:
            self.browser.get(PT_URL)
            time.sleep(1.5)

            if self.browser.has_cookies_for_current_website():
                self.browser.load_cookies()
                time.sleep(1.5)
                self.browser.refresh()
                time.sleep(0.5)
            else:
                input('Log in then press enter')
                self.browser.get(PT_URL)
                time.sleep(1.5)
                self.browser.save_cookies()
        except:
            traceback.print_exc()
            self.browser.driver.quit()

            raise

    def follow(self, user_name: str) -> bool:
        try:
            self.browser.get(UrlCreator.user_url(user_name))
            rand.sleep(0.5, 1)

            follow_container = self.browser.find(
                By.XPATH,
                "//div[contains(@data-test-id, 'user-follow-button')]",
                timeout=2.5)

            if follow_container:
                follow_button = self.browser.find(
                    By.XPATH,
                    "//div[contains(@class, 'tBJ dyH iFc yTZ erh tg7 mWe')]",
                    follow_container)

                if follow_button is not None and follow_button.text == "Follow":
                    follow_button.click()
                    rand.sleep(0.5, 1)
                else:
                    return False

                follow_buttons_updated = self.browser.find_all(
                    By.XPATH,
                    "//div[contains(@class, 'tBJ dyH iFc yTZ pBj tg7 mWe')]")

                for elem in follow_buttons_updated:
                    if elem.text == "Following":
                        print('user.text', elem.text)

                        return True

            elif follow_container is None:
                user = self.browser.find(
                    By.XPATH,
                    "//div[contains(@class, 'tBJ dyH iFc yTZ erh tg7 mWe')]")

                if user.text == "Follow":
                    user.click()
                    rand.sleep(1, 1.5)

                user_updated = self.browser.find(
                    By.XPATH,
                    "//div[contains(@class, 'tBJ dyH iFc yTZ erh tg7 mWe')]")

                return user_updated.text == "Following"
        except:
            traceback.print_exc()

            return False

    def unfollow(self, user_name: str) -> bool:
        try:
            self.browser.get(UrlCreator.user_url(user_name))
            rand.sleep(1, 2)
            user_container = self.browser.find(
                By.XPATH,
                "//div[contains(@data-test-id, 'user-unfollow-button')]")

            if user_container is not None:
                user_element = self.browser.find(
                    By.XPATH,
                    "//button[contains(@class, 'RCK Hsu USg Vxj aZc Zr3 hA- GmH adn a_A gpV hNT iyn BG7 NTm KhY')]",
                    user_container)
                user_element.click()
                rand.sleep(0.5, 1)

                return self.browser.find_by(
                    'button',
                    attributes={
                        'class':
                        'RCK Hsu USg Vxj aZc Zr3 hA- GmH adn Il7 Jrn hNT iyn BG7 NTm KhY',
                        'type': 'button'
                    }) is not None
            else:
                user = self.browser.find(
                    By.XPATH,
                    "//div[contains(@class, 'tBJ dyH iFc yTZ pBj tg7 mWe')]")

                if user.text == "Following":
                    user.click()
                    rand.sleep(1, 1.5)

                user_updated = self.browser.find(
                    By.XPATH,
                    "//div[contains(@class, 'tBJ dyH iFc yTZ erh tg7 mWe')]")

                return user_updated.text == "Follow"
        except:
            traceback.print_exc()

            return False

    def repin(self,
              pin_id: str,
              board_name: str,
              needs_repin_id: bool = False) -> Tuple[bool, Optional[str]]:
        try:
            self.browser.get(UrlCreator.pin_url(pin_id))
            rand.sleep(0.7, 1.2)

            board_dropdown = self.browser.find(
                By.XPATH,
                "//div[contains(@class, 'tBJ dyH iFc _yT pBj DrD IZT swG z-6')]",
                timeout=3)

            if board_dropdown is not None:
                board_dropdown.click()
                rand.sleep(0.1, 0.5)
            else:
                if self.__create_and_save_to_board(
                        board_name) and needs_repin_id:
                    return True, self.__get_link_to_repinned_post()
                elif self.__create_and_save_to_board(
                        board_name) and needs_repin_id is False:
                    return True, None

                return False, None

            boards = self.browser.find_all(
                By.XPATH,
                "//div[contains(@class, 'tBJ dyH iFc yTZ pBj DrD IZT mWe z-6')]",
                timeout=5)

            for board in boards:
                if board.text == board_name:
                    board.click()
                    rand.sleep(0.2, 0.5)

                    break
            else:
                self.browser.find(
                    By.XPATH, "//div[contains(@class, 'rDA wzk zI7 iyn Hsu')]"
                ).click()  # create board button
                text_tag = self.browser.find(
                    By.XPATH, "//input[contains(@id, 'boardEditName')]")
                text_tag.send_keys(board_name)
                rand.sleep(0.5, 1)
                self.browser.find(
                    By.XPATH,
                    "//button[contains(@class, 'RCK Hsu USg Vxj aZc Zr3 hA- GmH adn Il7 Jrn hNT iyn BG7 NTm KhY')]"
                ).click()  # create_button

                if needs_repin_id:
                    return True, self.__get_link_to_repinned_post()
                else:
                    return self.browser.find(
                        By.XPATH,
                        "//div[contains(@class, 'Eqh Shl s7I zI7 iyn Hsu')]"
                    ) is not None, None

            rand.sleep(0.5, 1)

            if needs_repin_id:
                return self.browser.find(
                    By.XPATH,
                    "//div[contains(@class, 'Eqh Shl s7I zI7 iyn Hsu')]"
                ) is not None, self.__get_link_to_repinned_post()
            else:
                return self.browser.find(
                    By.XPATH,
                    "//div[contains(@class, 'Eqh Shl s7I zI7 iyn Hsu')]"
                ) is not None, None
        except:
            traceback.print_exc()

            return False, None

    def __get_link_to_repinned_post(self) -> Optional[str]:
        try:
            saved_to_button = self.browser.find(
                By.XPATH,
                "//div[contains(@class, 'Shl ujU zI7 iyn Hsu')]",
                timeout=3)
            full_link = self.browser.find(
                By.CSS_SELECTOR, 'a', saved_to_button).get_attribute('href')

            self.browser.get(full_link)
            print(full_link)
            rand.sleep(2.5, 3)

            latest_image_box = self.browser.find(
                By.XPATH, "//div[contains(@class, 'Yl- MIw Hb7')]", timeout=5)
            pin_id = self.browser.find(
                By.XPATH, "//div[contains(@data-test-id, 'pin')]",
                latest_image_box).get_attribute('data-test-pin-id')
            rand.sleep(0.1, 0.3)

            return pin_id
        except:
            traceback.print_exc()

            return None

    def __create_and_save_to_board(self, board_name: str) -> bool:
        try:
            print('I am in the __create_and_save_to_board func')
            self.browser.find(
                By.XPATH,
                "//div[contains(@class, 'tBJ dyH iFc MF7 erh DrD IZT mWe')]"
            ).click()  # save button
            self.browser.find(
                By.XPATH, "//div[contains(@class, 'Umk fte zI7 iyn Hsu')]"
            ).click()  # create_board_button
            text_tag = self.browser.find(
                By.XPATH, "//input[contains(@id, 'boardEditName')]")
            text_tag.send_keys(board_name)
            rand.sleep(0.5, 0.8)
            self.browser.find(
                By.XPATH,
                "//button[contains(@class, 'RCK Hsu USg Vxj aZc Zr3 hA- GmH adn Il7 Jrn hNT iyn BG7 NTm KhY')]"
            ).click()  # create_button
            rand.sleep(1, 1.5)

            return self.browser.find(
                By.XPATH,
                "//button[contains(@class, 'RCK Hsu USg Vxj aZc Zr3 hA- GmH adn Il7 Jrn hNT iyn BG7 NTm KhY')]",
                timeout=3) is None
        except:
            traceback.print_exc()

            return False

    def get_board_followers(
            self,
            user_name: str,
            board_name: str,
            ignored_users: List[str],
            number_of_users_to_follow,
            full_board_url: str = None
    ) -> Optional[Tuple[List[str], List[str]]]:
        try:
            if full_board_url is not None:
                self.browser.get(full_board_url)
            else:
                self.browser.get(UrlCreator.board_url(user_name, board_name))

            rand.sleep(1, 1.5)
            followers_container = self.browser.find(
                By.XPATH,
                "//div[contains(@class, 'rLK iyn eEj FTD L4E DI9 BG7')]")

            if followers_container is not None:
                followers_container.click()
                rand.sleep(1, 1.5)

            saved_users = 0
            final_users = []

            while number_of_users_to_follow >= saved_users:
                try:
                    users_list = self.browser.find_all(
                        By.XPATH,
                        "//div[contains(@class, 'Module User hasText thumb medium')]"
                    )
                    users_length_before = len(final_users)

                    for user_container in users_list:
                        try:
                            print(user_container.text)
                            print('im in the for')
                            user_url = self.browser.find(
                                By.CSS_SELECTOR, 'a',
                                user_container).get_attribute('href')
                            user_name = user_url.split('.com/')[1].split(
                                '/')[0]

                            if user_name in ignored_users:
                                continue

                            ignored_users.append(user_name)
                            final_users.append(user_name)
                            saved_users += 1
                            print(saved_users, ':', user_name)

                            if saved_users == number_of_users_to_follow:
                                return (final_users, ignored_users)
                        except:
                            traceback.print_exc()
                except:
                    traceback.print_exc()

                users_length_after = len(final_users)
                see_more_button = self.browser.find(
                    By.XPATH,
                    "//div[contains(@class, 'tBJ dyH iFc yTZ pBj tg7 mWe')]",
                    timeout=1.5)

                if see_more_button is None or users_length_before == users_length_after:
                    return (final_users, ignored_users)

                see_more_button.click()
                rand.sleep(1, 1.5)

        except:
            traceback.print_exc()

            return None

    def search_pinterest_boards(self,
                                search_term: str,
                                number_of_boards_to_get: int = 35
                                ) -> Optional[List[Tuple[str, str]]]:
        try:
            self.browser.get(UrlCreator.search_board_url(search_term))
            rand.sleep(1, 1.5)

            if self.browser.find(By.XPATH,
                                 "//div[contains(@class, 'noResults')]"):
                return None

            board_names_container = self.browser.find_all(
                By.XPATH, "//div[contains(@class, 'Yl- MIw Hb7')]")
            number_of_saved_boards = 0
            board_urls = []

            while True:
                before_scroll = self.browser.current_page_offset_y()

                for board_name_element in board_names_container:
                    try:
                        full_board_url = self.browser.find(
                            By.CSS_SELECTOR, 'a',
                            board_name_element).get_attribute('href')
                        board_info = full_board_url.split('.com/')[1]
                        user_name = board_info.split('/')[0]
                        board_name = board_info.split('/')[1]

                        if (user_name, board_name) in board_urls:
                            continue

                        board_urls.append((user_name, board_name))
                        number_of_saved_boards += 1

                        if number_of_boards_to_get == number_of_saved_boards:
                            return board_urls
                    except:
                        traceback.print_exc()

                self.browser.scroll(1000)
                rand.sleep(0.5, 1.5)
                after_scroll = self.browser.current_page_offset_y()

                if after_scroll == before_scroll:
                    return board_urls
        except:
            traceback.print_exc()

            return None

    def get_pins_from_home_feed(self) -> Optional[List[str]]:
        try:
            self.browser.get(UrlCreator.home_feed_url())
            rand.sleep(1, 1.5)

            home_pins = []
            home_pin_containers = self.browser.find_all(
                By.XPATH, "//div[contains(@class, 'Yl- MIw Hb7')]")

            for pin in home_pin_containers:
                try:
                    full_url = self.browser.find(By.CSS_SELECTOR, 'a',
                                                 pin).get_attribute('href')

                    if 'pinterest.com' not in full_url:
                        continue

                    if 'pin/' in full_url:
                        pin_id = full_url.split('pin/')[1]
                        home_pins.append(pin_id)
                except:
                    traceback.print_exc()

            return home_pins
        except:
            traceback.print_exc()

            return None

    def post_pin(self,
                 image_path: str,
                 board_name: str,
                 title: Optional[str] = None,
                 description: Optional[str] = None,
                 url: Optional[str] = None) -> Optional[str]:
        try:
            self.browser.get(UrlCreator.pin_builder_url())
            rand.sleep(1, 1.5)

            image_box = self.browser.find_by('div',
                                             class_='DUt XiG zI7 iyn Hsu')

            if not image_box:
                raise 'did not find image_box'

            image_input = self.browser.find(By.CSS_SELECTOR, 'input',
                                            image_box)

            if not image_input:
                raise 'did not find image_input'

            image_input.send_keys(image_path)
            rand.sleep(1, 1.5)

            select_board_button = self.browser.find_by(
                'button',
                attributes={'data-test-id': 'board-dropdown-select-button'})
            select_board_button.click()
            rand.sleep(1, 1.5)
            board_search_field = self.browser.find_by('input',
                                                      id_='pickerSearchField')
            self.browser.send_keys_delay_random(board_search_field, board_name)
            rand.sleep(1, 1.5)
            boards = self.browser.find_all_by(
                'div',
                class_='tBJ dyH iFc yTZ pBj DrD IZT mWe z-6',
                timeout=2.5)

            exists_board = False

            if boards and len(boards) > 0:
                for board in boards:
                    if board.text == board_name:
                        exists_board = True
                        board.click()
                        rand.sleep(0.1, 0.5)

                        break

            if not exists_board:
                dropdown_boards = self.browser.find_by(
                    'div', class_='DUt qJc sLG zI7 iyn Hsu')
                create_board = self.browser.find_by(
                    'div',
                    class_='rDA wzk zI7 iyn Hsu',
                    in_element=dropdown_boards)

                if create_board is not None:
                    create_board.click()
                    rand.sleep(0.1, 0.5)

                board_name_textfield = self.browser.find_by(
                    'input', id_='boardEditName', timeout=2)

                if board_name_textfield.get_attribute('value') != board_name:
                    while len(board_name_textfield.get_attribute('value')) > 0:
                        board_name_textfield.send_keys(Keys.BACK_SPACE)
                        board_name_textfield = self.browser.find_by(
                            'input', id_='boardEditName', timeout=2)

                    self.browser.send_keys_delay_random(
                        board_name_textfield, board_name)
                    rand.sleep(0.5, 1)

                create_board_button = self.browser.find_by(
                    'button',
                    class_=
                    'RCK Hsu USg adn CCY czT F10 xD4 fZz hUC Il7 Jrn hNT BG7 NTm KhY',
                    timeout=2.5) or self.browser.find_by(
                        'button', {'type': 'submit'}, timeout=2.5)
                create_board_button.click()
                rand.sleep(0.5, 1)
                just_created_board_save_button = self.browser.find_by(
                    'div', class_='tBJ dyH iFc yTZ erh DrD IZT mWe')
                just_created_board_save_button.click()
                rand.sleep(0.5, 1.5)

            if title:
                title_box = self.browser.find_by(
                    'div', class_='CDp xcv L4E zI7 iyn Hsu')

                if title_box:
                    title_textfield = self.browser.find(
                        By.CSS_SELECTOR, "textarea", title_box)

                    if title_textfield:
                        self.browser.send_keys_delay_random(
                            title_textfield, title)
                        rand.sleep(0.5, 1.5)

            if description:
                about_box = self.browser.find_by(
                    'div', class_='Jea Tte ujU xcv L4E zI7 iyn Hsu', timeout=2)

                if about_box:
                    about_textfield = self.browser.find_by(
                        'div',
                        class_='notranslate public-DraftEditor-content',
                        in_element=about_box,
                        timeout=2)

                    if about_textfield:
                        self.browser.send_keys_delay_random(
                            about_textfield, description)
                        rand.sleep(0.5, 1.5)

            if url:
                url_box = self.browser.find_by(
                    'div', {'data-test-id': 'pin-draft-link'})

                if url_box:
                    url_textfield = self.browser.find(By.CSS_SELECTOR,
                                                      "textarea", url_box)

                    if url_textfield:
                        self.browser.send_keys_delay_random(url_textfield, url)
                        rand.sleep(0.5, 1.5)

            save_button = self.browser.find_by(
                'button', {'data-test-id': 'board-dropdown-save-button'})
            save_button.click()

            rand.sleep(0.1, 0.5)
            see_it_now = self.browser.find_by('div',
                                              {'data-test-id': 'seeItNow'})
            full_pin_url = self.browser.find(By.CSS_SELECTOR, 'a',
                                             see_it_now).get_attribute('href')

            return full_pin_url.split('pin/')[1]
        except:
            traceback.print_exc()

            return None
Ejemplo n.º 5
0
class YouTubeUploader:
    """A class for uploading videos on YouTube via Selenium using metadata JSON file
	to extract its title, description etc"""
    def __init__(self, headless: bool, cookies_path: str,
                 channel: str) -> None:
        self.channel = channel
        cookies_path = str(Path(cookies_path)) if cookies_path else str(
            Path.cwd())
        assert os.path.isdir(
            cookies_path), f"Directory '{cookies_path}' does not exist!"
        self.browser = Firefox(cookies_path, cookies_path, headless=headless)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.browser.driver.quit()

    def login(self, username: Optional[str], password: Optional[str]) -> bool:
        self.browser.get(Constant.YOUTUBE_URL)

        if self.browser.has_cookies_for_current_website():
            print("Loading cookies")
            self.browser.load_cookies()

        YOUTUBE_STUDIO_URL = f"https://studio.youtube.com/channel/{self.channel}"
        self.browser.get(YOUTUBE_STUDIO_URL)

        if self.browser.driver.current_url == YOUTUBE_STUDIO_URL:
            print("Logged in!")
            return True

        if None in {username, password} or "" in {
                username.strip(), password.strip()
        }:
            print("Username or password not provided!")
            return False

        # [G] fill in the username (email)
        print(f"Sending keys: email: '{username}@fel.cvut.cz'")
        email_field = self.browser.find(By.ID, "identifierId")
        email_field.click()
        email_field.clear()
        email_field.send_keys(f"{username}@fel.cvut.cz")

        # [G] click 'next' button
        print("Click: next")
        self.browser.find(By.ID, "identifierNext").click()

        if self.browser.find(By.XPATH, Constant.G_LOGIN_FAILED,
                             timeout=2) is not None:
            print("Invalid username!")
            return False

        # [SSO] fill in the username
        print(f"Sending keys: SSO username: '******'")
        sso_username_field = self.browser.find(By.ID, "username")
        sso_username_field.click()
        sso_username_field.clear()
        sso_username_field.send_keys(username)

        # [SSO] fill in the password
        print(f"Sending keys: SSO password: '******'")
        sso_password_field = self.browser.find(By.ID, "password")
        sso_password_field.click()
        sso_password_field.clear()
        sso_password_field.send_keys(password)

        # [SSO] click 'SSO login' button
        print("Click: SSO login")
        self.browser.find(By.NAME, "_eventId_proceed").click()

        if self.browser.find(By.CLASS_NAME, "error-message",
                             timeout=2) is not None:
            print("Invalid username or password!")
            return False

        print("Waiting for Google login...")
        if self.browser.find(By.ID, "upload-icon", timeout=20) is None:
            print("Login timeout!")
            return False

        if self.browser.driver.current_url != YOUTUBE_STUDIO_URL:
            print("Login failed!")
            return False

        # save cookies
        print("Saving cookies")
        self.browser.save_cookies()

        print("Logged in!")
        return True

    def upload(self, video: Video) -> (bool, Optional[str]):
        self.browser.get(f"https://studio.youtube.com/channel/{self.channel}")

        print("Click: upload")
        self.browser.find(By.ID, "upload-icon").click()

        print(f"Attaching video: '{video.filename}'")
        self.browser.find(By.XPATH, Constant.INPUT_FILE_VIDEO).send_keys(
            os.path.abspath(video.filename))

        if video.description:
            print(f"Sending keys: video description: '{video.description}'")
            description_field = self.browser.find(
                By.XPATH, Constant.DESCRIPTION_CONTAINER)
            description_field.click()
            description_field.clear()
            description_field.send_keys(video.description)

        if video.playlist:
            print("Click: playlist dropdown")
            self.browser.find(By.CLASS_NAME, "dropdown").click()

            # TODO: playlist ID
            print("Selecting the playlist")
            playlists = self.browser.find_all(
                By.CSS_SELECTOR,
                "label.style-scope.ytcp-checkbox-group.ytcp-checkbox-label")

            for playlist in playlists:
                playlist_name = self.browser.find(By.CSS_SELECTOR,
                                                  "span.label.label-text",
                                                  playlist).text
                if video.playlist == playlist_name:
                    print(f"Click: playlist checkbox: '{playlist_name}'")
                    self.browser.find(By.CSS_SELECTOR,
                                      "ytcp-checkbox-lit.ytcp-checkbox-group",
                                      playlist).click()
                    break
            else:  # create a new playlist
                print("Playlist not found in the list, creating a new one")
                self.browser.find(By.CLASS_NAME, "new-playlist-button").click()

                create_playlist_form = self.browser.find(
                    By.ID, "create-playlist-form")

                print(f"Sending keys: playlist name: '{video.playlist}'")
                textarea = self.browser.find(By.TAG_NAME,
                                             "textarea",
                                             element=create_playlist_form)
                textarea.click()
                textarea.clear()
                textarea.send_keys(video.playlist)

                print("Click: visibility dropdown")
                self.browser.find(By.CLASS_NAME,
                                  "visibility",
                                  element=create_playlist_form).click()

                print(f"Click: visibility: '{video.privacy.upper()}'")
                self.browser.find(
                    By.CLASS_NAME,
                    f'paper-item[test-id="{video.privacy.upper()}"]',
                    element=create_playlist_form).click()

                print("Click: create playlist")
                self.browser.find(By.CLASS_NAME,
                                  "create-playlist-button").click()

            print("Click: done")
            time.sleep(Constant.USER_WAITING_TIME)
            self.browser.find(By.CLASS_NAME, "done-button").click()

        print("Click: not made for kids")
        kids_section = self.browser.find(By.NAME,
                                         Constant.NOT_MADE_FOR_KIDS_LABEL)
        self.browser.find(By.ID, Constant.RADIO_LABEL, kids_section).click()

        print(f"Sending keys: video title: '{video.title}'")
        title_field = self.browser.find(By.ID, Constant.TEXTBOX, timeout=30)
        title_field.click()
        title_field.clear()
        title_field.send_keys(Keys.CONTROL + 'a')
        title_field.send_keys(video.title)

        for _ in range(3):
            print("Click: next")
            self.browser.find(By.ID, Constant.NEXT_BUTTON).click()

        if video.privacy:
            print(f"Click: visibility: '{video.privacy.upper()}'")
            privacy_button = self.browser.find(By.NAME, video.privacy.upper())
            self.browser.find(By.ID, Constant.RADIO_LABEL,
                              privacy_button).click()

        time.sleep(Constant.USER_WAITING_TIME)
        video_id = self.__get_video_id()
        print(f"Video link: https://youtu.be/{video_id}")

        container = self.browser.find(
            By.CSS_SELECTOR,
            ".left-button-area.style-scope.ytcp-uploads-dialog")

        while True:
            texts = [
                a.text for a in self.browser.find_all(
                    By.CLASS_NAME, "progress-label", container)
            ]
            print(f'\r{texts[-1]}\033[K', end="")
            if any(substring in element
                   for substring in {"complete", "dokončeno"}
                   for element in texts):
                print()
                time.sleep(Constant.USER_WAITING_TIME)
                break
            else:
                time.sleep(0.5)

        print("Click: done")
        done_button = self.browser.find(By.ID, Constant.DONE_BUTTON)

        # Catch such error as
        # "File is a duplicate of a video you have already uploaded"
        if done_button.get_attribute('aria-disabled') == 'true':
            error_message = self.browser.find(By.XPATH,
                                              Constant.ERROR_CONTAINER).text
            print(f"Upload ERROR: {error_message}")
            return False, None

        done_button.click()
        self.browser.get(Constant.YOUTUBE_URL)
        return True, video_id

    def __get_video_id(self) -> Optional[str]:
        video_id = None

        try:
            video_url_container = self.browser.find(
                By.XPATH, Constant.VIDEO_URL_CONTAINER)
            video_url_element = self.browser.find(By.XPATH,
                                                  Constant.VIDEO_URL_ELEMENT,
                                                  element=video_url_container)
            video_id = video_url_element.get_attribute(
                Constant.HREF).split('/')[-1]
        except:
            print(f"ERROR: could not find video_id")

        return video_id
Ejemplo n.º 6
0
class YouTubeUploader:
    """A class for uploading videos on YouTube via Selenium using metadata JSON file
    to extract its title, description etc"""

    def __init__(self, video_path: str, metadata_json_path: Optional[str] = None, thumbnail_path: Optional[str] = None) -> None:
        self.video_path = video_path
        self.thumbnail_path = thumbnail_path
        self.metadata_dict = load_metadata(metadata_json_path)
        current_working_dir = str(Path.cwd())
        self.browser = Firefox(current_working_dir, current_working_dir)
        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(logging.DEBUG)
        self.__validate_inputs()

    def __validate_inputs(self):
        if not self.metadata_dict[Constant.VIDEO_TITLE]:
            self.logger.warning("The video title was not found in a metadata file")
            self.metadata_dict[Constant.VIDEO_TITLE] = Path(self.video_path).stem
            self.logger.warning("The video title was set to 7777 {}".format(Path(self.video_path).stem))
        if not self.metadata_dict[Constant.VIDEO_DESCRIPTION]:
            self.logger.warning("The video description was not found in a metadata file")

# selenium.common.exceptions.WebDriverException: Message: Reached error page: about:neterror?e=nssFailure2&u=https%3A//www.youtube.com/&c=UTF-8&d=%E8%BD%BD%E5%85%A5%E9%A1%B5%E9%9D%A2%E6%97%B6%E4%B8%8E%20www.youtube.com%20%E7%9A%84%E8%BF%9E%E6%8E%A5%E4%B8%AD%E6%96%AD%E3%80%82
    def upload(self):
        try:
            self.__login()
            return self.__upload()
        except Exception as e:
            print(e)
            self.__quit()
            raise

    def __login(self):
        self.browser.get(Constant.YOUTUBE_URL)
        time.sleep(Constant.USER_WAITING_TIME)

        if self.browser.has_cookies_for_current_website():
            self.browser.load_cookies()
            time.sleep(Constant.USER_WAITING_TIME)
            self.browser.refresh()
        else:
            self.logger.info('Please sign in and then press enter')
            input()
            self.browser.get(Constant.YOUTUBE_URL)
            time.sleep(Constant.USER_WAITING_TIME)
            self.browser.save_cookies()

    def __write_in_field(self, field, string, select_all=False):
        field.click()
        time.sleep(Constant.USER_WAITING_TIME)
        if select_all:
            field.send_keys(Keys.COMMAND + 'a')
            time.sleep(Constant.USER_WAITING_TIME)
        field.send_keys(string)

    def __upload(self) -> (bool, Optional[str]):
        self.browser.get(Constant.YOUTUBE_URL)
        time.sleep(Constant.USER_WAITING_TIME)
        self.browser.get(Constant.YOUTUBE_UPLOAD_URL)
        time.sleep(Constant.USER_WAITING_TIME)
        absolute_video_path = str(Path.cwd() / self.video_path)
        self.browser.find(By.XPATH, Constant.INPUT_FILE_VIDEO).send_keys(absolute_video_path)
        self.logger.debug('Attached video {}'.format(self.video_path))

        if self.thumbnail_path is not None:
            absolute_thumbnail_path = str(Path.cwd() / self.thumbnail_path)
            self.browser.find(By.XPATH, Constant.INPUT_FILE_THUMBNAIL).send_keys(absolute_thumbnail_path)
            change_display = "document.getElementById('file-loader').style = 'display: block! important'"
            self.browser.driver.execute_script(change_display)
            self.logger.debug('Attached thumbnail {}'.format(self.thumbnail_path))

        title_field = self.browser.find(By.ID, Constant.TEXTBOX, timeout=10)
        self.__write_in_field(title_field, self.metadata_dict[Constant.VIDEO_TITLE], select_all=True)
        self.logger.debug('The video title was set to \"{}\"'.format(self.metadata_dict[Constant.VIDEO_TITLE]))

        video_description = self.metadata_dict[Constant.VIDEO_DESCRIPTION]
        if video_description:
            description_container = self.browser.find(By.XPATH,
                                                      Constant.DESCRIPTION_CONTAINER)
            description_field = self.browser.find(By.ID, Constant.TEXTBOX, element=description_container)
            self.__write_in_field(description_field, self.metadata_dict[Constant.VIDEO_DESCRIPTION])
            self.logger.debug(
                'The video description was set to \"{}\"'.format(self.metadata_dict[Constant.VIDEO_DESCRIPTION]))

        kids_section = self.browser.find(By.NAME, Constant.NOT_MADE_FOR_KIDS_LABEL)
        self.browser.find(By.ID, Constant.RADIO_LABEL, kids_section).click()
        self.logger.debug('Selected \"{}\"'.format(Constant.NOT_MADE_FOR_KIDS_LABEL))

        # Advanced options
        # self.browser.find(By.XPATH, Constant.MORE_BUTTON).click()
        # self.logger.debug('Clicked MORE OPTIONS')

        # tags_container = self.browser.find(By.XPATH,
        #                                             Constant.TAGS_INPUT_CONTAINER)
        # tags_field = self.browser.find(By.ID, Constant.TAGS_INPUT, element=tags_container)
        # self.__write_in_field(tags_field, ','.join(self.metadata_dict[Constant.VIDEO_TAGS]))
        # self.logger.debug(
        #     'The tags were set to \"{}\"'.format(self.metadata_dict[Constant.VIDEO_TAGS]))

        self.browser.find(By.ID, Constant.NEXT_BUTTON).click()
        self.logger.debug('Clicked {}'.format(Constant.NEXT_BUTTON))

        self.browser.find(By.ID, Constant.NEXT_BUTTON).click()
        self.logger.debug('Clicked element another {}'.format(Constant.NEXT_BUTTON))


        self.browser.find(By.ID, Constant.NEXT_BUTTON).click()
        self.logger.debug('Clicked checking another {}'.format(Constant.NEXT_BUTTON))

        public_main_button = self.browser.find(By.NAME, Constant.PUBLIC_BUTTON)
        self.browser.find(By.ID, Constant.RADIO_LABEL, public_main_button).click()
        self.logger.debug('Made the video {}'.format(Constant.PUBLIC_BUTTON))

        video_id = self.__get_video_id()

        status_container = self.browser.find(By.XPATH,
                                             Constant.STATUS_CONTAINER)
        while True:
            in_process = status_container.text.find(Constant.UPLOADED) != -1
            if in_process:
                time.sleep(Constant.USER_WAITING_TIME)
            else:
                break

        done_button = self.browser.find(By.ID, Constant.DONE_BUTTON)

        # Catch such error as
        # "File is a duplicate of a video you have already uploaded"
        if done_button.get_attribute('aria-disabled') == 'true':
            error_message = self.browser.find(By.XPATH,
                                              Constant.ERROR_CONTAINER).text
            self.logger.error(error_message)
            return False, None

        done_button.click()
        self.logger.debug("Published the video with video_id = {}".format(video_id))
        time.sleep(Constant.USER_WAITING_TIME)
        self.browser.get(Constant.YOUTUBE_URL)
        self.__quit()
        return True, video_id

    def __get_video_id(self) -> Optional[str]:
        video_id = None
        try:
            video_url_container = self.browser.find(By.XPATH, Constant.VIDEO_URL_CONTAINER)
            video_url_element = self.browser.find(By.XPATH, Constant.VIDEO_URL_ELEMENT,
                                                  element=video_url_container)
            video_id = video_url_element.get_attribute(Constant.HREF).split('/')[-1]
        except:
            self.logger.warning(Constant.VIDEO_NOT_FOUND_ERROR)
            pass
        return video_id

    def __quit(self):
        self.browser.driver.quit()
Ejemplo n.º 7
0
class YouTubeUploader:
    """A class for uploading videos on YouTube via Selenium using metadata JSON file
    to extract its title, description etc"""
    def __init__(self,
                 video_path: str,
                 metadata_json_path: Optional[str] = None,
                 thumbnail_path: Optional[str] = None) -> None:
        self.video_path = video_path
        self.thumbnail_path = thumbnail_path
        self.metadata_dict = load_metadata(metadata_json_path)
        self.current_working_dir = str(Path.cwd())
        self.browser = Firefox(self.current_working_dir,
                               self.current_working_dir,
                               headless=True,
                               geckodriver_path="/usr/local/bin/geckodriver")
        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(logging.DEBUG)
        self.__validate_inputs()

    def __validate_inputs(self):
        if not self.metadata_dict[Constant.VIDEO_TITLE]:
            self.logger.warning(
                "The video title was not found in a metadata file")
            self.metadata_dict[Constant.VIDEO_TITLE] = Path(
                self.video_path).stem
            self.logger.warning("The video title was set to {}".format(
                Path(self.video_path).stem))
        if not self.metadata_dict[Constant.VIDEO_DESCRIPTION]:
            self.logger.warning(
                "The video description was not found in a metadata file")

    def upload(self):
        try:
            self.__login()
            return self.__upload()
        except Exception as e:
            print(e)
            self.__quit()
            raise

    def __login(self):
        self.browser.get(Constant.YOUTUBE_URL)
        time.sleep(Constant.USER_WAITING_TIME)

        if self.browser.has_cookies_for_current_website():
            self.browser.load_cookies()
            time.sleep(Constant.USER_WAITING_TIME)
            self.browser.refresh()
            time.sleep(Constant.USER_WAITING_TIME)
            if not self.browser.find(By.XPATH, Constant.YOUTUBE_SIGNIN_BUTTON):
                return

        self.logger.debug(
            'Couldnt find cookies. attempting login via automation')
        self.logger.debug('Clicking sign in button on top right corner')
        self.browser.driver.get_screenshot_as_file('/tmp/ss1.png')
        self.browser.find(By.XPATH, Constant.YOUTUBE_SIGNIN_BUTTON).click()
        time.sleep(Constant.USER_WAITING_TIME)
        self.logger.debug('Attempting to fill email')
        self.browser.driver.get_screenshot_as_file('/tmp/ss2.png')
        self.browser.find(By.XPATH,
                          Constant.GOOGLE_SIGNIN_CARD_EMAIL).send_keys(
                              os.getenv("YOUTUBE_USER_EMAIL"))
        time.sleep(Constant.USER_WAITING_TIME)
        self.logger.debug('Attempting to click next')
        self.browser.driver.get_screenshot_as_file('/tmp/ss3.png')
        self.browser.find(By.XPATH,
                          Constant.GOOGLE_SIGNIN_CARD_EMAIL_NEXT).click()
        time.sleep(Constant.USER_WAITING_TIME)
        self.logger.debug('Attempting to fill password')
        self.browser.driver.get_screenshot_as_file('/tmp/ss4.png')
        self.browser.find(By.XPATH,
                          Constant.GOOGLE_SIGNIN_CARD_PASSWORD).send_keys(
                              os.getenv("YOUTUBE_USER_PASS"))
        time.sleep(Constant.USER_WAITING_TIME)
        self.logger.debug('Attempting to go all in !')
        self.browser.driver.get_screenshot_as_file('/tmp/ss5.png')
        self.browser.find(By.XPATH,
                          Constant.GOOGLE_SIGNIN_CARD_PASSWORD_NEXT).click()
        self.browser.driver.get_screenshot_as_file('/tmp/ss6.png')
        self.logger.debug(
            'Attempting to find Channel Avatar button after signin, on top right corner'
        )
        current_ticker = 0
        while current_ticker <= Constant.GOOGLE_SIGNIN_ACCEPTANCE_TIME:
            if self.browser.find(By.XPATH,
                                 Constant.YOUTUBE_CHANNEL_AVATAR_BUTTON):
                self.logger.debug('Found it! saving youtube cookies...')
                break
            self.logger.debug('Sleeping...')
            time.sleep(1)
            current_ticker += 1
        self.browser.save_cookies()

    def __write_in_field(self, field, string, select_all=False):
        field.click()
        time.sleep(Constant.USER_WAITING_TIME)
        if select_all:
            field.send_keys(Keys.COMMAND + 'a')
            time.sleep(Constant.USER_WAITING_TIME)
        field.send_keys(string)

    def __upload(self) -> (bool, Optional[str]):
        self.browser.get(Constant.YOUTUBE_URL)
        time.sleep(Constant.USER_WAITING_TIME)
        self.browser.get(Constant.YOUTUBE_UPLOAD_URL)
        time.sleep(Constant.USER_WAITING_TIME)
        absolute_video_path = str(Path.cwd() / self.video_path)
        self.browser.find(
            By.XPATH, Constant.INPUT_FILE_VIDEO).send_keys(absolute_video_path)
        self.logger.debug('Attached video {}'.format(self.video_path))

        if self.thumbnail_path is not None:
            absolute_thumbnail_path = str(Path.cwd() / self.thumbnail_path)
            time.sleep(Constant.USER_WAITING_TIME_LONG)
            self.browser.find(By.XPATH,
                              Constant.INPUT_FILE_THUMBNAIL).send_keys(
                                  absolute_thumbnail_path)
            change_display = "document.getElementById('file-loader').style = 'display: block! important'"
            self.browser.driver.execute_script(change_display)
            time.sleep(Constant.USER_WAITING_TIME_LONG)
            self.logger.debug('Attached thumbnail {}'.format(
                self.thumbnail_path))

        # title_field = self.browser.find(By.ID, Constant.TEXTBOX, timeout=10)
        # self.__write_in_field(title_field, self.metadata_dict[Constant.VIDEO_TITLE], select_all=True)
        # self.logger.debug('The video title was set to \"{}\"'.format(self.metadata_dict[Constant.VIDEO_TITLE]))

        # video_description = self.metadata_dict[Constant.VIDEO_DESCRIPTION]
        # if video_description:
        #     description_container = self.browser.find(By.XPATH,
        #                                               Constant.DESCRIPTION_CONTAINER)
        #     description_field = self.browser.find(By.ID, Constant.TEXTBOX, element=description_container)
        #     self.__write_in_field(description_field, self.metadata_dict[Constant.VIDEO_DESCRIPTION])
        #     self.logger.debug(
        #         'The video description was set to \"{}\"'.format(self.metadata_dict[Constant.VIDEO_DESCRIPTION]))

        # kids_section = self.browser.find(By.NAME, Constant.NOT_MADE_FOR_KIDS_LABEL)
        # self.browser.find(By.ID, Constant.RADIO_LABEL, kids_section).click()
        # self.logger.debug('Selected \"{}\"'.format(Constant.NOT_MADE_FOR_KIDS_LABEL))

        # Advanced options

        # self.browser.find(By.XPATH, Constant.MORE_BUTTON).click()
        # self.logger.debug('Clicked MORE OPTIONS')

        # tags_container = self.browser.find(By.XPATH,
        #                                             Constant.TAGS_INPUT_CONTAINER)
        # tags_field = self.browser.find(By.ID, Constant.TAGS_INPUT, element=tags_container)
        # self.__write_in_field(tags_field, ','.join(self.metadata_dict[Constant.VIDEO_TAGS]))
        # self.logger.debug(
        #     'The tags were set to \"{}\"'.format(self.metadata_dict[Constant.VIDEO_TAGS]))

        # self.browser.find(By.ID, Constant.NEXT_BUTTON).click()
        # self.logger.debug('Clicked {}'.format(Constant.NEXT_BUTTON))

        # self.browser.find(By.ID, Constant.NEXT_BUTTON).click()
        # self.logger.debug('Clicked another {}'.format(Constant.NEXT_BUTTON))

        # public_main_button = self.browser.find(By.NAME, Constant.PRIVATE_BUTTON)
        # self.browser.find(By.ID, Constant.RADIO_LABEL, public_main_button).click()
        # self.logger.debug('Made the video {}'.format(Constant.PRIVATE_BUTTON))

        video_id = self.__get_video_id()

        status_container = self.browser.find(By.XPATH,
                                             Constant.STATUS_CONTAINER)
        while True:
            in_process = status_container.text.find(Constant.UPLOADED) != -1
            if in_process:
                time.sleep(Constant.USER_WAITING_TIME)
            else:
                break
        self.logger.debug('Video uploaded with video_id = {}'.format(video_id))

        # done_button = self.browser.find(By.ID, Constant.DONE_BUTTON)

        # Catch such error as
        # "File is a duplicate of a video you have already uploaded"
        # if done_button.get_attribute('aria-disabled') == 'true':
        #     error_message = self.browser.find(By.XPATH,
        #                                       Constant.ERROR_CONTAINER).text
        #     self.logger.error(error_message)
        #     return False, None

        # done_button.click()
        # self.logger.debug("Published the video with video_id = {}".format(video_id))
        # time.sleep(Constant.USER_WAITING_TIME)
        self.browser.get(Constant.YOUTUBE_URL)
        self.__quit()
        return True, video_id

    def __get_video_id(self) -> Optional[str]:
        video_id = None
        try:
            video_url_container = self.browser.find(
                By.XPATH, Constant.VIDEO_URL_CONTAINER)
            video_url_element = self.browser.find(By.XPATH,
                                                  Constant.VIDEO_URL_ELEMENT,
                                                  element=video_url_container)
            video_id = video_url_element.get_attribute(
                Constant.HREF).split('/')[-1]
        except:
            self.logger.warning(Constant.VIDEO_NOT_FOUND_ERROR)
            pass
        return video_id

    def __quit(self):
        self.browser.driver.quit()