Si vous constatez la perte de tickets, de commentaires ou de projets, veuillez consulter la page https://status.framasoft.org/incident/626.

dontpanic.py 39.2 KB
Newer Older
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
Thuban's avatar
Thuban committed
3 4 5


"""
Tankypon's avatar
Tankypon committed
6
Auteur :      thuban <thuban@yeuxdelibad.net>
Thuban's avatar
Thuban committed
7
licence :     MIT
Thuban's avatar
Thuban committed
8 9 10 11 12

Description :
    Just watch your favorite show and keep track of them.
"""

13 14
import getpass
import sys
15

16
from argparse import ArgumentParser
Thuban's avatar
Thuban committed
17
from bottle import *
18
from threading import Thread
19
import webbrowser
Tankypon's avatar
Tankypon committed
20

21
from database import DontPanicDB
22
from i18n import _
Thuban's avatar
Thuban committed
23
import locale
Tankypon's avatar
Tankypon committed
24
from subtitle import Subtitle
25
from torrentsession import TorrentSession
26 27
from utils import *
from webutils import *
Thuban's avatar
Thuban committed
28

29 30 31 32
if sys.version_info >= (3, 0):
    from urllib.parse import quote as quoteurl
else:
    from urllib import quote as quoteurl
33

34 35
logger = logging.getLogger(__name__)

Tankypon's avatar
PEP8  
Tankypon committed
36

37
def search2html(keywords, extrawords=' ', searchengine='btdigg', page=0, isserie=0):
38 39 40 41 42 43 44 45 46 47 48 49 50
    """
    Show search result

    Args:
        keywords:
        extrawords:
        searchengine:
        page:
        isserie:

    Return:

    """
51 52
    if searchengine == tpl_msg['tpl_all']:
        searchengine = "*"
Tankypon's avatar
Tankypon committed
53

54
    sch = '{} {}'.format(keywords, extrawords).strip()
55 56
    with TorrentSearch(searchengine) as ts:
        s = ts.search(sch, page)
Thuban's avatar
Thuban committed
57

58 59
    # If there is results
    if s:
Tankypon's avatar
Tankypon committed
60
        html = '<table class="sortable">\n'
61 62 63
        html += '<thead>\n'
        html += '<tr>\n'
        html += '<th class="text-left">{}</th>\n'.format(_('Name'))
Tankypon's avatar
Tankypon committed
64 65
        html += '<th>{}</th>\n'.format(_('Size'))
        html += '<th>{}</th>\n'.format(_('Leechs'))
66
        html += '<th id="seeds">{}</th>\n'.format(_('Seeds'))
67 68 69 70
        html += '</tr>\n'
        html += '</thead>\n'

        html += '<tbody>'
71 72
        for result in s:
            link = '/stream/{}/{}/{}'.format(keywords, quoteurl(result['magnet']), isserie)
73 74 75
            html += '<tr>\n'
            html += '<td><a href="{}">{}</a></td>\n'.format(link, result['name'])
            html += '<td class="text-center"><a href="{}">{}</a></td>\n'.format(link, result['size'])
76

77
            html += '<td class="text-center"><a href="{}">{}</a></td>\n'.format(link, result['leechs'])
78 79 80 81 82 83 84 85 86 87 88 89 90 91

            seeds = int(result['seeds'])
            # color depending on seeds number
            color = 'white'
            if seeds <= 10:
                color = 'red'
            elif 40 >= seeds > 10:
                color = 'orange'
            elif 70 >= seeds > 40:
                color = 'yellow'
            elif 100 >= seeds > 70:
                color = 'blue'
            elif seeds > 100:
                color = 'green'
Tankypon's avatar
Tankypon committed
92

93
            html += '<td class="text-center" style="background:{};"><a href="{}">{}</a></td>'.format(
94
                    color, link, result['seeds'])
95
            html += '</tr>'
96
        html += '</tbody>'
97 98
        html += '</table>'
        if searchengine == 'btdigg':
Tankypon's avatar
Tankypon committed
99
            html += '<a href="/moreresults/{}/{}/{}/{}/{}">{}</a>'.format(
100
                    keywords, int(page) + 1, searchengine, extrawords, isserie, _('More results'))
101
    else:  # If there is no result
Tankypon's avatar
Tankypon committed
102
        html = '<p><br>{}</p>'.format(
Thuban's avatar
pep-8  
Thuban committed
103
            _('There is no result for your search, try something else'))
104

Tankypon's avatar
Tankypon committed
105 106
    return html

Thuban's avatar
Thuban committed
107

Thuban's avatar
Thuban committed
108
def sch2html(keywords, serie=False):
109 110 111 112 113 114 115 116
    """


    Args:
        keywords:
        serie:

    Return:
Thuban's avatar
Thuban committed
117
        Return a research on omdb in html code
118
    """
Thuban's avatar
Thuban committed
119
    results = omdb_sch(keywords, serie=serie)
Thuban's avatar
Thuban committed
120
    if len(results) == 0:
121
        return ''
Thuban's avatar
Thuban committed
122 123 124
    else:
        html = '<ul class="ca-menu">'
        for r in results:
Tankypon's avatar
Tankypon committed
125
            isserie = 1 if serie else 0
Thuban's avatar
Thuban committed
126
            html += '<li><a href="/whatsthat/{1}/{2}/{3}"><img src="{0}"><p>{1} <br> {2}</p></a></li>'.format(
Tankypon's avatar
Tankypon committed
127
                    r['poster'], r['title'], r['year'], isserie)
Thuban's avatar
Thuban committed
128 129
        html += '</ul>'

Tankypon's avatar
Tankypon committed
130 131
        return html

Thuban's avatar
Thuban committed
132

Tankypon's avatar
Tankypon committed
133
def getseriestuff(keywords, year, user=''):
Thuban's avatar
Thuban committed
134
    serie_stuff = ''
135
    r = OMDBbAPI.search_by_title(keywords, year)
Thuban's avatar
Thuban committed
136
    if r['Response'] == 'True':
