If you’re like me, you may download a lot of videos at once and then be forced to ask the question, “Wait, what haven’t I watched yet?” I created lnSponge, a simple TCL script, to keep track of new files for me. It scans a given directory tree and creates symbolic links to all new files in a designated folder. It retains a copy of all the filenames so you can delete the symbolic links after you’re done watching the media and it won’t reappear on the next run.
The script takes in four arguments indicating the path of files to scan, the path to the symbolic link folder, the file to use to hold the database of filenames and the number of trackback days (e.g. ignore all new files older than n days). It produces no output if there are no new files to add, so it can easily be coupled with the Ruby Email Script
Example usage:
./lnsponge.tcl /mnt/orion/tv_shows /mnt/orion/new ~/.tvshows.list 10
Script:
#!/usr/bin/tclsh
#
# lnSponge - v1.0
#
# A script designed to create a set of symbolic links
# in one directory for all new files found in a given
# directory tree. Created links are kept track of in
# an index file to prevent creating duplicate links.
#
# Useful to keep track of new files for users who
# download a lot of media.
#
# Sumit Khanna <sumdog@gmail.com>
# http://penguindreams.org
#
# Free for noncommercial use
#
namespace eval lnSpongeVars {
#only consider files that were created in the past n days
variable mdays
#path to meida tree
variable path
#path to directory with symbolic links
variable newlinks
#path to processed file list
variable plist
#file descriptor for update process
variable plistchannel
#tmp arry for processed files
variable pfilearray
}
proc printUsage {} {
puts "lnSponge Usage:"
puts ""
puts "lnsponge.tcl <media path> <new links path> <list file> <trackback days>"
puts ""
puts "\tmedia path - path to directories to recursivly search for new media"
puts "\tnew links path - path to directory that contains links to new media"
puts "\tlist file - file which contains list of processed files"
puts "\ttrackback days - ignore new files that were modified more than n days ago"
puts ""
}
#reads in command line args into lnSpongeVars namespace
# make sure $argc ==4 first
proc readCmdArgs {} {
variable ::lnSpongeVars::mdays
variable ::lnSpongeVars::path
variable ::lnSpongeVars::newlinks
variable ::lnSpongeVars::plist
global argv
set path [lindex $argv 0]
set newlinks [lindex $argv 1]
set plist [lindex $argv 2]
set mdays [lindex $argv 3]
}
#ftime = UNIX time/seconds from epoch
#days = number of days
#return true if ftime is < days; else false
proc checkDays { ftime } {
variable ::lnSpongeVars::mdays
#seconds per day
set secdays [ expr $mdays * 24 * 60 * 60 ]
if { [ expr [ clock seconds ] - $ftime ] < $secdays } {
return 1
}
return 0
}
#initial reading of list
# is read in entirety
proc readPList {} {
variable ::lnSpongeVars::plist
variable ::lnSpongeVars::pfilearray
if { [ file exists $plist ] } {
set fd [open $plist "r"]
while {[gets $fd line] >= 0} {
array set pfilearray [list "$line" "1"]
}
close $fd
}
}
#--functions for appending to processed file list
proc openPFile {} {
variable ::lnSpongeVars::plist
variable ::lnSpongeVars::plistchannel
set plistchannel [open $plist "a"]
}
proc addPFile { name } {
variable ::lnSpongeVars::plistchannel
puts $plistchannel $name
}
proc closePFile {} {
variable ::lnSpongeVars::plistchannel
close $plistchannel
}
#--
#recursive function for scanning
# all directories from a given path
# and creating links for those files
# not on processed list
#$rpath = base of tree to scan
proc probeDirs {rpath} {
variable ::lnSpongeVars::pfilearray
variable ::lnSpongeVars::newlinks
if {[ file isdirectory $rpath ]} {
set i [glob -nocomplain -directory $rpath *]
foreach sub $i {
probeDirs $sub
}
} else {
file stat $rpath finfo
set ftime [ lindex [ array get finfo "ctime" ] 1 ]
set btime [ checkDays $ftime ]
#if it was created in the past n days
if { $btime == 1 } {
#if it's not all ready in processed list
set basename [file tail $rpath]
if {![info exists pfilearray($basename)]} {
if { [ file exists "$newlinks/$basename" ] } {
puts "File Exists (ignoring): $basename"
} else {
puts "New Link: $basename"
file link -symbolic "$newlinks/$basename" $rpath
}
addPFile [file tail $rpath]
}
}
}
}
#main entry point
# functions must be called in this order
if { $argc != 4 } {
printUsage
exit 1
} else {
readCmdArgs
readPList
openPFile
probeDirs $::lnSpongeVars::path
closePFile
}