summaryrefslogtreecommitdiff
path: root/epour/gui/TorrentSelector.py
blob: c6c899348e90251be67b3985ae84478a1db570c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
#
#  Epour - A bittorrent client using EFL and libtorrent
#
#  Copyright 2012-2017 Kai Huuhko <kai.huuhko@gmail.com>
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.
#

import os
import logging

from libtorrent import add_torrent_params_flags_t

from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
from efl import elementary as elm
from efl.elementary import DialogWindow
from efl.elementary import Button
from efl.elementary import Box
from efl.elementary import Frame
from efl.elementary import Fileselector
from efl.elementary import FileselectorEntry
from efl.elementary import Entry, markup_to_utf8, utf8_to_markup
from efl.elementary import Check
from efl.elementary import Scroller
from efl.elementary import Spinner
# from efl.elementary import ELM_SEL_TYPE_CLIPBOARD, \
#     ELM_SEL_FORMAT_TEXT

from .Widgets import UnitSpinner

EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
EXPAND_HORIZ = EVAS_HINT_EXPAND, 0.0
FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
FILL_HORIZ = EVAS_HINT_FILL, 0.5

from efl.elementary import Configuration

elm_conf = Configuration()
scale = elm_conf.scale

log = logging.getLogger("epour.gui")


class TorrentSelector(DialogWindow):

    names = {
        1: _("Seed Mode"),
        8: _("Share Mode"),
        16: _("Apply IP Filter"),
        32: _("Start Paused"),
        128: _("Duplicate Is Error"),
        1024: _("Super Seeding"),
        2048: _("Sequential Download"),
    }


    tooltips = {
        1: utf8_to_markup(_(

'''If Seed Mode is set, Epour will assume that all files are
present for this torrent and that they all match the hashes in the
torrent file. Each time a peer requests to download a block, the
piece is verified against the hash, unless it has been verified
already. If a hash fails, the torrent will automatically leave the
seed mode and recheck all the files. The use case for this mode is
if a torrent is created and seeded, or if the user already know
that the files are complete, this is a way to avoid the initial
file checks, and significantly reduce the startup time.

Setting Seed Mode on a torrent without metadata (a .torrent
file) is a no-op and will be ignored.'''

            )),
        8: utf8_to_markup(_(

'''Determines if the torrent should be added in share mode or not.
Share mode indicates that we are not interested in downloading the
torrent, but merely want to improve our share ratio (i.e. increase
it). A torrent started in share mode will do its best to never
download more than it uploads to the swarm. If the swarm does not
have enough demand for upload capacity, the torrent will not
download anything. This mode is intended to be safe to add any
number of torrents to, without manual screening, without the risk
of downloading more than is uploaded.

A torrent in share mode sets the priority to all pieces to 0,
except for the pieces that are downloaded, when pieces are decided
to be downloaded. This affects the progress bar, which might be set
to "100% finished" most of the time. Do not change file or piece
priorities for torrents in share mode, it will make it not work.

The share mode has one setting, the share ratio target.'''

            )),
        16: utf8_to_markup(_(

'''Determines if the IP filter should apply to this torrent or not. By
default all torrents are subject to filtering by the IP filter
(i.e. this flag is set by default). This is useful if certain
torrents needs to be excempt for some reason, being an auto-update
torrent for instance.'''

            )),
        32: utf8_to_markup(_(

'''Specifies whether or not the torrent is to be started in a paused
state. I.e. it won't connect to the tracker or any of the peers
until it's resumed. This is typically a good way of avoiding race
conditions when setting configuration options on torrents before
starting them.'''

            )),
        1024: utf8_to_markup(_(

'''Sets the torrent into super seeding mode. If the torrent is not a
seed, this flag has no effect.'''

            )),
        2048: utf8_to_markup(_(

'''Sets the sequential download state for the torrent.'''

            )),
    }

    def __init__(self, parent, session, t_uri=None):
        DialogWindow.__init__(
            self, parent, "addtorrent", _("Add Torrent"),
            size=(scale * 400, scale * 400), autodel=True
            )

        self.add_dict = {}

        scrol = Scroller(
            self, size_hint_weight=EXPAND_BOTH, size_hint_align=FILL_BOTH)
        self.resize_object_add(scrol)

        box = Box(
            scrol, size_hint_weight=EXPAND_BOTH, size_hint_align=FILL_BOTH,
            align=(0.5, 0.0))

        scrol.content = box

        hbox = Box(
            box, size_hint_weight=EXPAND_HORIZ, size_hint_align=FILL_HORIZ,
            horizontal=True)
        box.pack_end(hbox)
        hbox.show()

        uri_entry = Entry(
            box, size_hint_weight=EXPAND_HORIZ, size_hint_align=FILL_HORIZ,
            single_line=True, scrollable=True
            )
        uri_entry.part_text_set("guide", _("Enter torrent file path / magnet URI / info hash"))

        if t_uri:
            uri_entry.entry = utf8_to_markup(t_uri)

        hbox.pack_end(uri_entry)
        uri_entry.show()

        fsb = Button(box, text=_("Select file"))
        fsb.callback_clicked_add(lambda x: TorrentFs(self, uri_entry))
        hbox.pack_end(fsb)
        fsb.show()

        options = Frame(
            self, size_hint_weight=EXPAND_HORIZ, size_hint_align=FILL_HORIZ,
            text=_("Advanced Options") + u" \u25BA", collapse=True
            )
        box.pack_end(options)
        options.show()

        def toggler(obj):
            if obj.collapse:
                obj.text = obj.text.replace(u"\u25BA", u"\u25BC")
            else:
                obj.text = obj.text.replace(u"\u25BC", u"\u25BA")
            obj.collapse_go(not obj.collapse)

        options.callback_clicked_add(toggler)

        opt_box = Box(
            options, size_hint_weight=EXPAND_BOTH, size_hint_align=FILL_BOTH
            )
        options.content = opt_box

        def entry_cb(e, key):
            self.add_dict[key] = e.entry

        for v, t in {
            "name": _("Name"),
            "trackerid": _("Tracker ID")
                }.items():
            e = Entry(opt_box, size_hint_align=FILL_HORIZ)
            e.part_text_set("guide", t)
            e.callback_changed_user_add(entry_cb, v)
            opt_box.pack_end(e)
            e.show()

        def fs_cb(fs):
            self.add_dict["save_path"] = fs.path

        # Downloaded data is saved in this path
        title = _("Data Save Path")
        save_path = FsEntry(
            opt_box, size_hint_align=FILL_HORIZ, text=title,
            folder_only=True, expandable=False
            )
        save_path.callback_changed_add(fs_cb)
        path = session.conf.get("Settings", "storage_path").strip()
        save_path.path = path
        opt_box.pack_end(save_path)
        save_path.show()

        self.add_dict["flags"] = (
            add_torrent_params_flags_t.flag_apply_ip_filter +
            add_torrent_params_flags_t.flag_update_subscribe +
            add_torrent_params_flags_t.flag_duplicate_is_error +
            add_torrent_params_flags_t.flag_auto_managed)

        def option_flag_cb(c, flag):
            flags = self.add_dict["flags"]
            flags = flags ^ flag
            if flag == int(add_torrent_params_flags_t.flag_paused):
                flags = flags ^ int(add_torrent_params_flags_t.flag_auto_managed)
            self.add_dict["flags"] = flags

        for name, flag in sorted(add_torrent_params_flags_t.names.items()):
            if not int(flag) in self.names.keys():
                continue
            c = Check(
                opt_box, size_hint_align=(0.0, 0.5),
                text=self.names.get(int(flag), " ".join(name.split("_")[1:]).capitalize()),
                state=bool(self.add_dict["flags"] & int(flag))
                )
            if int(flag) in self.tooltips:
                c.tooltip_text_set(self.tooltips[int(flag)])
                c.tooltip_window_mode_set(True)
            c.callback_changed_add(option_flag_cb, int(flag))
            opt_box.pack_end(c)
            c.show()

        INT_MAX = 2147483647

        def spin_cb(s, key):
            self.add_dict[key] = s.value

        for v, t in {
            "max_uploads": _("Max Uploads"),
            "max_connections": _("Max Connections")
                }.items():
            s = Spinner(
                opt_box, size_hint_align=FILL_HORIZ, min_max=(0, INT_MAX),
                label_format=t + ": %0.f"
                )
            s.callback_changed_add(spin_cb, v)
            opt_box.pack_end(s)
            s.show()

        for v, t in {
            "upload_limit": _("Upload Limit"),
            "download_limit": _("Download Limit")
                }.items():
            s = UnitSpinner(opt_box, "B/s", 1024, UnitSpinner.binary_prefixes)
            s.size_hint_weight = EXPAND_HORIZ
            s.size_hint_align = FILL_HORIZ
            s.set_value(0)
            s.callback_changed_add(
                lambda x, y: self.add_dict.__setitem__(v, y)
                )
            s.spinner.label_format = t + ": %0.f"
            opt_box.pack_end(s)
            s.show()

        # TODO:
        # list(strings) trackers (list(entry))
        # list(tuple(str host, int port), ...) dht_nodes
        # storage_mode_t storage_mode
        # list(int) file_priorities (list(spinner))

        opt_box.show()

        bbox = Box(
            box, size_hint_weight=EXPAND_HORIZ,
            size_hint_align=FILL_HORIZ, horizontal=True)

        ok_btn = Button(bbox, text=_("Ok"), size_hint_align=(1.0, 0.5))
        bbox.pack_end(ok_btn)
        ok_btn.show()

        cancel_btn = Button(bbox, text=_("Cancel"), size_hint_align=(1.0, 0.5))
        bbox.pack_end(cancel_btn)
        cancel_btn.show()

        bbox.show()

        box.pack_end(bbox)
        box.show()
        scrol.show()

        def add_torrent_cb(btn, uri_entry, session, add_dict):
            uri = uri_entry.entry

            if not uri:
                return

            uri = markup_to_utf8(uri)

            session.fill_add_dict_based_on_uri(add_dict, uri)
            session.add_torrent_with_dict(add_dict)

            self.delete()

        ok_btn.callback_clicked_add(
            add_torrent_cb, uri_entry, session, self.add_dict)
        cancel_btn.callback_clicked_add(lambda x: self.delete())

        self.show()


class FsEntry(Fileselector, FileselectorEntry):

    def __init__(self, parent, **kwargs):
        FileselectorEntry.__init__(self, parent, **kwargs)


class TorrentFs(DialogWindow):

    def __init__(self, parent, uri_entry):

        super(TorrentFs, self).__init__(
            parent, "torrentselect", "Select Torrent", size=(500, 500),
            autodel=True)

        fs = Fileselector(
            self, size_hint_weight=EXPAND_BOTH, size_hint_align=FILL_BOTH,
            expandable=False, is_save=False, path=os.path.expanduser("~")
            )
        if elm.need_efreet():
            fs.mime_types_filter_append(
                ["application/x-bittorrent"], "Torrent files")
            fs.mime_types_filter_append(
                ["*"], "All files")

        self.resize_object_add(fs)
        fs.show()

        def done_cb(fs, path):
            if path and os.path.isfile(path):
                uri_entry.entry_set(path)
            self.delete()

        fs.callback_done_add(done_cb)

        self.show()