Skip to content

Commit

Permalink
Added common cookie handling
Browse files Browse the repository at this point in the history
- added PRG pattern to torrent add page
- skipping already existing trackers when adding new ones: #27
- added notification to tracker add
- support to put stopped torrents to pause state
  • Loading branch information
wolandmaster committed Jul 10, 2021
1 parent 215aa2f commit 422b2b5
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 72 deletions.
2 changes: 1 addition & 1 deletion control/control
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Package: luci-app-rtorrent
Version: 0.2.2
Version: 0.2.3
Depends: libc, rtorrent-rpc, luaexpat, luasocket, luasec, luci-compat, luci-lib-httpprotoutils
Source: https://github.com/wolandmaster/luci-app-rtorrent
Section: luci
Expand Down
33 changes: 14 additions & 19 deletions src/usr/lib/lua/luci/model/cbi/rtorrent/add.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,28 @@ require "luci.model.cbi.rtorrent.string"
local torrents = array()
local uploads = "/etc/luci-uploads/rtorrent"
local form, uri, file, dir, tags, start
common.remove_cookie("rtorrent-notifications")

form = SimpleForm("rtorrent", "Add Torrent")
form.template = "rtorrent/simpleform"
form.submit = "Add"
form.notifications = {}
form.parse = function(self, ...)
local state = SimpleForm.parse(self, ...)
if state == FORM_VALID then
if torrents:empty() then
uri:add_error(1, "missing")
file:add_error(1, "missing", "Either a torrent URL / magnet URI or "
.. "an uploaded torrent file must be provided!")
state = FORM_INVALID
else
for _, torrent in torrents:pairs() do
table.insert(form.notifications, "Added <i>%s</i>" % torrent:get("name"))
end
end
end
return state
end
form.notifications = common.get_cookie("rtorrent-notifications", {})
form.handle = function(self, state, data)
if state ~= FORM_NODATA and torrents:empty() then
uri:add_error(1, "missing")
file:add_error(1, "missing", "Either a torrent URL / magnet URI or "
.. "an uploaded torrent file must be provided!")
return true, FORM_INVALID
end
if state == FORM_VALID then
for _, torrent in torrents:pairs() do
torrent:set("start", data.start):set("directory", data.dir):set("tags", data.tags)
common.add_to_rtorrent(torrent)
table.insert(form.notifications, "Added <i>%s</i>" % torrent:get("name"))
end
uri:remove(1)
file:remove(1)
common.set_cookie("rtorrent-notifications", form.notifications)
luci.http.redirect(nixio.getenv("REQUEST_URI"))
end
return true
end
Expand Down Expand Up @@ -134,7 +127,9 @@ dir = form:field(Value, "dir", "Download directory")
dir.default = rtorrent.call("directory.default")
dir.rmempty = false
dir.validate = function(self, value, section)
if value and not datatypes.directory(value) then
if not value then
return nil, "Download directory must be provided!"
elseif not datatypes.directory(value) then
return nil, "Directory '" .. value .. "' does not exists!"
elseif not nixio.fs.access(value, "w") then
return nil, "Directory '" .. value .. "' write permission denied!"
Expand Down
19 changes: 19 additions & 0 deletions src/usr/lib/lua/luci/model/cbi/rtorrent/common.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ local http = require "socket.http"
local https = require "ssl.https"
local ltn12 = require "ltn12"
local nixio = require "nixio"
local util = require "luci.util"
local lhttp = require "luci.http"
local uci = require "luci.model.uci".cursor()
local build_url = require "luci.dispatcher".build_url
local datatypes = require "luci.cbi.datatypes"
local xmlrpc = require "xmlrpc"
local rtorrent = require "rtorrent"
Expand Down Expand Up @@ -294,3 +297,19 @@ function rss_downloader_status()
.. "rss_downloader, 60, 300, ((execute.throw, /usr/lib/lua/rss_downloader.lua, --uci))</code>"
end
end

function set_cookie(name, data, attributes)
attributes = attributes or ""
lhttp.header("Set-Cookie", "%s=%s; Path=%s; SameSite=Strict%s" % {
name, data and util.serialize_data(data):urlencode() or "", build_url("admin", "rtorrent"), attributes
})
end

function get_cookie(name, default)
local cookie = lhttp.getcookie(name)
return cookie and util.restore_data(cookie) or default
end

function remove_cookie(name)
set_cookie(name, nil, "; Max-Age=0")
end
8 changes: 2 additions & 6 deletions src/usr/lib/lua/luci/model/cbi/rtorrent/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,13 @@

local nixio = require "nixio"
local rtorrent = require "rtorrent"
local util = require "luci.util"
local common = require "luci.model.cbi.rtorrent.common"
local array = require "luci.model.cbi.rtorrent.array"
local build_url = require "luci.dispatcher".build_url
require "luci.model.cbi.rtorrent.string"

