From d5319e6f0d8761797d297ff3ba028889a03744a2 Mon Sep 17 00:00:00 2001 From: hummypkg Date: Thu, 12 Jun 2014 17:45:11 +0000 Subject: [PATCH] sweeper 2.0.0 + add and/or, tooltips, moveset --- CONTROL/conffiles | 1 - CONTROL/control | 6 +- webif/plugin/sweeper/auto.hook | 241 +++++++++++++++++++++------- webif/plugin/sweeper/edit.jim | 15 +- webif/plugin/sweeper/harness.jim | 45 ++++++ webif/plugin/sweeper/img/plus.png | Bin 0 -> 1188 bytes webif/plugin/sweeper/rules_json.jim | 54 +++++-- webif/plugin/sweeper/schema.js | 118 +++++++++----- webif/plugin/sweeper/script.js | 153 ++++++++++++++---- webif/plugin/sweeper/style.css | 6 + webif/plugin/sweeper/test | 50 +----- webif/plugin/sweeper/test.jim | 29 +--- 12 files changed, 494 insertions(+), 224 deletions(-) delete mode 100644 CONTROL/conffiles create mode 100755 webif/plugin/sweeper/harness.jim create mode 100644 webif/plugin/sweeper/img/plus.png diff --git a/CONTROL/conffiles b/CONTROL/conffiles deleted file mode 100644 index 941459f..0000000 --- a/CONTROL/conffiles +++ /dev/null @@ -1 +0,0 @@ -etc/sweeper.conf diff --git a/CONTROL/control b/CONTROL/control index d9545f2..ed42be4 100644 --- a/CONTROL/control +++ b/CONTROL/control @@ -1,9 +1,9 @@ Package: sweeper Priority: optional Section: misc -Version: 1.0.19 +Version: 2.0.0 Architecture: mipsel Maintainer: af123@hummypkg.org.uk -Depends: webif(>=1.0.14-8) -Description: Sweeper is a package for managing recordings in a variety of ways using custom rules [Add delete action. Some online help.] +Depends: webif(>=1.0.14-10) +Description: Sweeper is a package for managing recordings in a variety of ways using custom rules [Add and/or clauses. See forum.] Tags: http://hummy.tv/forum/threads/5138/ diff --git a/webif/plugin/sweeper/auto.hook b/webif/plugin/sweeper/auto.hook index d7097a3..e610af8 100644 --- a/webif/plugin/sweeper/auto.hook +++ b/webif/plugin/sweeper/auto.hook @@ -69,67 +69,135 @@ proc ::sweeper::strcontains {ref val} { >= 0] } -###################################################################### -# Rule criteria +proc ::sweeper::moveset {ts dst {op rename}} { + set file [$ts get file] + log "${op}set($file) -> $dst" 0 -proc ::sweeper::flag {ts flag} { + # Handle alias for rename + if {$op eq "move"} { set op "rename" } + + # Determine whether this is a cross-filesystem move. + file stat [$ts get file] sts + file stat $dst std + set xfs 0 + if {$sts(dev) ne $std(dev)} { + log " Cross-filesystem - will copy then delete." 0 + set xfs 1 + } + + set fset [lsort [$ts fileset]] + + if {$xfs && $op eq "rename"} { + # For cross-filesystem moves, copy the whole file set + # and then delete the originals if all the copies were + # successful. + foreach f $fset { + set tail [file tail $f] + if {[catch {file copy $f "$dst/$tail"} msg]} { + log " ....... $f: XFS copy failed, $msg." 0 + file delete -force "$dst/$tail" + log " .... Leaving originals intact." 0 + return + } + log " ....... $f: OK" 0 + } + log " Now deleting original files." 0 + foreach f $fset { + set tail [file tail $f] + if {[file exists "$dst/$tail"] && + [file size "$dst/$tail"] == + [file size $f]} { + file tdelete $f + log " ....... $f: OK" 0 + } else { + log " ....... $f: ERROR, sizes differ." 0 + return + } + } + return + } + + # Otherwise - copy or local FS move. + + foreach f $fset { + set tail [file tail $f] + if {$op eq "copy" && [file exists "$dst/$tail"]} { + if {[file size "$dst/$tail"] == [file size $f]} { + log " ....... $f: Already copied." 2 + continue + } + log "Deleting truncated copy $dst/$tail" 0 + file tdelete "$dst/$tail" + } + log " ....... $f" + if {[catch {file $op $f "$dst/[file tail $f]"} msg]} { + log "$op failed, $msg.", 0 + return + } + } +} + +###################################################################### +# Rule conditions + +proc ::sweeper::flag {ts flag folder} { return [$ts flag $flag] } -proc ::sweeper::lcn {ts num} { +proc ::sweeper::lcn {ts num folder} { return [::sweeper::intcomp [$ts get channel_num] $num] } -proc ::sweeper::duration {ts dur} { +proc ::sweeper::duration {ts dur folder} { return [::sweeper::intcomp [$ts duration] $dur] } -proc ::sweeper::hour {ts str} { +proc ::sweeper::hour {ts str folder} { set hour [clock format [$ts get start] -format "%H"] return [::sweeper::intcomp $hour $str] } -proc ::sweeper::schedduration {ts dur} { +proc ::sweeper::schedduration {ts dur folder} { return [::sweeper::intcomp [$ts get scheddur] $dur] } -proc ::sweeper::size {ts size} { +proc ::sweeper::size {ts size folder} { return [::sweeper::intcomp [$ts size] $size] } -proc ::sweeper::age {ts age} { +proc ::sweeper::age {ts age folder} { set recage $(([clock seconds] - [$ts get end]) / 3600) log " ... Recording age: $recage" 2 return [::sweeper::intcomp $recage $age] } -proc ::sweeper::wage {ts age} { +proc ::sweeper::wage {ts age folder} { set recage $(([clock seconds] - [$ts lastmod]) / 3600) log " ... Watched age: $recage" 2 return [::sweeper::intcomp $recage $age] } -proc ::sweeper::definition {ts def} { +proc ::sweeper::definition {ts def folder} { return [::sweeper::strcontains [$ts get definition] $def] } -proc ::sweeper::filename {ts str} { +proc ::sweeper::filename {ts str folder} { return [::sweeper::strcontains [$ts bfile] $str] } -proc ::sweeper::title {ts str} { +proc ::sweeper::title {ts str folder} { return [::sweeper::strcontains [$ts get title] $str] } -proc ::sweeper::synopsis {ts str} { +proc ::sweeper::synopsis {ts str folder} { return [::sweeper::strcontains [$ts get synopsis] $str] } -proc ::sweeper::guidance {ts str} { +proc ::sweeper::guidance {ts str folder} { return [::sweeper::strcontains [$ts get guidance] $str] } -proc ::sweeper::genre {ts genre} { +proc ::sweeper::genre {ts genre folder} { set glist [ts genrelist] set tsg [$ts get genre] @@ -139,7 +207,7 @@ proc ::sweeper::genre {ts genre} { return 0 } -proc ::sweeper::lock {ts g} { +proc ::sweeper::lock {ts g folder} { if {$g} { if {![$ts flag Locked]} { log "Locked recording." 0 @@ -154,13 +222,13 @@ proc ::sweeper::lock {ts g} { return 1 } -proc ::sweeper::folder_fflag {ts flag} { +proc ::sweeper::folder_fflag {ts flag folder} { return [file exists "[file dirname [$ts get file]]/.$flag"] } ###################################################################### -proc ::sweeper::action {ts cmds} { +proc ::sweeper::action {ts cmds folder} { global root lassign $cmds cmd rest @@ -171,12 +239,19 @@ proc ::sweeper::action {ts cmds} { continue { return 0 } stop { return 1 } preserve { return 1 } + copy - + copycreate - move - movecreate { set rest [::sweeper::expand $ts $rest] set dir [::sweeper::resolvedir $rest] + set create 0 + if {[string range $cmd end-5 end] eq "create"} { + set create 1 + set cmd [string range $cmd 0 end-6] + } if {![file isdirectory $dir]} { - if {$cmd eq "move"} { + if {!$create} { log " ... No such directory $dir" 2 return 1 } @@ -190,14 +265,11 @@ proc ::sweeper::action {ts cmds} { } } } - log "Moving [$ts get file] to $rest" 0 - foreach f [$ts fileset] { - log " ....... $f" - if {!$::sweeper::dryrun} { - file rename $f "$dir/[file tail $f]" - if {"$dir" ni $::sweeper::recalc} { - lappend ::sweeper::recalc $dir - } + log "$cmd [$ts get file] to $rest" 0 + if {!$::sweeper::dryrun} { + ::sweeper::moveset $ts $dir $cmd + if {"$dir" ni $::sweeper::recalc} { + lappend ::sweeper::recalc $dir } } return 1 @@ -291,7 +363,8 @@ proc ::sweeper::folder_apply {dir callback} { proc ::sweeper:folder_merge {src dst {op rename}} { if {$src eq $dst} return - log "Moving recordings from $src to $dst" 0 + if {$op eq "move"} { set op "rename" } + log "$op recordings from $src to $dst" 0 foreach e [readdir -nocomplain $src] { if {![string match {*.ts} $e]} continue set entry "$src/$e" @@ -313,12 +386,7 @@ proc ::sweeper:folder_merge {src dst {op rename}} { continue } - foreach f [$ts fileset] { - if {$op eq "copy" \ - && [file exists "$dst/[file tail $f]"]} continue - log " ....... $f" - file $op $f "$dst/[file tail $f]" - } + ::sweeper::moveset $ts $dst $op } if {$op eq "rename" && ![system rmdir_if_empty $src]} { log "Failed to remove directory" 0 @@ -331,7 +399,7 @@ proc ::sweeper:folder_merge {src dst {op rename}} { } } -proc ::sweeper::folder_action {ts cmds} { +proc ::sweeper::folder_action {ts cmds folder} { global root lassign $cmds cmd rest @@ -345,12 +413,19 @@ proc ::sweeper::folder_action {ts cmds} { continue { return 0 } stop { return 1 } preserve { return 1 } + copycreate - + copy - movecreate - move { set rest [::sweeper::expand $ts $rest] set dir [::sweeper::resolvedir $rest] + set create 0 + if {[string range $cmd end-5 end] eq "create"} { + set create 1 + set cmd [string range $cmd 0 end-6] + } if {![file isdirectory $dir]} { - if {$cmd eq "move"} { + if {!$create} { log " ... No such directory $dir" 2 return 1 } @@ -365,7 +440,7 @@ proc ::sweeper::folder_action {ts cmds} { } } if {!$::sweeper::dryrun} { - ::sweeper:folder_merge $folder $dir + ::sweeper:folder_merge $folder $dir $cmd } return 1 } @@ -450,6 +525,64 @@ proc ::sweeper::folder_action {ts cmds} { return 0 } +proc ::sweeper::or {ts clause folder} { + log " --> OR:" 2 + set ret 0 + while {[llength $clause] > 1} { + set clause [lassign $clause cmd arg] + set ret [::sweeper::clause $folder $cmd $arg $ts] + if {$ret} { + log " <-- OR true." 2 + break + } + } + if {!$ret} { + log " <-- OR false." 2 + } + return $ret +} + +proc ::sweeper::and {ts clause folder} { + log " --> AND:" 2 + set ret 0 + while {[llength $clause] > 1} { + set clause [lassign $clause cmd arg] + set ret [::sweeper::clause $folder $cmd $arg $ts] + if {!$ret} { + log " <-- AND false." 2 + break + } + } + if {$ret} { + log " <-- AND true." 2 + } + return $ret +} + +proc ::sweeper::clause {folder cmd arg ts} { + 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 $folder] + } else { + set ret [::sweeper::$cmd $ts $arg $folder] + } + if {$cmd eq "action"} { return $ret } + if {$negate} { set ret $(!$ret) } + if {!$ret} { + log " Nomatch" 2 + } else { + log " MATCH" 2 + } + return $ret +} + proc ::sweeper::runrule {ts rule} { log "Processing \[$rule]" 2 @@ -462,25 +595,9 @@ 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] - } + set ret [::sweeper::clause $folder $cmd $arg $ts] if {$cmd eq "action"} { return $ret } - if {$negate} { set ret $(!$ret) } - if {!$ret} { - log " Nomatch" 2 - break - } - log " MATCH" 2 + if {!$ret} break } return 0 @@ -498,12 +615,18 @@ proc ::sweeper::apply {dir cf} { $fp close set runfolder 0 + set nrules 0 foreach rule $rules { if {[lindex $rule 0] eq "folder"} { incr runfolder } + if {[string index $rule 0] ne "#" && [llength $rule] > 1} { + incr nrules + } } + if {!$nrules} return + log "" 2 - log "--- SWEEP SCAN STARTING FOR $dir ---" 2 + log "--- SWEEP SCAN STARTING FOR $dir ($nrules) ---" 2 log "" 2 foreach e [readdir -nocomplain $dir] { @@ -530,7 +653,7 @@ proc ::sweeper::apply {dir cf} { foreach rule $rules { if {[lindex $rule 0] eq "folder"} continue - if {[string index $rule 0] eq "#" || \ + if {[string index $rule 0] eq "#" || [llength $rule] < 2} continue if {[::sweeper::runrule $ts $rule]} break } @@ -608,7 +731,7 @@ proc ::sweeper::apply {dir cf} { } foreach rule $rules { - if {[string index $rule 0] eq "#" || \ + if {[string index $rule 0] eq "#" || [llength $rule] < 2} continue if {[lindex $rule 0] ne "folder"} continue if {[::sweeper::runrule $ts $rule]} break diff --git a/webif/plugin/sweeper/edit.jim b/webif/plugin/sweeper/edit.jim index 883babb..45887a2 100755 --- a/webif/plugin/sweeper/edit.jim +++ b/webif/plugin/sweeper/edit.jim @@ -74,30 +74,29 @@ Add pre-defined ruleset: Enable/Disable Rule Delete Rule + title="Delete Rule"> Duplicate Rule + title="Duplicate Rule"> Move Rule Up + title="Move Rule Up"> Move Rule Down + title="Move Rule Down"> - Add condition +
@@ -153,7 +152,7 @@ Add pre-defined ruleset: Negate? + value='1' title="Invert the sense of the condition."/>
diff --git a/webif/plugin/sweeper/harness.jim b/webif/plugin/sweeper/harness.jim new file mode 100755 index 0000000..5d86ab7 --- /dev/null +++ b/webif/plugin/sweeper/harness.jim @@ -0,0 +1,45 @@ + +source /mod/webif/lib/setup +require lock system.class ts.class pretty_size browse.class \ + safe_delete settings.class plugin + +proc log {msg {level 1}} { + puts "[\ + clock format [clock seconds] -format "%d/%m/%Y %H:%M"\ + ] - $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} {} + +proc scan_run {dir flag callback} { + global dustbin + + if {[string match {\[*} [file tail $dir]]} return + + if {[file exists "$dir/.$flag"]} { $callback $dir } + + foreach entry [readdir -nocomplain $dir] { + if {[file isdirectory "$dir/$entry"]} { + scan_run "$dir/$entry" $flag $callback + } + } +} + +set root [system mediaroot] + +source auto.hook + diff --git a/webif/plugin/sweeper/img/plus.png b/webif/plugin/sweeper/img/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..57a31a31f8bd53da69aecb467ff59c0d404c7f82 GIT binary patch literal 1188 zcmeAS@N?(olHy`uVBq!ia0vp^{2c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDi`Zf4->=xSnNVPI%!Xy|I>Xl&x*X6fo?>Iw{f zOIKr$o& z6x?nx$EjBz=oo!a#3DsBObD2IKumbD1#;jCKQ#}S+KYh6`f%cYZ3YHL2~QWtkcwML zfByfsXXY?4GBSGdwpfh8h`ZqiV+U`;4EYXzrjN`D^9)XAzGr1qIlysw&i?zV3?8}- zSqE698re)1WVB_)oOh6!!C}Ji#EMOAfg9tBUJZro8>S~NdMuYS>vQ8hZiZ~OM{G=f zN7VT1XP)~(UQKAlq>~DVS2Lw~f7UQA*!02R&d&nDb;+N8CZ#aTL{6ww z=+$ESr4hE^^F&6E&#p6!C-}0lN_Z$6I269z%kb$ltI?l70ur(E4ztuJEUaWIQetGS zwcWHvpxr{^{?7u=*vF0w*79vw!KBJI;h9^C==-`oM^*`TFdJMlGf9?k5N=?OI>gBO vifKXbia3_cHpvE_1~yHGjm&oa%q$WN_qpRHn8i*i1eF_}u6{1-oD!M 1} { + set arg [lassign $arg subcmd subarg] + if {$c} { add_json ",\n" } + incr c + clause $subcmd $subarg $($indent + 6) + } + add_json "\n$istr ]" + } + add_json "\n" + add_json "$istr}" +} + proc rule {id rule} { - global clausedescr lcomment + global lcomment lockfound set enabled 1 if {[lindex $rule 0] eq "##"} { @@ -76,22 +115,11 @@ 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 " }" + clause $cmd $arg } add_json "\n" if {$cmd ne "action"} { diff --git a/webif/plugin/sweeper/schema.js b/webif/plugin/sweeper/schema.js index 024d023..f4411d0 100644 --- a/webif/plugin/sweeper/schema.js +++ b/webif/plugin/sweeper/schema.js @@ -4,42 +4,49 @@ var schema = { 'class': 'all', type: 'int', desc: 'Logical Channel Number', + negate: true, def: "0" }, duration: { 'class': 'all', type: 'int', desc: 'Recording duration (in minutes)', + negate: true, def: "0" }, schedduration: { 'class': 'all', type: 'int', desc: 'Scheduled duration (in minutes)', + negate: true, def: "0" }, size: { 'class': 'all', type: 'int', desc: 'Recording size (in bytes)', + negate: true, def: "0" }, hour: { 'class': 'all', type: 'int', desc: 'Hour in which the recording started', + negate: true, def: "0" }, age: { 'class': 'all', type: 'int', desc: 'Age (in hours)', + negate: true, def: "0" }, wage: { 'class': 'all', type: 'int', desc: 'Time since last watched, or recorded (in hours)', + negate: true, def: "0" }, title: { @@ -47,6 +54,7 @@ var schema = { type: 'substr', desc: 'Recording Title contains', idesc: 'Recording Title does not contain', + negate: true, def: 'Enter text here...' }, synopsis: { @@ -54,6 +62,7 @@ var schema = { type: 'substr', desc: 'Synopsis contains', idesc: 'Synopsis does not contain', + negate: true, def: 'Enter text here...' }, guidance: { @@ -61,12 +70,14 @@ var schema = { type: 'substr', desc: 'Guidance Text contains', idesc: 'Guidance Text does not contain', + negate: true, def: 'Enter text here...' }, genre: { 'class': 'all', type: 'select', desc: 'Recording Genre', + negate: true, select: { Unclassified: 'Unclassified', Film: 'Film', @@ -84,6 +95,7 @@ var schema = { 'class': 'all', type: 'select', desc: 'Recording Definition', + negate: true, select: { SD: 'Standard Definition', HD: 'High Definition' @@ -95,6 +107,7 @@ var schema = { type: 'select', desc: 'Recording Flagged as', idesc: 'Recording not Flagged as', + negate: true, select: { Locked: 'Locked', New: 'New', @@ -110,6 +123,7 @@ var schema = { 'class': 'all', type: 'select', desc: 'Change Recording Lock', + negate: true, select: { 1: 'Lock Recording', 0: 'Unlock Recording' @@ -121,6 +135,8 @@ var schema = { 'class': 'all', type: 'substr', desc: 'Filename contains', + idesc: 'Filename does not contain', + negate: true, def: 'Enter text here...' }, fflag: { @@ -128,7 +144,24 @@ var schema = { type: 'string', desc: 'Folder flagged as', idesc: 'Folder not flagged as', + negate: true, def: 'nosweep' + }, + 'or': { + 'class': 'all', + type: 'composite', + label: 'Or', + desc: 'If any of...', + negate: false, + def: '' + }, + 'and': { + 'class': 'all', + type: 'composite', + label: 'And', + desc: 'If all of...', + negate: false, + def: '' } }, action: { @@ -150,24 +183,24 @@ var schema = { desc: 'Move recording to folder...', continues: false }, - lock: { - 'class': 'all', - argtype: 'none', - desc: 'Lock recordings', - continues: true - }, - unlock: { - 'class': 'all', - argtype: 'none', - desc: 'Unlock recordings', - continues: true - }, movecreate: { 'class': 'all', argtype: 'folder', desc: 'Move recording to folder (creating if necessary)', continues: false }, + copy: { + 'class': 'all', + argtype: 'folder', + desc: 'Copy recording to folder...', + continues: false + }, + copycreate: { + 'class': 'all', + argtype: 'folder', + desc: 'Copy recording to folder (creating if necessary)', + continues: false + }, fileunder: { 'class': 'folder', argtype: 'folder', @@ -187,6 +220,18 @@ var schema = { desc: 'Rename recording files to...', continues: true }, + lock: { + 'class': 'all', + argtype: 'none', + desc: 'Lock recordings', + continues: true + }, + unlock: { + 'class': 'all', + argtype: 'none', + desc: 'Unlock recordings', + continues: true + }, delete: { 'class': 'all', argtype: 'none', @@ -216,47 +261,38 @@ var macros = { example: { desc: 'Example rules', rules: [ - { - "raw": "lcn {>= 70} lcn {<= 79} duration {>= 90} action {move Children/Films}", - "name": "Move any Children's films (by length)", + { + "raw": "lcn {>= 70} lcn {<= 79} or {duration {>= 90} genre Film } action {move Children/Films}", + "name": "Move any Children's films", "type": "file", "enabled": "0", "criteria": [ { + "negate": 0, "cmd": "lcn", "arg": ">= 70" }, { + "negate": 0, "cmd": "lcn", "arg": "<= 79" }, { - "cmd": "duration", - "arg": ">= 90" - } - ], - "action": { - "cmd": "move", - "arg": "Children/Films" - } - }, - { - "raw": "lcn {>= 70} lcn {<= 79} genre Film action {move Children/Films}", - "name": "Move any Children's films (by genre)", - "type": "file", - "enabled": "0", - "criteria": [ - { - "cmd": "lcn", - "arg": ">= 70" - }, - { - "cmd": "lcn", - "arg": "<= 79" - }, - { - "cmd": "genre", - "arg": "Film" + "negate": 0, + "cmd": "or", + "arg": "duration {>= 90} genre Film ", + "criteria": [ + { + "negate": 0, + "cmd": "duration", + "arg": ">= 90" + }, + { + "negate": 0, + "cmd": "genre", + "arg": "Film" + } + ] } ], "action": { diff --git a/webif/plugin/sweeper/script.js b/webif/plugin/sweeper/script.js index d8b859b..d33397a 100644 --- a/webif/plugin/sweeper/script.js +++ b/webif/plugin/sweeper/script.js @@ -90,16 +90,20 @@ var getters = { } }; -function ruleconf(rule) +function clauseconf(clause) { var s = ''; - if (rule.hasClass('ruledisabled')) - s += '## '; - if (rule.attr('type') == 'folder') - s += 'folder '; - - rule.find('tr.clause').each(function(i) { + clause.find('tbody:first > tr.clause,tbody:first > tr.compositeclause') + .each(function() { + if ($(this).hasClass('compositeclause')) + { + cc = clauseconf($(this).find('table:first')); + cmd = $(this).attr('cmd'); + if (cc && cc.length) + s += cmd + ' {' + cc + '} '; + return; + } cmd = $(this).find('th.cmd').attr('cmd'); negate = $(this).find('th.cmd').attr('negate'); c = schema.criterion[cmd]; @@ -111,6 +115,19 @@ function ruleconf(rule) s += '!'; s += cmd + ' ' + quot(val) + ' '; }); + return s; +} + +function ruleconf(rule) +{ + var s = ''; + + if (rule.hasClass('ruledisabled')) + s += '## '; + if (rule.attr('type') == 'folder') + s += 'folder '; + + s += clauseconf(rule); act = rule.find('tr.action th.cmd').attr('cmd'); arg = $.trim(rule.find('tr.action td.val').text()); @@ -144,18 +161,23 @@ function rulerefresh(rule) if (!showraw) rule.find('.raw').hide(); rule.find('.raw').html(ruleconf(rule)); - rule.find('tr.clause th.title').text('And:').first().text('If:'); - if (rule.find('tr.clause').length < 1) + rule.find('tr.clause th.title,tr.compositeclause th.title') + .text('And:').first().text('If:'); + rule.find('tr.compositeclause').each(function(e) { + cmd = $(this).attr('cmd'); + label = schema.criterion[cmd].label; + $(this).find('table th.title').text(label + ':') + .first().text('If:'); + }); + if (rule.find('tr.clause,tr.compositeclause').length < 1) { rule.find('tr.action th.title').text('Always:'); - rule.find('tr.otherwise').hide(); - rule.find('div.criteria,div.arrow').hide(); + rule.find('tr.otherwise,div.criteria,div.arrow').hide(); } else { rule.find('tr.action th.title').text('Then:'); - rule.find('tr.otherwise').show(); - rule.find('div.criteria,div.arrow').show(); + rule.find('tr.otherwise,div.criteria,div.arrow').show(); } } @@ -172,7 +194,34 @@ function critdesc(cmd, negate) return c.desc; } -function criterion(data) +function composite_criterion(c, data) +{ + s = '' + + 'And:' + + '' + + '' + + ''; + + $.each(data.criteria, function(key, val) { + s += criterion(val, 'comp'); + }); + + s += '
' + + '' + + '' + + ' ' + + '' + + '' + + ' ' + + c.desc + + '
'; + + return s; +} + +function criterion(data, classes) { var c = schema.criterion[data.cmd]; var s; @@ -182,7 +231,12 @@ function criterion(data) alert('Unknown Criterion (' + data.cmd + ')'); return; } - s = 'And:' + + if (c.type == 'composite') + return composite_criterion(c, data); + + s = 'And:' + '' + critdesc(data.cmd, data.negate) + ''; @@ -192,10 +246,10 @@ function criterion(data) s += 'UNKNOWN (' + data.arg + ')'; s += '' + - '' + + '' + '' + ' ' + - '' + + '' + '' + ''; return s; @@ -264,8 +318,12 @@ function addrule(id, data) $.each(data.criteria, function(key, val) { if (val.cmd == 'lock' && ( data.action.cmd == 'lock' || data.action.cmd == 'unlock')) + { + alert('Removed legacy lock/unlock - ' + + 'check rules before saving.'); return; - $c.find('tbody').append(criterion(val)); + } + $c.find('tbody:first').append(criterion(val)); }); $c.find('th.title:first').text('If:'); @@ -599,7 +657,7 @@ $('#macros').on('click', '#b_macro', function(e) { changed(0); -function addcriterion(rule) +function addcriterion(rule, target) { var type = rule.attr('type'); @@ -616,6 +674,7 @@ function addcriterion(rule) ); }); $('#newcondition_negate').prop('checked', false); + $('#newcondition_cmd').trigger('change'); $('#newcondition').dialog({ height: 'auto', width: 'auto', @@ -624,21 +683,22 @@ function addcriterion(rule) position: { my: 'bottom left', at: 'top right', - of: rule + of: target }, 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({ + var obj = { cmd: val, negate: negate, - arg: schema.criterion[val].def - })); + arg: schema.criterion[val].def, + criteria: [] + }; + target.find('tbody:first') + .append(criterion(obj)); changed(1); rulerefresh(rule); }, @@ -656,7 +716,8 @@ $('#ruleset') }) .on('click', 'a.addcriterion', function(e) { e.preventDefault(); - addcriterion($(this).closest('div.rule')); + var rule = $(this).closest('div.rule'); + addcriterion(rule, rule.find('table.criteria')); }) .on('click', 'a.editcomment', function(e) { e.preventDefault(); @@ -827,6 +888,32 @@ $('#ruleset') .html($('#empty_rulebase').html()); }); }); + }) + .on('click', 'a.delsubclause', function(e) { + e.preventDefault(); + $(this).dojConfirmAction({ + question: 'Delete sub-condition?', + yesAnswer: 'Yes', + cancelAnswer: 'No' + }, function(el) { + var rule = $(el).closest('div.rule'); + $(el).closest('tr.compositeclause') + .fadeOut('slow', function() { + $(this).remove(); + rulerefresh(rule); + changed(1); + }); + }); + }) + .on('click', 'a.addsubcriterion', function(e) { + e.preventDefault(); + var rule = $(this).closest('div.rule'); + var clause = $(this).closest('table.compositeclause'); + addcriterion(rule, clause); + }) + .on('dblclick', 'div.raw', function(e) { + e.preventDefault(); + $(this).toggleClass("rawvis"); }); $('#edit_action_act').on('change', function(e) { @@ -837,7 +924,17 @@ $('#ruleset') $('#edit_action_arg').enable(); $('.edit_action_help').hide(); $('#edit_action_help_' + schema.action[cmd].argtype).show(); - }) + }); + + $('#newcondition_cmd').on('change', function(e) { + var cmd = $(this).val(); + if (schema.criterion[cmd].negate) + $('#newcondition_negate').enable(); + else + $('#newcondition_negate') + .prop('checked', false) + .disable(); + }); // Set up macros $.each(macros, function(key, val) { @@ -854,5 +951,7 @@ $('#ruleset') }); loadrules($('span.dir').text()); + + $(document).tooltip(); }); diff --git a/webif/plugin/sweeper/style.css b/webif/plugin/sweeper/style.css index d4aa70e..445992c 100644 --- a/webif/plugin/sweeper/style.css +++ b/webif/plugin/sweeper/style.css @@ -62,6 +62,11 @@ div.raw right: 12px; } +div.rawvis +{ + background: #ccc; +} + div#buttons { padding-top: 1em; @@ -107,6 +112,7 @@ span.legendright table { border: 0; + line-height: 1em; } table th diff --git a/webif/plugin/sweeper/test b/webif/plugin/sweeper/test index 0372fc2..8c4a863 100755 --- a/webif/plugin/sweeper/test +++ b/webif/plugin/sweeper/test @@ -1,50 +1,12 @@ #!/mod/bin/jimsh -source /mod/webif/lib/setup -require lock system.class ts.class tdelete pretty_size browse.class \ - safe_delete settings.class plugin +source /mod/webif/plugin/sweeper/harness.jim -proc log {msg {level 1}} { - puts "[\ - clock format [clock seconds] -format "%d/%m/%Y %H:%M"\ - ] - $msg" +if {![llength $argv]} { + ::sweeper::scan 0 +} else { + scan_run [lindex $argv 0] sweeper ::sweeper::sweep } - -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} {} - -proc scan_run {dir flag callback} { - global dustbin - - if {[string match {\[*} [file tail $dir]]} return - - if {[file exists "$dir/.$flag"]} { $callback $dir } - - foreach entry [readdir -nocomplain $dir] { - if {[file isdirectory "$dir/$entry"]} { - scan_run "$dir/$entry" $flag $callback - } - } -} - -set root [system mediaroot] - -source auto.hook - -::sweeper::scan 0 + #::sweeper::apply $::root $::sweeper::cf - diff --git a/webif/plugin/sweeper/test.jim b/webif/plugin/sweeper/test.jim index 9e26b21..f4dafdc 100755 --- a/webif/plugin/sweeper/test.jim +++ b/webif/plugin/sweeper/test.jim @@ -1,14 +1,11 @@ #!/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 +source /mod/webif/plugin/sweeper/harness.jim httpheader set dir [cgi_get dir ""] -set root [system mediaroot] set cf "/tmp/sweepertest.cf" @@ -31,30 +28,6 @@ if {[catch {set fp [open $cf w]} msg]} { $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