This commit is contained in:
hummypkg 2014-06-09 22:24:50 +00:00
parent 72076be0bc
commit 1eaff8d5d2
16 changed files with 341 additions and 47 deletions

View File

@ -1,9 +1,9 @@
Package: sweeper
Priority: optional
Section: misc
Version: 1.0.15
Version: 1.0.17
Architecture: mipsel
Maintainer: af123@hummypkg.org.uk
Depends: webif(>=1.0.14-4)
Depends: webif(>=1.0.14-6)
Description: Automatically manage single recording files. [Web Interface. Multi-folder support.]
Tags: http://hummy.tv/forum/threads/3843/

View File

@ -1,5 +1,6 @@
set ::sweeper::cf "/mod/etc/sweeper.conf"
set ::sweeper::dryrun 0
proc ::sweeper::unknown {cmd args} {
log "Unknown sweeper rule clause '$cmd'" 0
@ -23,6 +24,8 @@ proc ::sweeper::expand {ts str} {
}
set timestamp [clock format [$ts get start] -format "%Y%m%d%H%M%S"]
set yyyymmdd [string range $timestamp 0 7]
set hhmm [string range $timestamp 8 11]
set map [list \
"%title" [$ts get title] \
@ -32,10 +35,12 @@ proc ::sweeper::expand {ts str} {
"%channel" [$ts get channel_name] \
"%duration" [$ts duration] \
"%timestamp" $timestamp \
"%yyyymmdd" $yyyymmdd \
"%hhmm" $hhmm \
]
set ret [string map $map $str]
log " Expanded to \[$ret]" 2
log " Expanded \[$str] -> \[$ret]" 2
return $ret
}
@ -100,6 +105,10 @@ proc ::sweeper::definition {ts def} {
return [::sweeper::strcontains [$ts get definition] $def]
}
proc ::sweeper::filename {ts str} {
return [::sweeper::strcontains [$ts bfile] $str]
}
proc ::sweeper::title {ts str} {
return [::sweeper::strcontains [$ts get title] $str]
}
@ -162,35 +171,56 @@ proc ::sweeper::action {ts cmds} {
log " ... No such directory $root/$rest" 2
return 1
}
system mkdir_p "$root/$rest"
if {![file isdirectory "$root/$rest"]} {
log "Error creating directory $root/$rest" 1
return 1
} else {
log " ... created directory $root/$rest" 2
if {!$::sweeper::dryrun} {
system mkdir_p "$root/$rest"
if {![file isdirectory "$root/$rest"]} {
log "Error creating $root/$rest" 1
return 1
} else {
log " ... created $root/$rest" 2
}
}
}
log "Moving [$ts get file] to $rest" 0
foreach f [$ts fileset] {
log " ....... $f"
file rename $f "$root/$rest/[file tail $f]"
if {"$root/$rest" ni $::sweeper::recalc} {
lappend ::sweeper::recalc "$root/$rest"
if {!$::sweeper::dryrun} {
file rename $f "$root/$rest/[file tail $f]"
if {"$root/$rest" ni $::sweeper::recalc} {
lappend ::sweeper::recalc "$root/$rest"
}
}
}
return 1
}
renamefile {
set rest [system filename [::sweeper::expand $ts $rest]]
set file [$ts get file]
log "Renaming [$ts get file] to $rest" 0
if {[file exists "[file dirname $file]/$rest.ts"]} {
log "... ERROR Target already exists" 0
return 0
}
if {!$::sweeper::dryrun} {
ts renamegroup $file $rest
}
return 0
}
lock {
if {![$ts flag Locked]} {
log "Locked [$ts get file]" 0
$ts lock
if {!$::sweeper::dryrun} {
$ts lock
}
}
return 0
}
unlock {
if {[$ts flag Locked]} {
log "Unlocked [$ts get file]" 0
$ts unlock
if {!$::sweeper::dryrun} {
$ts unlock
}
}
return 0
}
@ -305,15 +335,19 @@ proc ::sweeper::folder_action {ts cmds} {
log " ... No such directory $root/$rest" 2
return 1
}
system mkdir_p "$root/$rest"
if {![file isdirectory "$root/$rest"]} {
log "Error creating directory $root/$rest" 1
return 1
} else {
log " ... created directory $root/$rest" 2
if {!$::sweeper::dryrun} {
system mkdir_p "$root/$rest"
if {![file isdirectory "$root/$rest"]} {
log "Error creating $root/$rest" 1
return 1
} else {
log " ... created $root/$rest" 2
}
}
}
::sweeper:folder_merge $folder "$root/$rest"
if {!$::sweeper::dryrun} {
::sweeper:folder_merge $folder "$root/$rest"
}
return 1
}
fileundercreate -
@ -330,12 +364,33 @@ proc ::sweeper::folder_action {ts cmds} {
if {$cmd ne "fileundercreate"} { return 1 }
set target "$root/$rest/$lfolder"
log "Creating $target" 0
system mkdir_p $target
if {!$::sweeper::dryrun} {
system mkdir_p $target
}
}
if {!$::sweeper::dryrun} {
::sweeper:folder_merge $folder $target
}
::sweeper:folder_merge $folder $target
return 1
}
renamefile {
::sweeper::folder_apply $folder [lambda {ts} {
set rest [system filename \
[::sweeper::expand $ts $rest]]
set file [$ts get file]
log "Renaming [$ts get file] to $rest" 0
if {[file exists "[file dirname $file]/$rest.ts"]} {
log "... ERROR Target already exists" 0
return 0
}
if {!$::sweeper::dryrun} {
ts renamegroup $file $rest
}
}
return 0
}
lock {
if {$::sweeper::dryrun} { return 0 }
::sweeper::folder_apply $folder [lambda {ts} {
if {![$ts flag Locked]} {
log "Locked [$ts get file]" 0
@ -345,6 +400,7 @@ proc ::sweeper::folder_action {ts cmds} {
return 0
}
unlock {
if {$::sweeper::dryrun} { return 0 }
::sweeper::folder_apply $folder [lambda {ts} {
if {[$ts flag Locked]} {
log "Unlocked [$ts get file]" 0
@ -373,12 +429,19 @@ proc ::sweeper::runrule {ts rule} {
while {[llength $rule] > 1} {
set rule [lassign $rule cmd arg]
log " $cmd\($arg)" 2
if {[string index $cmd 0] eq "!"} {
set negate 1
set cmd [string range $cmd 1 end]
} else {
set negate 0
}
if {$folder && [exists -proc ::sweeper::folder_$cmd]} {
set ret [::sweeper::folder_$cmd $ts $arg]
} else {
set ret [::sweeper::$cmd $ts $arg]
}
if {$cmd eq "action"} { return $ret }
if {$negate} { set ret $(!$ret) }
if {!$ret} {
log " Nomatch" 2
break
@ -454,11 +517,27 @@ proc ::sweeper::apply {dir cf} {
log "==== folder $entry ====" 2
log "" 2
if {[file exists "$entry/.nosweep"]} {
log "No-sweep folder." 2
continue
}
if {![file exists "$entry/.series"]} {
log "Not series folder." 2
continue
}
# Check if the .series entry is a real one versus one created
# by ts resetnew
set fd [open "$entry/.series"]
set bytes [read $fd]
close $fd
set sbytes [unpack $bytes -uintle 160 32]
if {$sbytes == 0} {
log ".series file was auto-created, skipping folder."
continue
}
set ts 0
foreach de [readdir -nocomplain $entry] {
set dentry "$entry/$de"

View File

@ -4,3 +4,8 @@
background-image: url(/plugin/sweeper/img/icon16.png);
}
.contextMenu li.nosweep a
{
background-image: url(/plugin/sweeper/img/noicon22.png);
}

View File

@ -3,3 +3,5 @@ jscss /plugin/sweeper/browse.js /plugin/sweeper/browse.css
lappend plugins(dmenu) {sweeper {desc "Sweeper Rules"}}
lappend plugins(dmenu) {nosweep {desc "Toggle no-sweep flag"}}

View File

@ -3,3 +3,7 @@ plugins.dmenu.sweeper = function(dir) {
window.location = '/plugin/sweeper/edit.jim?dir=' + dir;
};
plugins.dmenu.nosweep = function(dir, iconset, results, el) {
flagdir(dir, 'nosweep', iconset, results, el);
};

View File

@ -3,3 +3,7 @@ if {$dir eq [system mediaroot] || [file exists "$dir/.sweeper"]} {
lappend icons [_addicon "/plugin/sweeper/img/icon22.png" "Sweeper Rules"]
}
if {[file exists "$dir/.nosweep"]} {
lappend icons [_addicon "/plugin/sweeper/img/noicon22.png" "No-sweep"]
}

View File

@ -35,6 +35,7 @@ puts {
<button id=b_save>Save changes</button>
<button id=b_revert>Discard changes</button>
<button id=b_show>View configuration</button>
<button id=b_test>Test configuration</button>
<button id=b_raw>Toggle raw display</button>
</div>
<div id=output></div>
@ -141,12 +142,31 @@ Add pre-defined ruleset:
<input name=newrule_comment id=newrule_comment size=80 maxlength=255 />
</div>
<div class=hidden id=newcondition title="Add New Condition">
<table>
<tr><th>Condition:</th><td>
<select id=newcondition_cmd name=newcondition_cmd></select>
</td></tr><tr><th>Negate?</th><td>
<input id=newcondition_negate name=newcondition_negate type=checkbox
value='1' />
</td></tr>
</table>
</div>
<div class=hidden id=edit_action title="Rule Action">
<select name=edit_action_act id=edit_action_act>
</select>
<input name=edit_action_arg id=edit_action_arg size=60 maxlength=255 />
</div>
<div class=hidden id=testresults title="Test current ruleset">
<span class=blood>
The following output shows what would happen if these rules were applied.
No changes have been made.
</span>
<div id=testresults_inner></div>
</div>
<div class=hidden id=empty_rulebase>
<b>There are no rules currently defined for this folder.</b>
<br>

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

5
webif/plugin/sweeper/menu.hook Executable file
View File

@ -0,0 +1,5 @@
menuitem \
"Sweeper" \
"/plugin/sweeper/img/icon256.png" \
"/plugin/sweeper/edit.jim"

View File

@ -76,12 +76,19 @@ proc rule {id rule} {
set lockfound -1
while {[llength $rule] > 1} {
set rule [lassign $rule cmd arg]
if {[string index $cmd 0] eq "!"} {
set negate 1
set cmd [string range $cmd 1 end]
} else {
set negate 0
}
if {$cmd eq "action"} break
if {$cmd eq "lock"} { set lockfound $arg }
if {$c} { add_json ",\n" }
incr c
add_json " {\n"
add_json " \"negate\": $negate,\n"
add_json " \"cmd\": \"[quot $cmd]\",\n"
add_json " \"arg\": \"[quot $arg]\"\n"
add_json " }"

View File

@ -46,18 +46,21 @@ var schema = {
'class': 'all',
type: 'substr',
desc: 'Recording Title contains',
idesc: 'Recording Title does not contain',
def: 'Enter text here...'
},
synopsis: {
'class': 'all',
type: 'substr',
desc: 'Synopsis contains',
idesc: 'Synopsis does not contain',
def: 'Enter text here...'
},
guidance: {
'class': 'all',
type: 'substr',
desc: 'Guidance Text contains',
idesc: 'Guidance Text does not contain',
def: 'Enter text here...'
},
genre: {
@ -91,6 +94,7 @@ var schema = {
'class': 'all',
type: 'select',
desc: 'Recording Flagged as',
idesc: 'Recording not Flagged as',
select: {
Locked: 'Locked',
New: 'New',
@ -113,11 +117,18 @@ var schema = {
def: 1,
deprecated: true
},
filename: {
'class': 'all',
type: 'substr',
desc: 'Filename contains',
def: 'Enter text here...'
},
fflag: {
'class': 'folder',
type: 'string',
desc: 'Folder flagged as',
def: 'noflatten'
idesc: 'Folder not flagged as',
def: 'nosweep'
}
},
action: {
@ -169,6 +180,12 @@ var schema = {
desc: 'Merge into or create folder of ' +
'same name found under...',
continues: false
},
renamefile: {
'class': 'all',
argtype: 'string',
desc: 'Rename recording files to...',
continue: true
}
}
};

View File

@ -101,11 +101,14 @@ function ruleconf(rule)
rule.find('tr.clause').each(function(i) {
cmd = $(this).find('th.cmd').attr('cmd');
negate = $(this).find('th.cmd').attr('negate');
c = schema.criterion[cmd];
val = $.trim($(this).find('td.val').html());
if (getters[c.type])
val = getters[c.type](cmd, val);
if (negate == '1')
s += '!';
s += cmd + ' ' + quot(val) + ' ';
});
@ -156,14 +159,32 @@ function rulerefresh(rule)
}
}
function critdesc(cmd, negate)
{
var c = schema.criterion[cmd];
if (!c)
return 'Unknown (' + cmd + ')';
if (negate == '1' && c.idesc)
return c.idesc;
else if (negate == '1')
return c.desc + ' is not';
else
return c.desc;
}
function criterion(data)
{
var c = schema.criterion[data.cmd];
var s;
if (!c)
{
alert('Unknown Criterion (' + data.cmd + ')');
return;
}
s = '<tr class=clause><th class=title>And:</th>' +
'<th class=cmd cmd=' + data.cmd + '>' +
c.desc + '</th><td class=val>';
'<th class=cmd negate=' + data.negate + ' cmd=' + data.cmd + '>' +
critdesc(data.cmd, data.negate) + '</th><td class=val>';
if (setters[c.type])
s += setters[c.type](data.cmd, data.arg);
@ -185,6 +206,12 @@ function action(data)
var c = schema.action[data.cmd];
var s;
if (!c)
{
alert('Unknown action - ' + data.cmd);
return '';
}
if (c.argtype == 'folder' && !data.arg)
data.arg = mroot;
@ -252,9 +279,6 @@ function addrule(id, data)
$('#ruleset').append(rule);
// rule.find('button.addcriterion')
// .button({icons: { primary: "ui-icon-plus"}});
rulerefresh(rule);
return rule;
@ -519,6 +543,29 @@ $('#b_show').button({icons: {primary: "ui-icon-clipboard"}})
});
});
$('#b_test').button({icons: {primary: "ui-icon-check"}})
.on('click', function(e) {
e.preventDefault();
$('#testresults_inner').empty()
.html('<img src=/img/loading.gif> Running rules... Please wait...');
$('#testresults').dialog({
height: '600', width: '800',
draggable: true, resizable: true,
autoOpen: true,
buttons: {
"Close": function() {
$(this).dialog('close');
}
}
});
$.post('test.jim', {
dir: $('span.dir').text(),
data: conffile()
}, function(data) {
$('#testresults_inner').text(data);
});
});
$('#b_raw').button({icons: {primary: "ui-icon-gear"}})
.on('click', function(e) {
$('#ruleset div.raw').toggle('slow');
@ -552,6 +599,56 @@ $('#macros').on('click', '#b_macro', function(e) {
changed(0);
function addcriterion(rule)
{
var type = rule.attr('type');
if (type == 'folder')
options = select_folder_criteria;
else
options = select_file_criteria;
// Populate select box and reset form.
$('#newcondition_cmd').empty();
$.each(options, function(k, v) {
$('#newcondition_cmd').append(
$('<option></option>').attr('value', k).html(v)
);
});
$('#newcondition_negate').prop('checked', false);
$('#newcondition').dialog({
height: 'auto', width: 'auto',
draggable: false, resizable: false,
autoOpen: true,
position: {
my: 'bottom left',
at: 'top right',
of: rule
},
buttons: {
"Add Condition": function() {
$(this).dialog('close');
var id = rule.attr('id');
var val = $('#newcondition_cmd').val();
var negate = $('#newcondition_negate')
.prop('checked') ? '1' : '0';
rule.find('table.criteria tbody')
.append(criterion({
cmd: val,
negate: negate,
arg: schema.criterion[val].def
}));
changed(1);
rulerefresh(rule);
},
"Cancel": function() {
$(this).dialog('close');
}
}
});
}
$('#ruleset')
.on('click', 'a.editclause', function(e) {
e.preventDefault();
@ -559,24 +656,7 @@ $('#ruleset')
})
.on('click', 'a.addcriterion', function(e) {
e.preventDefault();
var rule = $(this).closest('div.rule');
var type = rule.attr('type');
if (type == 'folder')
options = select_folder_criteria;
else
options = select_file_criteria;
edit_select(rule, 'Add new condition',
options, '', function(rule, val) {
var id = rule.attr('id');
rule.find('table.criteria tbody')
.append(criterion({
cmd: val,
arg: schema.criterion[val].def
}));
changed(1);
rulerefresh(rule);
});
addcriterion($(this).closest('div.rule'));
})
.on('click', 'a.editcomment', function(e) {
e.preventDefault();

View File

@ -148,3 +148,9 @@ tr.blank td
line-height: 7px;
}
#testresults_inner
{
overflow: auto;
white-space: pre;
}

62
webif/plugin/sweeper/test.jim Executable file
View File

@ -0,0 +1,62 @@
#!/mod/bin/jimsh
package require cgi
source /mod/webif/lib/setup
require lock system.class ts.class tdelete pretty_size browse.class \
safe_delete settings.class plugin
httpheader
set dir [cgi_get dir ""]
set root [system mediaroot]
set cf "/tmp/sweepertest.cf"
set data [string map {
&amp; &
&lt; <
&gt; >
} [cgi_get data "-"]]
if {$data eq "-" || $data eq ""} {
puts "No data sent!"
exit
}
if {[catch {set fp [open $cf w]} msg]} {
puts "Error, $msg"
exit
}
$fp puts -nonewline $data
close $fp
proc log {msg {level 1}} {
puts $msg
}
proc elapsed {start} {
return $(([clock milliseconds] - $start) / 1000.0)
}
proc startclock {} {
set ::startclock_s [clock milliseconds]
}
proc endclock {size} {
set el [elapsed $::startclock_s]
set rate $($size / $el)
return "[pretty_size $size] in $el seconds - [pretty_size $rate]/s"
}
proc register {args} {}
set root [system mediaroot]
source auto.hook
set ::sweeper::dryrun 1
::sweeper::apply $dir $cf
file delete $cf

View File

@ -0,0 +1,3 @@
tb "/plugin/sweeper/img/icon256.png" "Sweeper" "/plugin/sweeper/edit.jim"