137
        eplist = get_episode_list(keywords, year)
Tankypon's avatar
Tankypon committed
138 139 140 141 142 143 144 145
        serie_stuff = '<div id="scroll">\n'
        serie_stuff += '<table class="sortable">\n'
        serie_stuff += '<thead>\n<tr>\n<th>{}</th>\n'.format(_('Season'))
        serie_stuff += '<th>{}</th>\n'.format(_('Episode'))
        serie_stuff += '<th>{}</th>\n'.format(_('Title'))
        serie_stuff += '<th class="text-right">{}</th>\n'.format(_('Release date'))
        serie_stuff += '<th>{}</th>\n</tr>\n</thead>\n'.format(_('Seen'))
        serie_stuff += '<tbody>\n'
146 147 148 149
        for season in eplist:
            for e in season['Episodes']:
                episode = 'S{0:02d}E{1:02d}'.format(int(season['Season']), int(e['Episode']))
                search = '{0} {1}'.format(keywords, episode)
Tankypon's avatar
Tankypon committed
150
                serie_stuff += '<tr>\n'
Tankypon's avatar
Tankypon committed
151 152 153 154 155 156 157 158 159 160 161 162
                serie_stuff += '<td class="text-center">' \
                               '\t<a href="#" onClick="updateInput(\'{}\')">{:02d}</a>' \
                               '</td>\n'.format(search, int(season['Season']))
                serie_stuff += '<td class="text-center">' \
                               '\t<a href="#" onClick="updateInput(\'{}\')">{:02d}</a>' \
                               '</td>\n'.format(search, int(e['Episode']))
                serie_stuff += '<td>' \
                               '\t<a id="submit" href="#" onClick="updateInput(\'{}\')">{}</a>' \
                               '</td>\n'.format(search, e['Title'])
                serie_stuff += '<td class="text-right">' \
                               '\t<a href="#" onClick="updateInput(\'{}\')">{}</a>' \
                               '</td>\n'.format(search, e['Released'])
163

164 165
                with DontPanicDB() as db:
                    if db.is_user_in(user):
166
                        togglelink = "/_toggleep/{}/".format(search)
167 168 169 170
                        color, seen = ("green", _("Yes")) if db.is_title_in_wl(user, search) else ("red", _("No"))

                        serie_stuff += '<td class="text-center" style="background:{1};">' \
                                       '<a href="{0}">{2}</a>' \
Tankypon's avatar
Tankypon committed
171
                                       '</td>\n'.format(togglelink, color, seen)
Thuban's avatar
Thuban committed
172
                    else:
Tankypon's avatar
Tankypon committed
173 174 175 176 177 178 179
                        serie_stuff += '<td class="text-right">?</td>\n'
                serie_stuff += '</tr>\n'
        serie_stuff += '</tbody>\n'
        serie_stuff += '</table>\n'
        serie_stuff += '</div>\n'

        serie_stuff += """
Thuban's avatar
Thuban committed
180
            <script src="/static/perfect-scrollbar.min.js"></script>
181
            <script>
Thuban's avatar
Thuban committed
182 183 184 185 186
                window.onload = function() {
                var container = document.getElementById("scroll");
                Ps.initialize(container);
                }

187 188 189 190
            function updateInput(ish){
                document.getElementById('keywords').value = ish;
                document.getElementById('series_form').submit();
            }
Tankypon's avatar
Tankypon committed
191
            </script>"""
192

Tankypon's avatar
Tankypon committed
193
    return serie_stuff
Thuban's avatar
Thuban committed
194

195

Tankypon's avatar
Tankypon committed
196
def omdb2template(keywords, year, isserie=0):
Thuban's avatar
Thuban committed
197 198 199 200 201 202 203 204
    title = ''
    runtime = ''
    director = ''
    writer = ''
    actors = ''
    poster = ''
    plot = ''
    rating = ''
205
    html = ''
206
    r = OMDBbAPI.search_by_title(keywords, year)
Thuban's avatar
Thuban committed
207 208 209 210

    if 'Response' not in r.keys():
        return title, runtime, director, writer, actors, poster, plot, rating, html
    elif r['Response'] == 'True':
Tankypon's avatar
Tankypon committed
211
        title = r['Title']
Thuban's avatar
Thuban committed
212 213 214 215 216
        runtime = r['Runtime']
        director = r['Director']
        writer = r['Writer']
        actors = r['Actors']
        poster = r['Poster']
Thuban's avatar
Thuban committed
217 218
        if poster == 'N/A':
            poster = "/static/unknown.png"
Thuban's avatar
Thuban committed
219 220 221
        plot = r['Plot']
        rating = r['imdbRating']
    else:
Thuban's avatar
Thuban committed
222
        html = _("Do you mean ? ")
Thuban's avatar
Thuban committed
223 224 225 226
        html += "<p>"
        lop = []
        k = keywords
        while len(k.split(' ')) > 1:
Tankypon's avatar
Tankypon committed
227
            k = k.rsplit(' ', 1)[0]
Thuban's avatar
Thuban committed
228
            lop.append(k)
Tankypon's avatar
Tankypon committed
229

Thuban's avatar
Thuban committed
230 231
        k = keywords
        while len(k.split(' ')) > 1:
Tankypon's avatar
Tankypon committed
232
            k = k.split(' ', 1)[-1]
Thuban's avatar
Thuban committed
233
            lop.append(k)
Tankypon's avatar
Tankypon committed
234

Tankypon's avatar
Tankypon committed
235
        for i in lop:
Thuban's avatar
Thuban committed
236
            html += ' - '
Tankypon's avatar
Tankypon committed
237
            html += '<a href="/whatsthat/{0}/{1}/{2}">{0}</a>'.format(i, year, isserie)
Thuban's avatar
Thuban committed
238 239 240
            html += ' - '
        html += "</p>"

Tankypon's avatar
Tankypon committed
241
    return title, runtime, director, writer, actors, poster, plot, rating, html
242 243 244 245 246


def watchlist(user):
    movies = ''
    series = ''