local compute, format, action, tab, sort, page = {}, {}, {}, unpack(arg)

luci.http.header("Set-Cookie", "rtorrent-main=%s; Path=%s; SameSite=Strict" % {
util.serialize_data({ tab, sort, page }):urlencode(), build_url("admin", "rtorrent")
})
common.set_cookie("rtorrent-main", { tab, sort, page })

tab = tab or "all"
local default_sort = "name-asc"
Expand Down Expand Up @@ -179,7 +175,7 @@ function action.start(hash)
elseif status.is_active == 0 then rtorrent.call("d.resume", hash) end
end

function action.pause(hash) rtorrent.call("d.pause", hash) end
function action.pause(hash) rtorrent.batchcall("d.", hash, "start", "pause") end
function action.stop(hash) rtorrent.batchcall("d.", hash, "stop", "close") end
function action.hash(hash) rtorrent.call("d.check_hash", hash) end
function action.remove(hash) rtorrent.batchcall("d.", hash, "close", "erase") end
Expand Down
12 changes: 4 additions & 8 deletions src/usr/lib/lua/luci/model/cbi/rtorrent/torrent/chunks.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@

local nixio = require "nixio"
local rtorrent = require "rtorrent"
local util = require "luci.util"
local build_url = require "luci.dispatcher".build_url
local common = require "luci.model.cbi.rtorrent.common"
local array = require "luci.model.cbi.rtorrent.array"
require "luci.model.cbi.rtorrent.string"

local hash, page = unpack(arg)
luci.http.header("Set-Cookie", "rtorrent-chunks=%s; Path=%s; SameSite=Strict" % {
util.serialize_data({ hash, page }):urlencode(), build_url("admin", "rtorrent")
})
common.set_cookie("rtorrent-chunks", { hash, page })

page = page and tonumber(page) or 1

