I use TVShows to subscribe to numerous tv torrents and this script, saved as TVtasks.app, watches over them. I used Lingon to set this to run once an hour. (I haven’t tried in six months or so, but Folder Actions in Leopard were behaving poorly, so I had to find some other way of running this script.)
It scans TVShows’ download folder, tries to figure out which show each torrent is for, move it, and open it. Transmission will download it to a temp folder and move it back to its final destination upon completion. TVtasks will then scan my Television folder for fresh videos, using file labels to track what has or hasn’t been processed. adds them to iTunes, and tries to figure out the show, season, and episode number of videos, then grab the episode title from TVrage.
The end result is a largely hassle-free, invisible flow; once I manually subscribe to a show’s torrent and add its search pattern and name to showTokens, they magically appear named and ready to go in iTunes/FrontRow. The regular expression matching uses SatImage.osax, which I’ve only tested through 3.2.0.
I am an AppleScript novice, so plenty of suck in here. But it works. Mostly. I’ve had one big problem in all versions of Leopard up to 10.5.4 (currently installed).
Intermittently, something snaps and “(every file in entire contents of folder tvFolderPath whose label index is not 6)” stops returning files even though I know that there are fresh, unlabeled videos. The FInder knows it, too, as I have a savedSearch which shows them. This is a long-standing issue that I have never been able to figure out, despite rewriting the torrent and video handlers numerous times. “Entire contents … as alias list” does not throw an error when this happens, so the script remains ignorant of any problems.
Relaunching the finder always fixes the problem. For some voodoo reason, opening and saving the script seems to fix it sometimes.
The only evidence I’ve found of a problem in the console is that I will start getting reports that locum terminated once an hour, but it’s not always concurrent with the problem.
So, what’s the deal?
FYI: this script is set up with the assumption that you have checked “copy music to iTunes folder” in your iTunes prefs. If not, then this script will probably mess stuff up.
-- path of your torrent folder
property torrentFolderPath : "Video:Maintenance:Torrents:Incoming"
-- path to the root of your tv folders
property tvFolderPath : "Video:Television"
-- path to folder where temporary folder will go
property chaffParentPath : "Video:Maintenance:Torrents"
-- list of of grep patterns and preferred human-readable names.
-- DON'T use parentheses in your matching patterns; it will throw off the season/episode number extraction
-- (truncated for clarity's sake)
property showTokens : {¬
{"american.?idol", "American Idol"}, ¬
{"bionic.?woman", "Bionic Woman"}, ¬
{"Bones", "Bones"}, ¬
{"dancing.?w.{0,4}the.?stars.{0,5}", "Dancing with the Stars"}, ¬
{"my.?name.?is.?Earl", "My Name is Earl"}, ¬
{"30.?Rock", "30 Rock"} ¬
}
checkForTorrents()
on checkForTorrents()
tell application "Finder"
set torrentFolder to folder torrentFolderPath
try
set freshTorrents to (files of torrentFolder whose name extension is "torrent") as alias list
on error
try
set freshTorrents to (files of torrentFolder whose name extension is "torrent") as alias as list
on error
try
set freshTorrents to {}
end try
end try
end try
do shell script "logger TVtasks found " & (length of freshTorrents) & " TORRENTS"
repeat with t in freshTorrents
repeat 1 times
set tInfo to my parseShowStuff(name of t as text)
if tInfo's pShow is not "Unknown" then
set tShow to tInfo's pShow as text
set tSeason to tInfo's pSeason as text
set showFolder to tvFolderPath & ":" & tShow
set seasonFolder to showFolder & ":Season " & tSeason
if not (exists folder showFolder) then
make new folder at folder tvFolderPath with properties {name:tShow}
end if
if not (exists folder seasonFolder) then
make new folder at folder showFolder with properties {name:"Season " & (tSeason as integer)}
end if
try
set finalTorrent to (duplicate t to folder seasonFolder with replacing) as alias
if exists file finalTorrent then
delete t
-- open the torrent in the background
do shell script "open -g " & (quoted form of POSIX path of finalTorrent)
end if
end try
else
(*
Well, we downloaded the torrent for a reason. go get it anyway.
The end result will be that it will get added and sit in the movie folder.
NB: the quicktime reference file will always expect to find the actual
movie file in the Unknown folder.
*)
try
set finalTorrent to (duplicate t to folder (tvFolderPath & ":Unknown") with replacing) as alias
if exists file finalTorrent then
delete t
-- open the torrent in the background
try
do shell script "open -g " & (quoted form of POSIX path of finalTorrent)
on error
try
open finalTorrent
end try
end try
end if
end try
end if
end repeat
end repeat
end tell
checkForVideos()
end checkForTorrents
on checkForVideos()
tell application "Finder"
try
set freshVideos to a reference to (every file in entire contents of folder tvFolderPath whose label index is not 6) as alias list
on error
try
set freshVideos to a reference to (every file in entire contents of folder tvFolderPath whose label index is not 6) as alias as list
on error
set freshVideos to {}
end try
end try
do shell script "logger TVtasks found " & (length of freshVideos) & " VIDEOS"
--(assuming you "checked copy to iTunes folder" pref in iTunes)
if not (exists folder (chaffParentPath & ":Chaff")) then make new folder at folder chaffParentPath with properties {name:"Chaff"}
repeat with v in freshVideos
repeat 1 times
-- Cannibalized from Andree Dettmer's Movie2iTunes
if v's name extension is in {"mov", "mpg", "mpeg", "mp4", "avi", "wmv", "swf", "m2v", "flv", "wma", "divx", "mkv"} and v as text does not contain "sample" then
set tempMovie to chaffParentPath & ":Chaff:" & (name of v as text) & ".mov"
do shell script "php -r 'echo $argv[1].rawurlencode($argv[3]).$argv[2];' " & quoted form of "<?xml version=\"1.0\"?><?quicktime type=\"application/x-quicktime-media-link\"?><embed src=\"file:/" & " " & quoted form of "\"/>" & " " & quoted form of POSIX path of v & " >> " & quoted form of POSIX path of tempMovie
set vInfo to my parseShowStuff(name of v as text)
set theTitle to false -- remember to check if this is still necessary
if vInfo's pShow is not "Unknown" then set theTitle to my getEpisodeName(vInfo)
tell application "iTunes"
(*
Telling the actual track seemed easiest. I recall getting knocked around by
a whole lot of "You can't get there from here" messages. It also seemed to be
the only way to set multiple properties, which feels a tad faster than telling
iTunes to change one property at a time.
*)
tell (add tempMovie as alias)
if vInfo's pShow is not "Unknown" then
-- We can set the show info, sans episode name, if the parser figured things out
try
set {show, season number, episode number, track number} to {vInfo's pShow, vInfo's pSeason, vInfo's pEpisode, vInfo's pEpisode}
end try
-- If we got the episode's title, then set it and make it a tv show.
if theTitle is not false then
try
set {name, unplayed, video kind} to {theTitle, true, TV show}
end try
end if
end if
tell application "Finder" to set v's label index to 6
end tell
end tell
else if v's name extension is "rar" then
--I hate RARs, but you still get 'em sometimes. Open them in the background
--NB: it is expected that you have a program set up to open RAR files, like StuffitExpander.
do shell script "open -g " & (quoted form of POSIX path of (v as text))
set v's label index to 6
end if
end repeat
end repeat
--regardless of success, we're dumping these temp files (assuming you "checked copy to iTunes folder" pref in iTunes)
delete folder (chaffParentPath & ":Chaff")
end tell
end checkForVideos
on getEpisodeName(epInfo)
-- go to tvrage.com and get the episode information. chop out the episode title.
set getEp to "'if($fp=fopen(\"http://www.tvrage.com/quickinfo.php?show=\".urlencode($argv[1]).\"&ep=\".urlencode($argv[2]),\"r\") ) {while(!feof($fp)){$line=fgets($fp,1024);list ($sec,$val)=explode(\"@\",$line,2);if($sec==\"Episode Info\"){list($ep,$title,$airdate)=explode(\"^\",$val);echo \"^\".$title;}}fclose($fp);}'"
set theTitle to (do shell script "php -r " & getEp & " \"" & epInfo's pShow & "\" \"" & epInfo's pSeason & "x" & epInfo's pEpisode & "\"")
if theTitle starts with "^" then
-- sloppy way to tell that it worked
return (text 2 thru -1 of theTitle)
else
--bad form to have the variable alternate between a string and a boolean. oh well.
return false
end if
end getEpisodeName
on parseShowStuff(theString)
set parsedInfo to {pShow:"Unknown"}
repeat with parseItem in showTokens
try
(*
Look for the grep pattern from the property showTokens, followed by a season/episode pattern. This does
not handle date-based file names. Satimage returns an error when the grep isn't found, thus
you must wrap it in a try block. If it works, theInfo is a string "S# E#", e.g. "1 13".
*)
set theInfo to find text ((item 1 of parseItem as string) & "[^0-9]{0,4}([0-9]{1,2})[^0-9]{0,3}([0-9]{2})") in theString using "\\1 \\2" with regexp and string result without case sensitive
set parsedInfo to {pShow:item 2 of parseItem, pSeason:word 1 of theInfo, pEpisode:word 2 of theInfo}
--if we got this far, then it worked. no need to keep repeating
exit repeat
end try
end repeat
return parsedInfo
end parseShowStuff