247 248
    with DontPanicDB() as db:
        watchlists = db.get_watchlist(user)
249

Tankypon's avatar
Tankypon committed
250
    for watch in watchlists:
251
        for ele in watch["list"]:
252 253 254
            date = ele["datetime"]
            title = ele["title"]
            magnet = ele["magnet"]
Tankypon's avatar
Tankypon committed
255
            video = str()
256

257
            link_remove = quoteurl('/_removeinwatchlist/{}/'.format(title))
Tankypon's avatar
Tankypon committed
258 259 260 261 262 263 264 265 266 267 268 269
            link_watch = quoteurl('/stream/{}/{}/{}'.format(title, magnet, 1 if watch["type"] == "serie" else 0))

            video += '<td>{}</td>\n'.format(date)
            video += '<td>{}</td>\n'.format(title)

            actions = str()
            if magnet:
                actions += '<span class="float-right"><a href="{}">▷ {}</a></span>'.format(link_watch, _('Watch again'))
            actions += '<span class="float-right"><a href="{}">✕ {}</a></span>'.format(link_remove, _('Remove'))
            video += '<td>{}</td>\n'.format(actions)

            video = '<tr>{}</tr>\n'.format(video)
270

Tankypon's avatar
Tankypon committed
271
            if watch["type"] == "serie":
Tankypon's avatar
Tankypon committed
272
                series += video
273
            else:
Tankypon's avatar
Tankypon committed
274
                movies += video
275 276

    if len(movies) > 0:
Tankypon's avatar
Tankypon committed
277 278 279 280 281 282 283 284
        movies = """
        <table style="padding-left: 30px;">
            <thead>
                <tr><th>{}</th><th>{}</th></tr>
            </thead>
            <tbody>{}</tbody>
        </table>""".format(_("Date"), _("Title"), movies)

285
    if len(series) > 0:
Tankypon's avatar
Tankypon committed
286 287 288 289 290 291 292
        series = """
        <table style="padding-left: 30px;">
            <thead>
                <tr><th>{}</th><th>{}</th></tr>
            </thead>
            <tbody>{}</tbody>
        </table>""".format(_("Date"), _("Title"), series)
293

Tankypon's avatar
Tankypon committed
294 295
    return movies, series

Thuban's avatar
Thuban committed
296

297
def create_select_search_engine(selected):
Thuban's avatar
Thuban committed
298
    list_search_engines = ["btdigg", "kickasstorrents", "piratebay", "torrentproject", tpl_msg['tpl_all']]
299 300 301 302 303 304 305 306 307

    list_html = str()
    for search_engine in list_search_engines:
        list_html += "\t<option{}>{}</option>\n".format(" selected" if search_engine == selected else "",
                                                        search_engine)

    return '<select name="searchengine" size="1">\n{}</select>'.format(list_html)


Thuban's avatar
Thuban committed
308
def pick_welcomemsg():
309 310 311 312 313 314 315 316
    return WELCOMEMSG[random.randrange(0, len(WELCOMEMSG))]


def pick_logo():
    if time.strftime("%m") == "12":
        return "dontpanic-noel.png"
    else:
        return "favicon.png"
Thuban's avatar
Thuban committed
317 318


Tankypon's avatar
Tankypon committed
319
class DontPanic:
Thuban's avatar
pep8  
Thuban committed
320
    def __init__(self, host, port, admin, adminpw="password", local_=True, magnet=""):
Tankypon's avatar
Tankypon committed
321
        self.startperc = None
Thuban's avatar
Thuban committed
322
        self.tmp = tempdir
Tankypon's avatar
Tankypon committed
323 324
        self.uplimit = None
        self.downlimit = None
Tankypon's avatar
Tankypon committed
325
        self.proxy = ""
Thuban's avatar
Thuban committed
326 327 328 329
        self.proxy_type = ""
        self.proxy_hostname = ""
        self.proxy_username = ""
        self.proxy_password = ""
330
        self.fav_sch_engine = ""
Thuban's avatar
Thuban committed
331
        self.lastversion = islastversion()
Tankypon's avatar
Tankypon committed
332

Thuban's avatar
Thuban committed
333 334 335
        self.app = Bottle()
        self.host = host
        self.port = port
Tankypon's avatar
PEP8  
Tankypon committed
336
        self.local = local_
337
        self.dldic = {}
Thuban's avatar
Thuban committed
338
        self.secret = randomtext(22)
Tankypon's avatar
Tankypon committed
339
        self.read_config()
340

341
        self.admin = admin
342
        self.adminpw = adminpw
343

344 345
        self.magnet_as_arg = magnet

346 347
        with DontPanicDB() as db:
            db.add_user(admin, adminpw)
Thuban's avatar
Thuban committed
348 349 350

        @self.app.route('/')
        def index():
351
            user = ''
352
            if self.local:
Tankypon's avatar
Tankypon committed
353
                response.set_cookie("username", self.admin, secret=self.secret, httponly=True)
354
                user = self.admin
Thuban's avatar
Thuban committed
355 356 357

            # show recent search
            html = ''
358 359
            if user == '':
                user = request.get_cookie("username", secret=self.secret)
Tankypon's avatar
Tankypon committed
360

Thuban's avatar
Thuban committed
361
            if user:
Tankypon's avatar
PEP8  
Tankypon committed
362 363
                with DontPanicDB() as database:
                    recent_search = database.get_recent_search(user)
Thuban's avatar
Thuban committed
364 365 366 367 368 369

                if len(recent_search) > 0:
                    for ele in recent_search:
                        title = ele["title"]
                        year = ele["year"]
                        isserie = ele["isserie"]
370 371
                        poster = ele["poster"]

Tankypon's avatar
Tankypon committed
372 373 374 375 376
                        posterhtml = '<img src="{}" title="{} {}" data-toggle="tooltip" ' \
                                     'data-placement="bottom">'.format(poster, title, year)
                        posterhtml = '<a href="/whatsthat/{}/{}/{}">{}</a>'.format(title, year, isserie, posterhtml)
                        posterhtml += '<a class="delete" href="/_removeinrecent/{}/">✗</a>'.format(title)
                        posterhtml = '<div class="show-image">{}</div>'.format(posterhtml)