local torrent = array(rtorrent.batchcall("d.", hash,
Expand Down Expand Up @@ -40,16 +38,14 @@ end)
local form, summary, list, offset
local chunks_count, chunk_size, completed_chunks, wanted_chunks, excluded_chunks, download_done

_G.redirect = build_url("admin", "rtorrent", "main",
unpack(util.restore_data(luci.http.getcookie("rtorrent-main"))))
_G.redirect = build_url("admin", "rtorrent", "main", unpack(common.get_cookie("rtorrent-main", {})))
form = SimpleForm("rtorrent", torrent:get("name"))
form.template = "rtorrent/simpleform"
form.submit = false
form.reset = false
form.all_tabs = array():append("info", "files", "trackers", "peers", "chunks"):get()
form.tab_url_postfix = function(tab)
local filters = tab == "chunks" and array(arg)
or array(util.restore_data(luci.http.getcookie("rtorrent-" .. tab) or ""))
local filters = (tab == "chunks") and array(arg) or array(common.get_cookie("rtorrent-" .. tab, {}))
return filters:get(1) == hash and filters:join("/") or hash
end
form.handle = function(self, state, data)
Expand Down
13 changes: 4 additions & 9 deletions src/usr/lib/lua/luci/model/cbi/rtorrent/torrent/files.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

local nixio = require "nixio"
local rtorrent = require "rtorrent"
local util = require "luci.util"
local build_url = require "luci.dispatcher".build_url
local common = require "luci.model.cbi.rtorrent.common"
local array = require "luci.model.cbi.rtorrent.array"
Expand All @@ -14,10 +13,8 @@ luci.dispatcher.context.requestpath = array(luci.dispatcher.context.requestpath)
local args = array():append(unpack(arg))
local compute, format, hash, sort, page, path = {}, {},
table.remove(arg, 1), table.remove(arg, 1), table.remove(arg), array(arg)
luci.http.header("Set-Cookie", "rtorrent-files=%s; Path=%s; SameSite=Strict" % {
util.serialize_data(array():append(hash, sort):append(unpack(path:get())):append(page):get()):urlencode(),
build_url("admin", "rtorrent")
})
common.set_cookie("rtorrent-files", array():append(hash, sort):append(unpack(path:get())):append(page):get())

sort = sort or "name-asc"
page = page and tonumber(page) or 1
local sort_column, sort_order = unpack(sort:split("-"))
Expand Down Expand Up @@ -162,14 +159,12 @@ local files = array(rtorrent.multicall("f.", hash, "/" .. array(path:get()):inse

local form, breadcrumb, list, icon, name, size, done, prio

_G.redirect = build_url("admin", "rtorrent", "main",
unpack(util.restore_data(luci.http.getcookie("rtorrent-main"))))
_G.redirect = build_url("admin", "rtorrent", "main", unpack(common.get_cookie("rtorrent-main", {})))
form = SimpleForm("rtorrent", torrent:get("name"))
form.template = "rtorrent/simpleform"
form.all_tabs = array():append("info", "files", "trackers", "peers", "chunks"):get()
form.tab_url_postfix = function(tab)
local filters = tab == "files" and args
or array(util.restore_data(luci.http.getcookie("rtorrent-" .. tab) or ""))
local filters = (tab == "files") and args or array(common.get_cookie("rtorrent-" .. tab, {}))
return filters:get(1) == hash and filters:join("/") or hash
end
form.handle = function(self, state, data)
Expand Down
7 changes: 3 additions & 4 deletions src/usr/lib/lua/luci/model/cbi/rtorrent/torrent/info.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

local nixio = require "nixio"
local rtorrent = require "rtorrent"
local util = require "luci.util"
local build_url = require "luci.dispatcher".build_url
local common = require "luci.model.cbi.rtorrent.common"
local array = require "luci.model.cbi.rtorrent.array"
require "luci.model.cbi.rtorrent.string"
Expand All @@ -15,13 +15,12 @@ local torrent = array(rtorrent.batchcall("d.", hash,
"name", "timestamp.started", "timestamp.finished", "message",
"custom1", "custom=icon", "custom=url", "custom=comment"))

_G.redirect = luci.dispatcher.build_url("admin", "rtorrent", "main",
unpack(util.restore_data(luci.http.getcookie("rtorrent-main"))))
_G.redirect = build_url("admin", "rtorrent", "main", unpack(common.get_cookie("rtorrent-main", {})))
form = SimpleForm("rtorrent", torrent:get("name"))
form.template = "rtorrent/simpleform"
form.all_tabs = array():append("info", "files", "trackers", "peers", "chunks"):get()
form.tab_url_postfix = function(tab)
local filters = array(util.restore_data(luci.http.getcookie("rtorrent-" .. tab) or ""))
local filters = array(common.get_cookie("rtorrent-" .. tab, {}))
return filters:get(1) == hash and filters:join("/") or hash
end
form.handle = function(self, state, data)
Expand Down
12 changes: 3 additions & 9 deletions src/usr/lib/lua/luci/model/cbi/rtorrent/torrent/peers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,13 @@

local rtorrent = require "rtorrent"
local json = require "luci.jsonc"
local util = require "luci.util"
local build_url = require "luci.dispatcher".build_url
local common = require "luci.model.cbi.rtorrent.common"
local array = require "luci.model.cbi.rtorrent.array"
require "luci.model.cbi.rtorrent.string"

local compute, format, hash, sort, page = {}, {}, unpack(arg)

luci.http.header("Set-Cookie", "rtorrent-peers=%s; Path=%s; SameSite=Strict" % {
util.serialize_data({ hash, sort, page }):urlencode(), build_url("admin", "rtorrent")
})
common.set_cookie("rtorrent-peers", { hash, sort, page })

local ip_location, country_flag, map = {}, {}, {}
sort = sort or "down_speed-desc"
Expand Down Expand Up @@ -181,16 +177,14 @@ local peers = array(rtorrent.multicall("p.", hash, "",

local form, list, icon, location, address, client, flags, done, down_speed, up_speed, downloaded, uploaded

_G.redirect = build_url("admin", "rtorrent", "main",
unpack(util.restore_data(luci.http.getcookie("rtorrent-main"))))
_G.redirect = build_url("admin", "rtorrent", "main", unpack(common.get_cookie("rtorrent-main", {})))
form = SimpleForm("rtorrent", torrent:get("name"))
form.template = "rtorrent/simpleform"
form.submit = false
form.reset = false
form.all_tabs = array():append("info", "files", "trackers", "peers", "chunks"):get()
form.tab_url_postfix = function(tab)
local filters = tab == "peers" and array(arg)
or array(util.restore_data(luci.http.getcookie("rtorrent-" .. tab) or ""))
local filters = (tab == "peers") and array(arg) or array(common.get_cookie("rtorrent-" .. tab, {}))
return filters:get(1) == hash and filters:join("/") or hash
end
form.handle = function(self, state, data)
Expand Down
42 changes: 26 additions & 16 deletions src/usr/lib/lua/luci/model/cbi/rtorrent/torrent/trackers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@
-- Licensed to the public under the GNU General Public License.

local rtorrent = require "rtorrent"
local util = require "luci.util"
local build_url = require "luci.dispatcher".build_url
local common = require "luci.model.cbi.rtorrent.common"
local array = require "luci.model.cbi.rtorrent.array"
require "luci.model.cbi.rtorrent.string"

local compute, format, hash, sort, page = {}, {}, unpack(arg)
luci.http.header("Set-Cookie", "rtorrent-trackers=%s; Path=%s; SameSite=Strict" % {
util.serialize_data({ hash, sort, page }):urlencode(), build_url("admin", "rtorrent")
})
common.set_cookie("rtorrent-trackers", { hash, sort, page })
common.remove_cookie("rtorrent-notifications")

sort = sort or "status-asc"
page = page and tonumber(page) or 1
local sort_column, sort_order = unpack(sort:split("-"))
Expand All @@ -23,6 +22,7 @@ function compute_total(tracker, index, trackers, total)
total:increment("seeds", tracker:get("seeds"))
total:increment("leeches", tracker:get("leeches"))
total:increment("downloaded", tracker:get("downloaded"))
total:get("urls"):insert(tracker:get("url"))
if trackers:last(index) then
format_values(total:set(".total_row", true)
:set("url", "TOTAL: %d pcs." % total:get("count"))
Expand Down Expand Up @@ -101,7 +101,7 @@ function format.peers(value)
}
end

local total = array():set("count", 0)
local total = array():set("count", 0):set("urls", array())
local torrent = array(rtorrent.batchcall("d.", hash, "name", "state", "is_active"))
local trackers = array(rtorrent.multicall("t.", hash, "",
"is_enabled", "url", "latest_new_peers", "latest_sum_peers",
Expand All @@ -116,18 +116,20 @@ local trackers = array(rtorrent.multicall("t.", hash, "",

local form, list, icon, url, status, peers, seeds, leeches, downloaded, updated, enabled, add

_G.redirect = build_url("admin", "rtorrent", "main",
unpack(util.restore_data(luci.http.getcookie("rtorrent-main"))))
_G.redirect = build_url("admin", "rtorrent", "main", unpack(common.get_cookie("rtorrent-main", {})))
form = SimpleForm("rtorrent", torrent:get("name"))
form.template = "rtorrent/simpleform"
form.notifications = common.get_cookie("rtorrent-notifications", {})
form.all_tabs = array():append("info", "files", "trackers", "peers", "chunks"):get()
form.tab_url_postfix = function(tab)
local filters = tab == "trackers" and array(arg)
or array(util.restore_data(luci.http.getcookie("rtorrent-" .. tab) or ""))
local filters = (tab == "trackers") and array(arg) or array(common.get_cookie("rtorrent-" .. tab, {}))
return filters:get(1) == hash and filters:join("/") or hash
end
form.handle = function(self, state, data)
if state == FORM_VALID then luci.http.redirect(nixio.getenv("REQUEST_URI")) end
if state == FORM_VALID then
common.set_cookie("rtorrent-notifications", form.notifications)
luci.http.redirect(nixio.getenv("REQUEST_URI"))
end
return true
end
form.cancel = "Trigger tracker scrape"
Expand Down Expand Up @@ -194,17 +196,21 @@ enabled.write = function(self, section, value)
end
end

add = form:field(TextValue, "add_tracker", "Add tracker(s)",
"All tracker URL should be in a separate line.")
add = form:field(TextValue, "add_tracker", "Add tracker(s)", "All tracker URL should be in a separate line.")
add.rows = 2
add.validate = function(self, value, section)
local errors = array()
for _, line in ipairs(value:split("\r\n")) do
if not line:trim():lower():match("^%w+://[%w_-]+%.[%w_-]+") then
errors:insert("Invalid URL: %s" % line:trim())
elseif total:get("urls"):contains(line:trim()) then
table.insert(form.notifications, "Skipped existing tracker <i>%s</i>" % line:trim())
else
table.insert(form.notifications, "Added tracker <i>%s</i>" % line:trim())
end
end
if not errors:empty() then
form.notifications = {}
for i, err in errors:pairs() do
if not errors:last(i) then self:add_error(section, err) end
end
Expand All @@ -213,11 +219,15 @@ add.validate = function(self, value, section)
return value
end
add.write = function(self, section, value)
local tracker_count = trackers:filter(function(value) return value:get("index") end):size()
for i, line in ipairs(value:split("\r\n")) do
rtorrent.call("d.tracker.insert", hash, (tracker_count + i - 1) % 33, line:trim())
local tracker_group = trackers:filter(function(value) return value:get("index") end):size()
local tracker_added = false
for _, line in ipairs(value:split("\r\n")) do
if not total:get("urls"):contains(line:trim()) then
rtorrent.call("d.tracker.insert", hash, tracker_group, line:trim())
tracker_group, tracker_added = (tracker_group + 1) % 33, true
end
end
if torrent:get("state") > 0 then
if tracker_added and torrent:get("state") > 0 then
if torrent:get("is_active") == 0 then
rtorrent.batchcall("d.", hash, "stop", "close", "start", "pause")
else
Expand Down

0 comments on commit 422b2b5

Please sign in to comment.