Example #1
0
    def test_special_season_shouldnot_became_last_season(self):
        season1 = LostFilmSeason(SpecialSeasons.Additional)

        show = LostFilmShow('Show', u'Шоу', 'Show', 2017)

        assert show.last_season is None
        assert show.seasons_url == 'https://www.lostfilm.tv/series/Show/seasons'

        show.add_season(season1)

        assert show.last_season is None

        assert len(show) == 1

        assert show[SpecialSeasons.Additional] == season1

        assert list(show) == [season1]
        assert list(reversed(show)) == [season1]
Example #2
0
    def test_add_season_success(self):
        season1 = LostFilmSeason(1)
        season2 = LostFilmSeason(2)

        show = LostFilmShow('Show', u'Шоу', 'Show', 2017)

        assert show.last_season is None
        assert show.seasons_url == 'https://www.lostfilm.tv/series/Show/seasons'

        show.add_season(season1)
        show.add_season(season2)

        assert len(show) == 2

        assert show[1] == season1
        assert show[2] == season2
        assert show.last_season == season2

        assert list(show) == [season1, season2]
        assert list(reversed(show)) == [season2, season1]
Example #3
0
    def test_add_episode_failed(self):
        season1 = LostFilmSeason(2)

        show = LostFilmShow('Show', u'Шоу', 'Show', 2017)

        assert show.seasons_url == 'https://www.lostfilm.tv/series/Show/seasons'

        show.add_season(season1)
        with pytest.raises(Exception) as e:
            show.add_season(season1)
        message = six.text_type(e.value)
        assert six.text_type(season1.number) in message
        assert 'already' in message

        assert len(show) == 1

        assert show[2] == season1
        assert show.last_season == season1

        assert list(show) == [season1]
        assert list(reversed(show)) == [season1]
Example #4
0
 def test_get_seasons_url(self, url, expected_url):
     assert LostFilmShow.get_seasons_url(url) == expected_url