377

Tankypon's avatar
Tankypon committed
378
                        html += '<li>{}</li>'.format(posterhtml)
Thuban's avatar
Thuban committed
379 380

                    html = '<ul class="ca-menu">{}</ul>'.format(html)
381

382 383 384
            tplvars = {'welcomemsg': pick_welcomemsg(),
                       'recents': html,
                       'logo': pick_logo(),
Thuban's avatar
Thuban committed
385
                       'islastv' : self.lastversion,
386
                       'search_engines': create_select_search_engine(self.fav_sch_engine)}
Thuban's avatar
Thuban committed
387
            tplvars.update(tpl_msg)
388
            return template('towel', tplvars)
Thuban's avatar
Thuban committed
389

Thuban's avatar
Thuban committed
390 391 392 393
        @self.app.route('/static/<filename:path>')
        def static(filename):
            return static_file(filename, root='./static/')

394 395
        @self.app.route('/me')
        def login():
Tankypon's avatar
Tankypon committed
396
            whoami = request.get_cookie("username", secret=self.secret)
397
            if not whoami:
Thuban's avatar
Thuban committed
398
                return '''
Tankypon's avatar
Tankypon committed
399 400 401 402 403 404 405
                        <form action="/me" method="post">
                            {}: <input name="username" type="text" />
                            {}: <input name="password" type="password" />
                            <input value="{}" type="submit" />
                        </form>
                        <p><a href="/sign">{}</a></p>
                        '''.format(_("Username"), _("Password"), _("Login"), _("Register"))
406 407
            else:
                movies, series = watchlist(whoami)
408
                tplvars = {'username': whoami, 'movies': movies, 'series': series}
409
                tplvars.update(tpl_msg)
Tankypon's avatar
Tankypon committed
410

411
                return template('perso', tplvars)
412 413 414

        @self.app.route('/me', method='POST')
        def do_login():
Tankypon's avatar
Tankypon committed
415
            username, password = request.forms.get('username'), request.forms.get('password')
416

Tankypon's avatar
PEP8  
Tankypon committed
417 418
            with DontPanicDB() as database:
                if database.check_login(username, password):
419 420 421
                    response.set_cookie("username", username, secret=self.secret)
                    redirect('/me')
                else:
Thuban's avatar
Thuban committed
422
                    abort(401, _('Your login information was incorrect'))
423 424 425 426 427 428 429 430

        @self.app.route('/logout')
        def logout():
            response.delete_cookie("username")
            redirect('/')

        @self.app.route('/sign')
        def sign():
Thuban's avatar
Thuban committed
431
            return ('''
Tankypon's avatar
Tankypon committed
432 433 434 435 436 437 438
                    <h1>{}</h1>
                    <form action="/sign" method="post">
                        {}: <input name="username" type="text" />
                        {}: <input name="password" type="password" />
                        <input value="{}" type="submit" />
                    </form>
                    '''.format(_("Register"), _("Username"), _("Password"), _("Login")))
439 440 441

        @self.app.route('/sign', method='POST')
        def do_sign():
Tankypon's avatar
Tankypon committed
442
            username, password = request.forms.get('username'), request.forms.get('password')
443

Tankypon's avatar
PEP8  
Tankypon committed
444 445
            with DontPanicDB() as database:
                if database.add_user(username, password):
Tankypon's avatar
Tankypon committed
446 447 448 449
                    return '''
                        <h1>{}</h1>
                        <p><a href="/me">{}</a> ;)</p>
                        '''.format(_("Congratulations, your subscription is a success"), _("Now, log in"))
450
                else:
Tankypon's avatar
Tankypon committed
451
                    return '<h1>{} {}</h1>'.format(_("Oh no!"), _("User {} already exists".format(username)))
452 453 454

        @self.app.route('/unsubscribe')
        def unsubscribe():
Tankypon's avatar
Tankypon committed
455
            whoami = request.get_cookie("username", secret=self.secret)
456
            if not whoami:
457 458 459 460 461 462 463 464
                html = _("You're not logged in")
            else:
                with DontPanicDB() as database:
                    if database.delete_user(whoami):
                        html = _("Your account is deleted")
                    else:
                        html = _("An error occurred while deleting your account. Please contact the administrator")

Tankypon's avatar
Tankypon committed
465
            html = "<div>{}</div>".format(html)
466 467 468 469 470

            tplvars = {'htmlstuff': html, 'title': _("Unsubscribe")}
            tplvars.update(tpl_msg)

            return template('classic', tplvars)
471

Thuban's avatar
Thuban committed
472 473 474
        @self.app.route('/clean')
        def cleanup():
            cleancache()
475
            redirect('/_admin')
Thuban's avatar
Thuban committed
476

477 478
        @self.app.route('/cleantorrent')
        def cleantorrent():
Thuban's avatar
Thuban committed
479
            self._clean_torlist()
480
            redirect('/_admin')
Thuban's avatar
Thuban committed
481 482 483 484 485 486

        @self.app.route('/search', method='get')
        def search():
            keywords = request.query.get('keywords')
            searchengine = request.query.get('searchengine')
            nocatalog = request.query.get('nocatalog')
487 488
            extrawords = request.query.get('extrawords')
            isserie = request.query.get('isserie')
Thuban's avatar
Thuban committed
489

Tankypon's avatar
Tankypon committed
490
            if not searchengine:
491
                searchengine = ''
Tankypon's avatar
Tankypon committed
492
            if not nocatalog:
Thuban's avatar
Thuban committed
493
                nocatalog = 0
494 495 496 497
            if not extrawords:
                extrawords = ' '
            if not isserie:
                isserie = '0'
Thuban's avatar
Thuban committed
498 499

            seriessch, moviessch = '', ''
500
            logger.info('Searching on {}...   '.format(searchengine))
