Esempio n. 1
0
    def process_entry(self, e_hash: str,
                      entry: Any) -> Tuple[str, Optional[Video]]:
        with Database(self.db_path) as database:
            if database.get_extractor_fail_count(e_hash) >= self.max_fail:
                return e_hash, None

        with youtube_dl.YoutubeDL(self.ydl_opts) as ydl:
            try:
                processed = ydl.process_ie_result(entry, False)
            except youtube_dl.DownloadError as download_error:
                logging.warning("Failed to get a video. Youtube-dl said: '%s'",
                                download_error)
                return e_hash, None
            else:
                publish_date = 0.0
                date_str = processed.get("upload_date")
                if date_str:
                    publish_date = datetime.datetime.strptime(
                        date_str, "%Y%m%d").timestamp()

                if processed.get("age_limit", 0) > config.ytcc.age_limit:
                    logger.warning("Ignoring video '%s' due to age limit",
                                   processed.get("title"))
                    return e_hash, None

                logger.info("Processed video '%s'", processed.get("title"))

                return e_hash, Video(url=processed["webpage_url"],
                                     title=processed["title"],
                                     description=processed.get(
                                         "description", ""),
                                     publish_date=publish_date,
                                     watch_date=None,
                                     duration=processed.get("duration", -1),
                                     extractor_hash=e_hash)
Esempio n. 2
0
    def test_resolve_video_id(self):
        db = init_db()
        video = db.resolve_video_id(1)
        expected = Video(id=1, yt_videoid="0", title="title1", description="description1", publisher="id_publisher1",
                         publish_date=1488286166.0, watched=False)

        self.eq_video(video, expected)
Esempio n. 3
0
    def play_video(self, video: Video, audio_only: bool = False) -> bool:
        """Play the given video with the mpv video player and mark the the video as watched.

        The video will not be marked as watched, if the player exits unexpectedly (i.e. exits with
        non-zero exit code) or another error occurs.

        :param video: The video to play.
        :param audio_only: If True, only the audio track of the video is played
        :return: False if the given video_id does not exist or the player closed with a non-zero
         exit code. True if the video was played successfully.
        """
        no_video_flag = []
        if audio_only:
            no_video_flag.append("--no-video")

        if video:
            try:
                command = [
                    "mpv", *no_video_flag, *self.config.mpv_flags,
                    self.get_youtube_video_url(video.yt_videoid)
                ]
                subprocess.run(command, check=True)
            except FileNotFoundError:
                raise YtccException("Could not locate the mpv video player!")
            except subprocess.CalledProcessError:
                return False

            video.watched = True
            return True

        return False
Esempio n. 4
0
    def download_video(self, video: Video, path: str = "", audio_only: bool = False) -> bool:
        """Download the given video with youtube-dl and mark it as watched.

        If the path is not given, the path is read from the config file.

        :param video: The video to download.
        :param path: The directory where the download is saved.
        :param audio_only: If True, only the audio track is downloaded.
        :return: True, if the video was downloaded successfully. False otherwise.
        """
        if path:
            download_dir = path
        elif self.config.download_dir:
            download_dir = self.config.download_dir
        else:
            download_dir = ""

        conf = self.config.youtube_dl

        ydl_opts: Dict[str, Any] = {
            "outtmpl": os.path.join(download_dir, conf.output_template),
            "ratelimit": conf.ratelimit,
            "retries": conf.retries,
            "quiet": conf.loglevel == "quiet",
            "verbose": conf.loglevel == "verbose",
            "ignoreerrors": False
        }

        if audio_only:
            ydl_opts["format"] = "bestaudio/best"
            if conf.thumbnail:
                ydl_opts["writethumbnail"] = True
                ydl_opts["postprocessors"] = [
                    {
                        'key': 'FFmpegExtractAudio',
                        'preferredcodec': "m4a"
                    },
                    {"key": "EmbedThumbnail"}
                ]
        else:
            ydl_opts["format"] = conf.format
            if conf.subtitles != "off":
                ydl_opts["subtitleslangs"] = list(map(str.strip, conf.subtitles.split(",")))
                ydl_opts["writesubtitles"] = True
                ydl_opts["writeautomaticsub"] = True
                ydl_opts["postprocessors"] = [{"key": "FFmpegEmbedSubtitle"}]

        with youtube_dl.YoutubeDL(ydl_opts) as ydl:
            url = self.get_youtube_video_url(video.yt_videoid)
            try:
                info = ydl.extract_info(url, download=False, process=False)
                if info.get("is_live", False) and conf.skip_live_stream:
                    return False

                ydl.process_ie_result(info, download=True)
                video.watched = True
                return True
            except youtube_dl.utils.YoutubeDLError:
                return False
Esempio n. 5
0
 def test_mark_watched(self):
     db = init_db()
     for video in db.resolve_video_ids([2, 3]):
         video.watched = True
     videos = db.session.query(Video).filter(Video.watched == False).all()
     expected = Video(id=1, yt_videoid="0", title="title1", description="description1", publisher="id_publisher1",
                      publish_date=1488286166.0, watched=False)
     self.assertEqual(len(videos), 1)
     self.eq_video(videos[0], expected)