class TestLostFilmTrackerPlugin(ReadContentMixin, DbTestCase):
    def setUp(self):
        super(TestLostFilmTrackerPlugin, self).setUp()
        self.tracker_settings = TrackerSettings(10, None)
        self.plugin = LostFilmPlugin()
        self.plugin.init(self.tracker_settings)

    @use_vcr()
    def test_prepare_add_topic(self):
        settings = self.plugin.prepare_add_topic(
            'http://www.lostfilm.tv/series/12_Monkeys/seasons')
        assert settings['display_name'] == u'12 обезьян / 12 Monkeys'
        assert settings['quality'] == u'SD'

    @use_vcr()
    def test_add_topic(self):
        params = {
            'display_name': u'12 обезьян / 12 Monkeys',
            'quality': '720p'
        }
        assert self.plugin.add_topic(
            'http://www.lostfilm.tv/series/12_Monkeys/seasons', params)
        topic = self.plugin.get_topic(1)
        assert topic is not None
        assert topic[
            'url'] == 'https://www.lostfilm.tv/series/12_Monkeys/seasons'
        assert topic['display_name'] == params['display_name']
        assert topic['quality'] == params['quality']
        assert topic['season'] is None
        assert topic['episode'] is None

    @helper.use_vcr()
    def test_login_verify(self):
        assert not self.plugin.verify()
        assert self.plugin.login() == LoginResult.CredentialsNotSpecified

        credentials = {'username': '', 'password': ''}
        assert self.plugin.update_credentials(
            credentials) == LoginResult.CredentialsNotSpecified
        assert not self.plugin.verify()

        credentials = {'username': '******', 'password': '******'}
        assert self.plugin.update_credentials(
            credentials) == LoginResult.IncorrentLoginPassword
        assert not self.plugin.verify()

        credentials = {
            'username': helper.real_email,
            'password': helper.real_password
        }

        assert self.plugin.update_credentials(credentials) == LoginResult.Ok
        assert self.plugin.verify()

    def test_login_success(self):
        mock_tracker = LostFilmTVTracker()
        mock_tracker.tracker_settings = self.tracker_settings
        mock_tracker.c_uid = '123456'
        login_mock = Mock()
        mock_tracker.login = login_mock
        self.plugin.tracker = mock_tracker
        self.plugin.update_credentials({
            'username': '******',
            'password': '******'
        })
        assert self.plugin.login() == LoginResult.Ok

        login_mock.assert_called_with('monitorrent', 'monitorrent')

    def test_login_failed_incorrect_login_password(self):
        mock_tracker = LostFilmTVTracker()
        mock_tracker.tracker_settings = self.tracker_settings
        login_mock = Mock(side_effect=LostFilmTVLoginFailedException(3))
        mock_tracker.login = login_mock
        self.plugin.tracker = mock_tracker
        self.plugin.update_credentials({
            'username': '******',
            'password': '******'
        })
        assert self.plugin.login() == LoginResult.IncorrentLoginPassword

        login_mock.assert_called_with('monitorrent', 'monitorrent')

    def test_login_failed_unknown_1(self):
        mock_tracker = LostFilmTVTracker()
        mock_tracker.tracker_settings = self.tracker_settings
        login_mock = Mock(side_effect=LostFilmTVLoginFailedException(1))
        mock_tracker.login = login_mock
        self.plugin.tracker = mock_tracker
        self.plugin.update_credentials({
            'username': '******',
            'password': '******'
        })
        assert self.plugin.login() == LoginResult.Unknown

        login_mock.assert_called_with('monitorrent', 'monitorrent')

    def test_login_failed_unknown_2(self):
        mock_tracker = LostFilmTVTracker()
        login_mock = Mock(side_effect=Exception)
        mock_tracker.login = login_mock
        self.plugin.tracker = mock_tracker
        self.plugin.update_credentials({
            'username': '******',
            'password': '******'
        })
        assert self.plugin.login() == LoginResult.Unknown

        login_mock.assert_called_with('monitorrent', 'monitorrent')

    def test_check_download(self):
        tracker = LostFilmPlugin()

        response = Response()

        response.status_code = 200
        assert tracker.check_download(response) == Status.Ok

        response.status_code = 302
        response.headers['Location'] = '/'
        assert tracker.check_download(response) == Status.NotFound

        response.status_code = 200
        response._content = (
            '<!--\r\n'
            '<meta http-equiv="refresh" content="0; url=/">;\r\n'
            '//-->').encode('utf-8')
        assert tracker.check_download(response) == Status.NotFound

        response.status_code = 500
        response.headers['Location'] = '/'
        # Should be error even with Location header
        assert tracker.check_download(response) == Status.Error

    @data(('http://www.lostfilm.tv/series/12_Monkeys', True),
          ('http://www.lostfilm.tv/browse.php?cat=236', False),
          ('http://www.lostfilm.tv/my.php', False))
    @unpack
    def test_can_parse_url(self, url, value):
        assert self.plugin.can_parse_url(url) == value

    @use_vcr
    def test_prepare_add_topic_success(self):
        result = self.plugin.prepare_add_topic(
            'http://www.lostfilm.tv/series/12_Monkeys/seasons')

        assert result == {
            'display_name': u'12 обезьян / 12 Monkeys',
            'quality': 'SD'
        }

    @data('SD', '720p', '1080p')
    @use_vcr
    def test_prepare_add_topic_success_2(self, quality):
        self.plugin.update_credentials({
            'username': '******',
            'password': '******',
            'default_quality': quality
        })
        result = self.plugin.prepare_add_topic(
            'http://www.lostfilm.tv/series/12_Monkeys/seasons')

        assert result == {
            'display_name': u'12 обезьян / 12 Monkeys',
            'quality': quality
        }

    @use_vcr
    def test_prepare_add_topic_fail(self):
        assert self.plugin.prepare_add_topic(
            'http://www.lostfilm.tv/series/Unknown') is None

    @requests_mock.Mocker()
    def test_execute_download_all_update_to_latest_success(self, mocker):
        """
        :type mocker: requests_mock.Mocker
        """
        file_name = 'Hell.On.Wheels.S05E02.720p.WEB.rus.LostFilm.TV.mp4.torrent'
        torrent_body = self.read_httpretty_content(file_name, 'rb')
        # Mr. Robot series
        mocker.get('https://www.lostfilm.tv/series/Mr_Robot/seasons',
                   text=self.read_httpretty_content('Series_Mr_Robot.html',
                                                    encoding='utf-8'))
        mocker.get('https://www.lostfilm.tv/v_search.php?c=245&s=2&e=12',
                   text=self.read_httpretty_content(
                       'v_search.php_c=245&s=2&e=12.html', encoding='utf-8'))
        mocker.get('https://www.lostfilm.tv/v_search.php?c=245&s=2&e=11',
                   text=self.read_httpretty_content(
                       'v_search.php_c=245&s=2&e=11.html', encoding='utf-8'))
        mocker.get(re.compile(
            u'http://retre.org/v3/(index\.php)?\?c=245&s=2&e=12&u=\d+&h=[a-z0-9]+&n=\d+'
        ),
                   text=self.read_httpretty_content(
                       'reTre.org_v3_c=245&s=2&e=12.html', encoding='utf-8'))
        mocker.get(re.compile(
            u'http://retre.org/v3/(index\.php)?\?c=245&s=2&e=11&u=\d+&h=[a-z0-9]+&n=\d+'
        ),
                   text=self.read_httpretty_content(
                       'reTre.org_v3_c=245&s=2&e=11.html', encoding='utf-8'))

        # Scream series
        mocker.get('https://www.lostfilm.tv/series/Scream/seasons',
                   text=self.read_httpretty_content('Series_Scream.html',
                                                    encoding='utf-8'))
        mocker.get('https://www.lostfilm.tv/v_search.php?c=251&s=2&e=13',
                   text=self.read_httpretty_content(
                       'v_search.php_c=245&s=2&e=11.html', encoding='utf-8'))
        mocker.get(re.compile(
            u'http://retre.org/v3/(index\.php)?\?c=251&s=2&e=13&u=\d+&h=[a-z0-9]+&n=\d+'
        ),
                   text=self.read_httpretty_content(
                       'reTre.org_v3_c=251&s=2&e=13.html', encoding='utf-8'))

        # tracktor.in download all files
        mocker.get(re.compile('http://tracktor.in/td.php(\?s=.*)?'),
                   content=torrent_body,
                   headers={
                       'content-disposition':
                       'attachment; filename=' + file_name
                   })

        self.plugin.tracker.setup(helper.real_session)
        self.plugin._execute_login = Mock(return_value=True)

        self._add_topic("https://www.lostfilm.tv/series/Mr_Robot/seasons",
                        u'Мистер Робот / Mr. Robot', 'Mr. Robot', 245, '720p',
                        2, 10)
        self._add_topic("https://www.lostfilm.tv/series/Scream/seasons",
                        u'Крик / Scream', 'Scream', 251, '720p', 2, 12)

        # noinspection PyTypeChecker
        self.plugin.execute(self.plugin.get_topics(None), EngineMock())

        topic1 = self.plugin.get_topic(1)
        topic2 = self.plugin.get_topic(2)

        assert topic1['season'] == 2
        assert topic1['episode'] == 12

        assert topic2['season'] == 2
        assert topic2['episode'] == 13

    @requests_mock.Mocker()
    def test_execute_cant_download_latest_episode_and_download_without_filename(
            self, mocker):
        """
        :type mocker: requests_mock.Mocker
        """
        file_name = 'Hell.On.Wheels.S05E02.720p.WEB.rus.LostFilm.TV.mp4.torrent'
        torrent_body = self.read_httpretty_content(file_name, 'rb')
        # Mr. Robot series
        mocker.get('https://www.lostfilm.tv/series/Mr_Robot/seasons',
                   text=self.read_httpretty_content('Series_Mr_Robot.html',
                                                    encoding='utf-8'))
        mocker.get('https://www.lostfilm.tv/v_search.php?c=245&s=2&e=12',
                   text=self.read_httpretty_content(
                       'v_search.php_c=245&s=2&e=12.html', encoding='utf-8'))
        mocker.get('https://www.lostfilm.tv/v_search.php?c=245&s=2&e=11',
                   text=self.read_httpretty_content(
                       'v_search.php_c=245&s=2&e=11.html', encoding='utf-8'))
        mocker.get(re.compile(
            u'http://retre.org/v3/(index\.php)?\?c=245&s=2&e=12&u=\d+&h=[a-z0-9]+&n=\d+'
        ),
                   text=self.read_httpretty_content(
                       'reTre.org_v3_c=245&s=2&e=12.html', encoding='utf-8'))
        mocker.get(re.compile(
            u'http://retre.org/v3/(index\.php)?\?c=245&s=2&e=11&u=\d+&h=[a-z0-9]+&n=\d+'
        ),
                   text=self.read_httpretty_content(
                       'reTre.org_v3_c=245&s=2&e=11.html', encoding='utf-8'))

        # Scream series
        mocker.get('https://www.lostfilm.tv/series/Scream/seasons',
                   text=self.read_httpretty_content('Series_Scream.html',
                                                    encoding='utf-8'))
        mocker.get('https://www.lostfilm.tv/v_search.php?c=251&s=2&e=13',
                   text=self.read_httpretty_content(
                       'v_search.php_c=245&s=2&e=11.html', encoding='utf-8'))
        mocker.get(re.compile(
            u'http://retre.org/v3/(index\.php)?\?c=251&s=2&e=13&u=\d+&h=[a-z0-9]+&n=\d+'
        ),
                   text=self.read_httpretty_content(
                       'reTre.org_v3_c=251&s=2&e=13.html', encoding='utf-8'))

        # tracktor.in download all files
        mocker.get(re.compile('http://tracktor.in/td.php(\?s=.*)?'),
                   content=torrent_body,
                   headers={
                       'content-disposition':
                       'attachment; filename=' + file_name
                   })

        # with filename
        mocker.get(re.compile(
            re.escape('http://tracktor.in/td.php?s=c245s2e11q720')),
                   content=torrent_body,
                   headers={
                       'content-disposition':
                       'attachment; filename=' + file_name
                   })
        mocker.get(re.compile(
            re.escape('http://tracktor.in/td.php?s=c245s2e12q720')),
                   text="Not Found",
                   status_code=404)
        # without filename
        mocker.get(re.compile(
            re.escape('http://tracktor.in/td.php?s=c251s2e13q720')),
                   content=torrent_body)

        self.plugin.tracker.setup(helper.real_session)
        self.plugin._execute_login = Mock(return_value=True)

        self._add_topic("http://www.lostfilm.tv/series/Mr_Robot/seasons",
                        u'Мистер Робот / Mr. Robot', 'Mr. Robot', 245, '720p',
                        2, 10)
        self._add_topic("http://www.lostfilm.tv/series/Scream/seasons",
                        u'Крик / Scream', 'Scream', 251, '720p', 2, 12)

        # noinspection PyTypeChecker
        self.plugin.execute(self.plugin.get_topics(None), EngineMock())

        topic1 = self.plugin.get_topic(1)
        topic2 = self.plugin.get_topic(2)

        assert topic1['season'] == 2
        assert topic1['episode'] == 11

        assert topic2['season'] == 2
        assert topic2['episode'] == 13

    @requests_mock.Mocker()
    def test_execute_nothing_changed(self, mocker):
        """
        :type mocker: requests_mock.Mocker
        """
        mocker.get('https://www.lostfilm.tv/series/Mr_Robot/seasons',
                   text=self.read_httpretty_content('Series_Mr_Robot.html',
                                                    encoding='utf-8'))
        mocker.get('https://www.lostfilm.tv/series/Scream/seasons',
                   text=self.read_httpretty_content('Series_Scream.html',
                                                    encoding='utf-8'))

        self.plugin.tracker.setup(helper.real_session)
        self.plugin._execute_login = Mock(return_value=True)

        self._add_topic("https://www.lostfilm.tv/series/Mr_Robot/seasons",
                        u'Мистер Робот / Mr. Robot', 'Mr. Robot', 245, '720p',
                        2, 12)
        self._add_topic("https://www.lostfilm.tv/series/Scream/seasons",
                        u'Крик / Scream', 'Scream', 251, '720p', 2, 13)

        # noinspection PyTypeChecker
        self.plugin.execute(self.plugin.get_topics(None), EngineMock())

        topic1 = self.plugin.get_topic(1)
        topic2 = self.plugin.get_topic(2)

        assert topic1['season'] == 2
        assert topic1['episode'] == 12

        assert topic2['season'] == 2
        assert topic2['episode'] == 13

    @requests_mock.Mocker()
    def test_execute_cant_get_quality(self, mocker):
        """
        :type mocker: requests_mock.Mocker
        """

        mocker.get(
            'https://www.lostfilm.tv/series/Legend_of_the_Seeker/seasons',
            text=self.read_httpretty_content(
                'Series_Legend_of_the_Seeker.html', encoding='utf-8'))

        mocker.get('https://www.lostfilm.tv/v_search.php?c=98&s=2&e=22',
                   text=self.read_httpretty_content(
                       'v_search.php_c=98&s=2&e=22.html', encoding='utf-8'))
        mocker.get(re.compile(
            u'http://retre.org/v3/(index\.php)?\?c=98&s=2&e=22&u=\d+&h=[a-z0-9]+&n=\d+'
        ),
                   text=self.read_httpretty_content(
                       'reTre.org_v3_c=98&s=2&e=22.html', encoding='utf-8'))

        self.plugin.tracker.setup(helper.real_session)
        self.plugin._execute_login = Mock(return_value=True)

        self._add_topic(
            u"https://www.lostfilm.tv/series/Legend_of_the_Seeker/seasons",
            u'Легенда об Искателе / Legend of the Seeker',
            u'Legend of the Seeker', 98, '720p', 2, 21)

        # noinspection PyTypeChecker
        self.plugin.execute(self.plugin.get_topics(None), EngineMock())

        topic1 = self.plugin.get_topic(1)

        assert topic1['season'] == 2
        assert topic1['episode'] == 21

    @requests_mock.Mocker()
    def test_execute_not_found_status(self, mocker):
        """
        :type mocker: requests_mock.Mocker
        """
        mocker.get('https://www.lostfilm.tv/series/Boardwalk_Empire/seasons',
                   status_code=200,
                   text=self.read_httpretty_content(
                       'lostfilm_redirect_to_root.html', encoding='utf-8'))

        self.plugin.tracker.setup(helper.real_session)
        self.plugin._execute_login = Mock(return_value=True)

        self._add_topic(
            u"https://www.lostfilm.tv/series/Boardwalk_Empire/seasons",
            u'Подпольная Империя / Broadwalk Empire', u'Broadwalk Empire', 131,
            '720p', 1, 12)

        # noinspection PyTypeChecker
        self.plugin.execute(self.plugin.get_topics(None), EngineMock())

        topic = self.plugin.get_topic(1)

        assert topic['season'] == 1
        assert topic['episode'] == 12
        assert topic['status'] == Status.NotFound

    @requests_mock.Mocker()
    def test_execute_download_html_should_fail(self, mocker):
        """
        :type mocker: requests_mock.Mocker
        """
        mocker.get(
            'https://www.lostfilm.tv/series/Legend_of_the_Seeker/seasons',
            text=self.read_httpretty_content(
                'Series_Legend_of_the_Seeker.html', encoding='utf-8'))

        mocker.get('https://www.lostfilm.tv/v_search.php?c=98&s=2&e=22',
                   text=self.read_httpretty_content(
                       'v_search.php_c=98&s=2&e=22.html', encoding='utf-8'))
        mocker.get(re.compile(
            u'http://retre.org/v3/(index\.php)?\?c=98&s=2&e=22&u=\d+&h=[a-z0-9]+&n=\d+'
        ),
                   text=self.read_httpretty_content(
                       'reTre.org_v3_c=98&s=2&e=22.html', encoding='utf-8'))
        mocker.get('http://tracktor.in/td.php?s=c98s2e22q480',
                   text='<html>HTML</html>')

        self.plugin.tracker.setup(helper.real_session)
        self.plugin._execute_login = Mock(return_value=True)

        self._add_topic(
            u"https://www.lostfilm.tv/series/Legend_of_the_Seeker/seasons",
            u'Легенда об Искателе / Legend of the Seeker',
            u'Legend of the Seeker', 98, 'SD', 2, 21)

        # noinspection PyTypeChecker
        self.plugin.execute(self.plugin.get_topics(None), EngineMock())

        topic1 = self.plugin.get_topic(1)

        assert topic1['season'] == 2
        assert topic1['episode'] == 21
        assert topic1['status'] == Status.Ok

    @requests_mock.Mocker()
    def test_execute_error_status(self, mocker):
        """
        :type mocker: requests_mock.Mocker
        """
        mocker.register_uri(
            requests_mock.GET,
            'https://www.lostfilm.tv/series/Legend_of_the_Seeker/seasons',
            status_code=500,
            text='<error>Backend Error</error>')

        self.plugin.tracker.setup(helper.real_session)
        self.plugin._execute_login = Mock(return_value=True)

        self._add_topic(
            u"https://www.lostfilm.tv/series/Legend_of_the_Seeker/seasons",
            u'Легенда об Искателе / Legend of the Seeker',
            u'Legend of the Seeker', 98, '720p', 2, 21)

        # noinspection PyTypeChecker
        self.plugin.execute(self.plugin.get_topics(None), EngineMock())

        topic1 = self.plugin.get_topic(1)

        assert topic1['season'] == 2
        assert topic1['episode'] == 21
        assert topic1['status'] == Status.Error

    @requests_mock.Mocker()
    def test_execute_download_latest_one_only(self, mocker):
        """
        :type mocker: requests_mock.Mocker
        """
        file_name = 'Hell.On.Wheels.S05E02.720p.WEB.rus.LostFilm.TV.mp4.torrent'
        torrent_body = self.read_httpretty_content(file_name, 'rb')

        self.plugin.tracker.setup(helper.real_session)
        self.plugin._execute_login = Mock(return_value=True)

        # Mr. Robot series
        mocker.get('https://www.lostfilm.tv/series/Mr_Robot/seasons',
                   text=self.read_httpretty_content('Series_Mr_Robot.html',
                                                    encoding='utf-8'))
        mocker.get('https://www.lostfilm.tv/v_search.php?c=245&s=2&e=12',
                   text=self.read_httpretty_content(
                       'v_search.php_c=245&s=2&e=12.html', encoding='utf-8'))
        mocker.get(re.compile(
            u'http://retre.org/v3/(index\.php)?\?c=245&s=2&e=12&u=\d+&h=[a-z0-9]+&n=\d+'
        ),
                   text=self.read_httpretty_content(
                       'reTre.org_v3_c=245&s=2&e=12.html', encoding='utf-8'))
        mocker.register_uri(requests_mock.GET,
                            'http://tracktor.in/td.php?s=c245s2e12q720',
                            content=torrent_body,
                            headers={
                                'content-disposition':
                                'attachment; filename=' + file_name
                            })

        self._add_topic("http://www.lostfilm.tv/series/Mr_Robot/seasons",
                        u'Мистер Робот / Mr. Robot', 'Mr. Robot', 245, '720p')

        # noinspection PyTypeChecker
        self.plugin.execute(self.plugin.get_topics(None), EngineMock())

        topic1 = self.plugin.get_topic(1)

        assert topic1['season'] == 2
        assert topic1['episode'] == 12

    def test_execute_login_failed(self):
        execute_login_mock = Mock(return_value=False)
        self.plugin._execute_login = execute_login_mock
        engine_mock = EngineMock()
        # noinspection PyTypeChecker
        self.plugin.execute(None, engine_mock)

        execute_login_mock.assert_called_with(engine_mock)

    @data((1, 10, 'S01E10'), (10, 9, 'S10E09'), (1, None, 'S01'),
          (11, None, 'S11'), (None, None, None))
    @unpack
    def test_get_topic_info(self, season, episode, expected):
        topic = LostFilmTVSeries(season=season, episode=episode)
        info = self.plugin.get_topic_info(topic)
        assert info == expected

    @data((LostFilmShow('Russian', u'Русский', "Russian",
                        666), u'Русский / Russian'),
          (LostFilmShow('Not Parsed', None, 'Not_Parsed', 666), u'Not Parsed'))
    @unpack
    def test_get_display_name(self, parsed_url, expected):
        # noinspection PyProtectedMember
        display_name = self.plugin._get_display_name(parsed_url)
        self.assertEqual(expected, display_name)

    @use_vcr()
    def test_parse_not_found_url(self):
        result = self.plugin.parse_url(
            "https://www.lostfilm.tv/series/Boardwalk_Empire")
        assert result is None

    @use_vcr()
    def test_parse_url(self):
        result = self.plugin.parse_url(
            "http://www.lostfilm.tv/series/Sherlock")
        assert result is not None
        assert result.russian_name == u'Шерлок'
        assert result.original_name == u'Sherlock'
        assert result.seasons_url == 'https://www.lostfilm.tv/series/Sherlock/seasons'

    def _add_topic(self,
                   url,
                   display_name,
                   search_name,
                   cat,
                   quality,
                   season=None,
                   episode=None):
        with DBSession() as db:
            topic = LostFilmTVSeries()
            topic.url = url
            topic.display_name = display_name
            topic.search_name = search_name
            topic.cat = cat
            topic.season = season
            topic.episode = episode
            topic.quality = quality
            db.add(topic)
            db.commit()
            return topic.id