Tankypon's avatar
Tankypon committed
501
            # FIXME: Better handle when a search engine has modified its html code
502 503 504 505
            try:
                htmlresults = search2html(keywords, extrawords=extrawords, searchengine=searchengine, isserie=isserie)
            except Exception as e:
                logger.info('Error while searching :  {}...   '.format(e))
Tankypon's avatar
Tankypon committed
506
                htmlresults = '<div><br>{}</div>'.format(_("Error while searching. Try another engine"))
507

Thuban's avatar
Thuban committed
508
            if nocatalog != "1":
509
                logger.info('Searching {}...   '.format("in series"))
Thuban's avatar
Thuban committed
510
                seriessch = sch2html(keywords, serie=True)
511
                logger.info('Searching {}...   '.format("in movies"))
Thuban's avatar
Thuban committed
512
                moviessch = sch2html(keywords, serie=False)
Thuban's avatar
Thuban committed
513

514 515 516 517 518
            tplvars = {'sch_res': htmlresults,
                       'the_search': '{} {}'.format(keywords, extrawords).strip(),
                       'moviessch': moviessch,
                       'seriessch': seriessch,
                       'isserie': isserie,
519
                       'search_engines': create_select_search_engine(searchengine),
520
                       'nocatalog': nocatalog}
521 522
            tplvars.update(tpl_msg)
            return template('results', tplvars)
Thuban's avatar
Thuban committed
523

524 525
        @self.app.route('/moreresults/<keywords>/<page>/<searchengine>/<extrawords>/<isserie>')
        def moreresults(keywords, page, searchengine, extrawords, isserie):
Tankypon's avatar
Tankypon committed
526
            # FIXME: Better handle when a search engine has modified its html code
527 528
            try:
                htmlresults = search2html(keywords.strip(),
529 530 531 532
                                          extrawords=extrawords,
                                          searchengine=searchengine,
                                          page=page,
                                          isserie=isserie)
533 534 535
            except Exception as e:
                logger.info('Error while searching :  {}...   '.format(e))
                htmlresults = _("Error while searching. Try another engine")
536 537

            tplvars = {
538 539 540
                'sch_res': htmlresults,
                'the_search': '{} {}'.format(keywords, extrawords).strip(),
                'moviessch': '',
Thuban's avatar
Thuban committed
541
                'seriessch': '',
542
                'search_engines': create_select_search_engine(searchengine),
Thuban's avatar
pep-8  
Thuban committed
543
                'isserie': isserie}
544 545
            tplvars.update(tpl_msg)
            return template('results', tplvars)
Thuban's avatar
Thuban committed
546

Thuban's avatar
Thuban committed
547 548
        @self.app.route('/whatsthat/<title>/<year>/<isserie>')
        def whatsthat(title, year, isserie):
Tankypon's avatar
Tankypon committed
549 550
            title_simplify = simplifyname(title)
            title, runtime, director, writer, actors, poster, plot, rating, html = omdb2template(
Thuban's avatar
pep-8  
Thuban committed
551
                title_simplify, year, isserie)
Thuban's avatar
Thuban committed
552

Thuban's avatar
Thuban committed
553 554
            whoami = request.get_cookie("username", secret=self.secret)
            self._add_to_recent_search(whoami, title, year, poster, isserie)
Thuban's avatar
Thuban committed
555

Tankypon's avatar
Tankypon committed
556 557 558 559
            if isserie:
                whoami = request.get_cookie("username", secret=self.secret)
                list_ep_serie = getseriestuff(title, year, user=whoami)

Thuban's avatar
Thuban committed
560 561 562 563
            try:
                lang = locale.setlocale(locale.LC_ALL, "")[0:2]  # 'fr' or 'en'
            except:
                lang = 'en'
564
            tplvars = {
565 566 567 568 569 570 571 572 573
                'title': title,
                'title_simplify': title_simplify,
                'year': year,
                'runtime': runtime,
                'director': director,
                'writer': writer,
                'actors': actors,
                'poster': poster,
                'plot': plot,
Thuban's avatar
Thuban committed
574
                'lang': lang,
575 576
                'rating': rating,
                'html': html,
Tankypon's avatar
Tankypon committed
577
                'isserie': isserie,
578
                'list_ep_serie': list_ep_serie,
579
                'search_engines': create_select_search_engine(self.fav_sch_engine)}
580
            tplvars.update(tpl_msg)
581
            return template('what', tplvars)
582 583

        @self.app.route("/stream/<name>/<magnet:path>/<isserie>")
Tankypon's avatar
PEP8  
Tankypon committed
584
        def stream(name, magnet, isserie):
Tankypon's avatar
Tankypon committed
585
            # Start download
Thuban's avatar
Thuban committed
586 587 588 589
            if self.proxy != "none":
                newses = TorrentSession(self.proxy_type, self.proxy_hostname, self.proxy_username, self.proxy_password)
            else:
                newses = TorrentSession()
Tankypon's avatar
Tankypon committed
590 591
            newses.download(magnet, self.tmp, self.uplimit, self.downlimit)

592
            index = 1
593
            if len(self.dldic) > 0:
Thuban's avatar
Thuban committed
594 595
                while index in self.dldic.keys():
                    index += 1
596
            self.dldic[index] = newses
597

Tankypon's avatar
Tankypon committed
598
            # Record to watchlist
Tankypon's avatar
Tankypon committed
599
            whoami = request.get_cookie("username", secret=self.secret)
600
            if whoami:
Tankypon's avatar
Tankypon committed
601
                t = Thread(target=self._add_when_ready, args=(newses, whoami, name, magnet, isserie))
602
                t.start()
Thuban's avatar
Thuban committed
603

604 605 606 607 608 609
            tplvars = {'movie_name': name,
                       'status': '',
                       'playlink': '',
                       'subtitles': '',
                       'stop': '',
                       'index': index}
610 611
            tplvars.update(tpl_msg)

612
            return template('towel-dl', tplvars)
Thuban's avatar
Thuban committed
613

Tankypon's avatar
Tankypon committed
614 615
        @self.app.route('/_getinfos/<name>/<index:int>')
        def getinfos(name, index):