Esempio n. 6
0
def init_db():
    insert_list = [
        Video(yt_videoid="0", title="title1", description="description1", publisher="id_publisher1", publish_date=1488286166,
              watched=False),
        Video(yt_videoid="0", title="title1", description="description1", publisher="id_publisher1", publish_date=1488286167,
              watched=False),
        Video(yt_videoid="1", title="title2", description="description1", publisher="id_publisher1", publish_date=1488286168,
              watched=False),
        Video(yt_videoid="1", title="title2", description="description2", publisher="id_publisher2", publish_date=1488286170,
              watched=False),
        Video(yt_videoid="2", title="title3", description="description3", publisher="id_publisher2", publish_date=1488286171,
              watched=False)
    ]
    db = Database(":memory:")
    db.add_channel(Channel(displayname="publisher1", yt_channelid="id_publisher1"))
    db.add_channel(Channel(displayname="publisher2", yt_channelid="id_publisher2"))
    db.add_channel(Channel(displayname="publisher3", yt_channelid="id_publisher3"))
    db.add_videos(insert_list)
    return db
Esempio n. 7
0
 def _update_channel(channel: Channel) -> List[Video]:
     yt_channel_id = channel.yt_channelid
     url = _get_youtube_rss_url(yt_channel_id)
     feed = feedparser.parse(url)
     return [
         Video(yt_videoid=str(entry.yt_videoid),
               title=str(entry.title),
               description=str(entry.description),
               publisher=yt_channel_id,
               publish_date=time.mktime(entry.published_parsed),
               watched=False) for entry in feed.entries
     ]
Esempio n. 8
0
 def _update_channel(channel: Channel) -> List[Video]:
     yt_channel_id = channel.yt_channelid
     url = f"https://www.youtube.com/feeds/videos.xml?channel_id={yt_channel_id}"
     feed = feedparser.parse(url)
     return [
         Video(
             yt_videoid=str(entry.yt_videoid),
             title=str(entry.title),
             description=str(entry.description),
             publisher=yt_channel_id,
             publish_date=time.mktime(entry.published_parsed),
             watched=False
         )
         for entry in feed.entries
     ]
Esempio n. 9
0
    def setUp(self):
        self.current_dir = os.path.dirname(__file__)
        self.ytcc = Ytcc(os.path.join(self.current_dir, "data/ytcc_test.conf"))
        self.db_conn = self.ytcc.database

        insert_list = [
            Video(yt_videoid="V-ozGFl3Jks",
                  title="tmptYnCut",
                  description="",
                  publisher="UCsLiV4WJfkTEHH0b9PmRklw",
                  publish_date=1488348731.0,
                  watched=True),
            Video(yt_videoid="a1gOeiyIqPs",
                  title="tmp99Yc1l",
                  description="",
                  publisher="UCsLiV4WJfkTEHH0b9PmRklw",
                  publish_date=1488348519.0,
                  watched=True),
            Video(yt_videoid="0ounUgOrcqo",
                  title="tmppfXKp6",
                  description="",
                  publisher="UCsLiV4WJfkTEHH0b9PmRklw",
                  publish_date=1488345630.0,
                  watched=True),
            Video(yt_videoid="7mckB-NdKWY",
                  title="tmpiM62pN",
                  description="",
                  publisher="UCsLiV4WJfkTEHH0b9PmRklw",
                  publish_date=1488345565.0,
                  watched=False),
            Video(yt_videoid="RmRPt93uAsQ",
                  title="tmpIXBgjd",
                  description="",
                  publisher="UCsLiV4WJfkTEHH0b9PmRklw",
                  publish_date=1488344217.0,
                  watched=False),
            Video(yt_videoid="nDPy3RyKdrg",
                  title="tmpwA0TjG",
                  description="",
                  publisher="UCsLiV4WJfkTEHH0b9PmRklw",
                  publish_date=1488343000.0,
                  watched=False),
            Video(yt_videoid="L0_F805qUIM",
                  title="tmpKDOkro",
                  description="",
                  publisher="UCxexYYtOetqikZqriLuTS-g",
                  publish_date=1488344253.0,
                  watched=True),
            Video(yt_videoid="lXWrdlDEzQs",
                  title="tmpEvCR4s",
                  description="",
                  publisher="UCxexYYtOetqikZqriLuTS-g",
                  publish_date=1488343152.0,
                  watched=True),
            Video(yt_videoid="cCnXsCQNkr8",
                  title="tmp1rpsWK",
                  description="",
                  publisher="UCxexYYtOetqikZqriLuTS-g",
                  publish_date=1488343046.0,
                  watched=True),
            Video(yt_videoid="rSxVs0XeQa4",
                  title="tmpc5Y2pd",
                  description="",
                  publisher="UCxexYYtOetqikZqriLuTS-g",
                  publish_date=1488342015.0,
                  watched=False),
            Video(yt_videoid="gQAsWrGfsrw",
                  title="tmpn1M1Oa",
                  description="",
                  publisher="UCxexYYtOetqikZqriLuTS-g",
                  publish_date=1488341324.0,
                  watched=False),
        ]
        self.db_conn.session.execute("delete from video")
        self.db_conn.session.execute("delete from channel")
        self.db_conn.add_channel(
            Channel(displayname="Webdriver Torso",
                    yt_channelid="UCsLiV4WJfkTEHH0b9PmRklw"))
        self.db_conn.add_channel(
            Channel(displayname="Webdriver YPP",
                    yt_channelid="UCxexYYtOetqikZqriLuTS-g"))
        self.db_conn.add_videos(insert_list)
        self.video = self.db_conn.session.query(Video).filter(
            Video.title == "tmpIXBgjd").one()
        self.video_id = self.video.id