616 617 618 619
            """
            Give infos html formatted

            Args:
Tankypon's avatar
Tankypon committed
620
                name: Title of the video
Tankypon's avatar
Tankypon committed
621
                index:
622
            """
623
            session = self.dldic[index]
Tankypon's avatar
Tankypon committed
624
            perc = session.get_progress()
625 626 627
            state = session.get_state()
            peers = session.get_peers()
            downrate = session.get_downrate()
Thuban's avatar
Thuban committed
628

Tankypon's avatar
PEP8  
Tankypon committed
629
            # Hack for progress bar
630
            # htmlperc = '<div class="progressbar" style="background-size: {}% 100%; ">'.format(perc)
Thuban's avatar
Thuban committed
631 632
            htmlperc = '<div>{}% <br>{} - {}/s - {} peers</div>'.format(perc, state, downrate, peers)
            htmlperc += '<div class="progressbar"><progress value="{0}" max="100" ></progress>'.format(perc)
Thuban's avatar
Thuban committed
633
            htmlperc += '</div>'
Tankypon's avatar
Tankypon committed
634

Tankypon's avatar
PEP8  
Tankypon committed
635
            # To stop the download
Thuban's avatar
Thuban committed
636 637 638
            stop = ''
            if state != "downloading metadata":
                stop = '<a href="/stopdl/{}" >{}</a>'.format(index, _('Stop this download'))
Thuban's avatar
Thuban committed
639

Tankypon's avatar
PEP8  
Tankypon committed
640
            # Show the play link
641
            moviepath = session.get_file_name()
Thuban's avatar
Thuban committed
642 643
            playlink = ''
            subtitles = ''
Tankypon's avatar
Tankypon committed
644
            if perc >= int(self.startperc) and session.ready2watch():
Tankypon's avatar
PEP8  
Tankypon committed
645
                # Show the link to play the file
Thuban's avatar
Thuban committed
646
                playlink = '<p><span class="big">' \
Tankypon's avatar
Tankypon committed
647
                           '\t<a href="/html5/{}" target="_blank">{} ▷</a>' \
Thuban's avatar
Thuban committed
648
                           '</span></p>'.format(moviepath, _('Play'))
Tankypon's avatar
Tankypon committed
649
                subtitles = '<a href="/getsubtitles/{}/{}" target="_blank">{}</a> - '.format(
Thuban's avatar
pep-8  
Thuban committed
650
                    name, moviepath, _('Find subtitles'))
651

Tankypon's avatar
Tankypon committed
652 653 654 655
            return {'status': htmlperc,
                    'playlink': playlink,
                    'subtitles': subtitles,
                    'stop': stop}
Thuban's avatar
Thuban committed
656 657 658

        @self.app.route('/stopdl/<i:int>')
        def stopdl(i):
659
            t = self.dldic[i]
Thuban's avatar
Thuban committed
660 661
            whoami = request.get_cookie("username", secret=self.secret)
            if whoami != self.admin:
Thuban's avatar
Thuban committed
662
                return _("Only the administrator can delete a download")
Thuban's avatar
Thuban committed
663
            else:
Thuban's avatar
Thuban committed
664
                h = t.handle
Thuban's avatar
Thuban committed
665
                session = t.session
Thuban's avatar
Thuban committed
666
                session.remove_torrent(h)
667
                self.dldic.pop(i)
Thuban's avatar
Thuban committed
668
                del session
Tankypon's avatar
Tankypon committed
669

Tankypon's avatar
Tankypon committed
670
                html = "<div>{}</div>".format(_("Torrent deleted"))
671

672
                tplvars = {'htmlstuff': html, 'title': "Deleted"}
673 674
                tplvars.update(tpl_msg)
                return template('classic', tplvars)
Thuban's avatar
Thuban committed
675

Thuban's avatar
Thuban committed
676 677
        @self.app.route('/html5/<filename:path>')
        def html5(filename):
Thuban's avatar
Thuban committed
678 679 680 681 682 683 684 685 686 687 688 689
            mt = mimetypes.guess_type(filename)[0]

            
            if mt == 'video/x-msvideo-nimportequoi' :
                #video = '<object data="/video/{}" width="360" height="250">\n'.format(filename)
                #video += '<param name="src" value="/video/{}" /> </object>\n'.format(filename)
                video = '<embed src="/video/{}">\n'.format(filename)
            else:
                mtype = 'type="{}"'.format(mt)
                video = '<video width="640" controls preload="auto" autoplay="true" >\n'
                video += '<source src="/video/{}" {} >\n'.format(filename, mtype)
                video += '</video>\n'
690 691

            links = '<div>\n'
692 693 694
            links += '<br/>\n'
            links += _("If your browser doesn't support the player above, you may want to : ")
            links += '<br/>\n'
695
            if self.local:
Thuban's avatar
Thuban committed
696 697
                links += '<a href="/localvideo/{}">▷ {}</a> - \n'.format(filename, _('Play with external video player'))
            links += '<a href="/video/{}">↓ {}</a>\n'.format(filename, _('Download the video'))
698
            links += '</div>\n'
699

Tankypon's avatar
Tankypon committed
700
            html = "<div>{} {}</div>".format(video, links)
701

702
            tplvars = {'title': os.path.basename(filename), 'htmlstuff': html}
703 704
            tplvars.update(tpl_msg)
            return template('classic', tplvars)
Thuban's avatar
Thuban committed
705

Thuban's avatar
Thuban committed
706
        @self.app.route('/video/<filename:path>')
Thuban's avatar
Thuban committed
707 708
        def send_static_video(filename):
            return static_file(filename, root=self.tmp)
Tankypon's avatar
PEP8  
Tankypon committed
709
            # return "<script>javascript:history.back();</script>"
710

711 712 713
        @self.app.route('/localvideo/<filename:path>')
        def open_in_external_player(filename):
            file_path = os.path.join(self.tmp, filename)
Thuban's avatar
Thuban committed
714
            if which("xdg-open"):
715 716
                import subprocess
                subprocess.Popen(["xdg-open", file_path])
717 718
            else:
                os.startfile(file_path)
Tankypon's avatar
Tankypon committed
719

720 721
            return "<script>javascript:history.back();</script>"

722
        @self.app.route('/getsubtitles/<title>/<filename:path>')
Tankypon's avatar
Tankypon committed
723
        def showsubtitles(title, filename):
724
            opensubtitles = '<p>{} <a href="http://www.opensubtitles.org/search2/moviename-{}">' \
725
                            'Opensubtitles.org</a></p>'.format(_('Look for subtitles on'), title)
726

Tankypon's avatar
Tankypon committed
727
            fullpath = os.path.join(self.tmp, filename)
728
            hash = get_hash(fullpath)
Tankypon's avatar
Tankypon committed
729
            avail = Subtitle.search(hash)
730
            if avail:
731
                subdb = "<p>{}:</p>\n".format(_('Choose a language to download subtitle'))
732 733 734
                subdb += '<ul">\n'
                o = os.path.splitext(filename)[0] + '.srt'
                for l in avail:
Tankypon's avatar
Tankypon committed
735
                    subdb += '<li style="display:inline-block;">- <a href="/dlsub/{0}/{1}/{2}">{0}</a> -</li>\n'.format(
Thuban's avatar
pep-8  
Thuban committed
736
                        l, hash, o)
737 738 739

                subdb += '</ul>\n'
            else:
Tankypon's avatar
Tankypon committed
740
                subdb = '<p>{}</p>'.format(_('No subtitle available on subdb'))
741

Tankypon's avatar
Tankypon committed
742
            html = "<div>{} {}</div>".format(subdb, opensubtitles)
743

744
            tplvars = {'title': title, 'htmlstuff': html}
745 746
            tplvars.update(tpl_msg)
            return template('classic', tplvars)
747 748

        @self.app.route('/dlsub/<lang>/<hash>/<filename:path>')
749
        def dlsub(lang, hash, filename):
Tankypon's avatar
Tankypon committed
750
            if Subtitle.download(hash, lang, os.path.join(self.tmp, filename)):
Tankypon's avatar
Tankypon committed
751
                html = """<div>
752
                        <p>{}<br><a href="/video/{}">{}</a></p>
Thuban's avatar
Thuban committed
753
                        <p>{}</p>
754
                        </div>
Tankypon's avatar
Tankypon committed
755 756 757 758
                        """.format(_("Success: your subtitles are in the torrent folder"),
                                   filename,
                                   _("Click here if you want to download them on you computer"),
                                   _("You may now close this page"))
759
            else:
Tankypon's avatar
Tankypon committed
760
                html = "<div>{}</div>".format(_("Failed to download subtitles"))
761 762 763 764

            tplvars = {'title': _("Subtitles"), 'htmlstuff': html}
            tplvars.update(tpl_msg)
            return template('classic', tplvars)
Thuban's avatar
Thuban committed
765

766
        @self.app.route('/_admin')
Thuban's avatar
Thuban committed
767
        def configure():
Tankypon's avatar
Tankypon committed
768
            whoami = request.get_cookie("username", secret=self.secret)
769
            if whoami != self.admin:
Thuban's avatar
Thuban committed
770
                return '''
Tankypon's avatar
Tankypon committed
771 772 773 774 775 776 777 778 779 780
                    <p>{}</p>
                    <form action="/_admin" method="post">
                        {}: <input name="username" type="text" />
                        {}: <input name="password" type="password" />
                        <input value="{}" type="submit" />
                    </form>
                    '''.format(_("Only the administrator can see this page"),
                               _("Username"),
                               _("Password"),
                               _("Login"))
781 782
            else:
                conf = load_config()
783 784

                tplvars = {
785
                    'cache': get_cache_size(),
Thuban's avatar
Thuban committed
786
                    'ntor': len(self.dldic),
787 788 789 790 791 792 793 794
                    'startperc': conf['startperc'],
                    'uplimit': conf['uplimit'],
                    'downlimit': conf['downlimit'],
                    'search_engines': create_select_search_engine(conf['searchengine']),
                    'proxy_type': conf['proxy_type'],
                    'proxy_hostname': conf['proxy_hostname'],
                    'proxy_username': conf['proxy_username'],
                    'proxy_password': conf['proxy_password']
Thuban's avatar
Thuban committed
795
                    }
796 797
                tplvars.update(tpl_msg)
                return template('config', tplvars)
798 799 800

        @self.app.route('/_admin', method='POST')
        def do_login():
Tankypon's avatar
Tankypon committed
801
            username, password = request.forms.get('username'), request.forms.get('password')
802
            if username == self.admin and password == self.adminpw:
Tankypon's avatar
Tankypon committed
803
                response.set_cookie("username", username, secret=self.secret)
804 805
                redirect('/_admin')
            else:
Tankypon's avatar
Tankypon committed
806
                return '<p>{}</p>'.format(_('Go to hell, take the highway. You are not the administrator.'))
Thuban's avatar
Thuban committed
807 808 809

        @self.app.route('/_applyconfig', method='post')
        def applyconfig():
810 811
            config_list = ["searchengine", "startperc", "uplimit", "downlimit",
                           "proxy_type", "proxy_hostname", "proxy_password"]
Tankypon's avatar
Tankypon committed
812 813 814 815 816 817 818 819 820 821 822 823 824 825
            user_config = load_config()
            update_config = False

            for config_to_check in config_list:
                new_config = request.forms.get(config_to_check)

                if user_config[config_to_check] != new_config:
                    user_config[config_to_check] = new_config

                    if not update_config:
                        update_config = True

            if update_config:
                save_config(user_config)
Thuban's avatar
Thuban committed
826

Tankypon's avatar
Tankypon committed
827
            self.read_config()
Thuban's avatar
Thuban committed
828

829
            redirect('/_admin')
Thuban's avatar
Thuban committed
830

831
        @self.app.route('/_goodbye')
Thuban's avatar
Thuban committed
832
        def stop_server():
Tankypon's avatar
Tankypon committed
833
            whoami = request.get_cookie("username", secret=self.secret)
834
            if whoami == self.admin:
Tankypon's avatar
Tankypon committed
835
                htmlstuff = "<p>{}.</p><p>{}! ☺</p>".format(_("Don't Panic is now off"), _('See you soon'))
Thuban's avatar
Thuban committed
836
                title = "ARGH! 😵"
837

838
                tplvars = {'htmlstuff': htmlstuff, 'title': title}
839 840
                tplvars.update(tpl_msg)
                yield template('goodbye', tplvars)
841
                self.stop()
842 843
            else:
                redirect('/')
Thuban's avatar
Thuban committed
844

Tankypon's avatar
Tankypon committed
845 846 847
        @self.app.route('/_about')
        def show_about():
            htmlstuff = "<p>{}. ☺</p><br/><p>{}! ♥</p>".format(_("Dontpanic is a free (as in 'freedom') python "
Tankypon's avatar
Tankypon committed
848 849 850 851 852
                                                                 "application to look for any movie or serie and watch "
                                                                 "them in 'streaming'. It looks like Popcorn Time or "
                                                                 "Torrents Time, but doesn't depend of a centralized "
                                                                 "api"),
                                                               _("This has been made by two independent developers "
Thuban's avatar
typo  
Thuban committed
853
                                                                 "who love free software"))
Tankypon's avatar
Tankypon committed
854 855 856
            title = "Don't Panic"
            version = "{}: {}".format(_("Version"), progversion)

857
            tplvars = {'title': title, 'version': version, 'logo': pick_logo(), 'htmlstuff': htmlstuff}
Tankypon's avatar
Tankypon committed
858 859 860
            tplvars.update(tpl_msg)
            yield template('about', tplvars)

861 862
        @self.app.route('/_toggleep/<title>/')
        def toggle_episode(title):
Tankypon's avatar
Tankypon committed
863
            whoami = request.get_cookie("username", secret=self.secret)
Tankypon's avatar
PEP8  
Tankypon committed
864 865 866 867
            link = request.headers.get('Referer')
            with DontPanicDB() as database:
                database.toggle_in_wl(whoami, title)
            redirect(link)
Thuban's avatar
Thuban committed
868

869
        @self.app.route('/_removeinwatchlist/<title>/')
Tankypon's avatar
Tankypon committed
870
        def remove_in_watchlist(title):
871 872 873 874 875 876
            whoami = request.get_cookie("username", secret=self.secret)
            link = request.headers.get('Referer')
            with DontPanicDB() as database:
                database.delete_title_in_wl(whoami, title)
            redirect(link)

877 878 879 880 881 882 883 884
        @self.app.route('/_removeinrecent/<title>/')
        def remove_in_recent(title):
            whoami = request.get_cookie("username", secret=self.secret)
            link = request.headers.get('Referer')
            with DontPanicDB() as database:
                database.remove_in_recent(whoami, title)
            redirect(link)

Thuban's avatar
Thuban committed
885 886
        @self.app.route('/_bestvideos/<i:int>')
        def best_videos(i):
Tankypon's avatar
Tankypon committed
887
            # Set source from IMDB
Thuban's avatar
Thuban committed
888
            best = BestIMDB()
Tankypon's avatar
Tankypon committed
889 890

            # See the best series
891
            if i == 1:
Tankypon's avatar
Tankypon committed
892
                url = best.url_best_tvshows()
893 894
                title = "Popular series"

Tankypon's avatar
Tankypon committed
895
                results = best.get_best_tvshows()
Tankypon's avatar
Tankypon committed
896
            # See the best dvd's
897
            else:
Tankypon's avatar
Tankypon committed
898
                url = best.url_best_dvd()
899 900
                title = "Best DVD's"

Tankypon's avatar
Tankypon committed
901
                results = best.get_best_dvd()
902 903 904

            html = str()
            for result in results:
Tankypon's avatar
Tankypon committed
905 906 907
                html += '<li><a href="/whatsthat/{1}/{2}/{3}"><img src="{0}" data-toggle="tooltip" ' \
                        'data-placement="bottom" title="{1} {2}"></a></li>'.format(result["img"], result["title"],
                                                                                   result["year"], i)
908 909 910

            html = '<ul class="ca-menu">{}</ul>'.format(html)

Tankypon's avatar
Tankypon committed
911
            tplvars = {'url': url, 'title': title, 'htmlstuff': html}
912
            tplvars.update(tpl_msg)
Tankypon's avatar
Tankypon committed
913
            return template('best', tplvars)
914

Tankypon's avatar
Tankypon committed
915 916
    def read_config(self):
        conf = load_config()
917 918 919 920 921 922 923 924
        self.startperc = conf['startperc']  # percent to start a movie
        self.uplimit = conf['uplimit']
        self.downlimit = conf['downlimit']
        self.fav_sch_engine = conf['searchengine']
        self.proxy = conf['proxy_type']
        self.proxy_hostname = conf['proxy_hostname']
        self.proxy_username = conf['proxy_username']
        self.proxy_password = conf['proxy_password']
Tankypon's avatar
Tankypon committed
925

Tankypon's avatar
Tankypon committed
926 927 928
        return conf

    def open_in_browser(self):
929
        while not is_running():
Thuban's avatar
Thuban committed
930
            time.sleep(0.1)
Tankypon's avatar
Tankypon committed
931

Thuban's avatar
Thuban committed
932
        time.sleep(0.5)
Tankypon's avatar
Tankypon committed
933
        if self.magnet_as_arg:
934 935 936
            webbrowser.open("http://localhost:{}/stream/dontpanic/{}/0".format(port, self.magnet_as_arg))
        else:
            webbrowser.open("http://localhost:{}".format(self.port))
Tankypon's avatar
Tankypon committed
937

Thuban's avatar
Thuban committed
938
    def stop(self):
Tankypon's avatar
Tankypon committed
939
        i