Nice, I am looking forward to checking out the new changes. I don't how how you plan on speeding the process the up, it will be interesting to see.
I have been plugging away at the Rules idea. I have fleshed it out further:
-GUI for editing rules
-Rule removal
-Added a Button for setting a Default scanning folder.
Wow doesn't really seem a lot when it is put in bullet points
Anyway here is the code:
; SceneRenamer by strictlyfocused02
;
; AutoHotkey Version: 1.0.48+
;
; Script Function:
; Extracts season and episode info from the filename
; and uses that to retreive info from TheTVDB.com to rename
; the file to a user-specified format
; Special thanks to TheTVDB.com
; This project would not have been possible without their top-notch API
#Include xpath.ahk
#NoEnv
SendMode, Input
SetWorkingDir, %A_ScriptDir%
;check for rules.ini and create if it doesnt exist
IfNotExist, rules.ini
Blank =
IniWrite,%Blank%, rules.ini, Rules, AllRuleNames
;Get the default folder
IniRead,DefaultTargetFolder, rules.ini, DefaultFolder, Folder
MainGUI:
Gui, Destroy
Gui, Add, Button, Default gButtonAddFolder, &Add folder
Gui, Add, Button, x+20 gButtonAddFile, Add &file
Gui, Add, Button, x+20 gButtonClear, &Clear list
Gui, Add, Button, x+20 gButtonDefaultRenameFolder, &Default Rename Folder ;Addtional button for scaning the default folder
Gui, Add, Button, x+20 ButtonAddRule, Add &Rule ; Add rule button
Gui, Add, Button, x+20 ButtonEditRules, &Edit Rules ; Edit rule button
Gui, Add, Button, x+60 gButtonSetDefaultFolder, &Set Default Folder ; Edit rule button
Gui, Add, ListView, xm r25 w800 vMyListView, Name|Preview|Directory|Extension|TVDB Series ID No|Show Name|FileName ;Added collum for getting the selected Show Name
Gui, Add, DropDownList, y+7 w250 AltSubmit vNameScheme, (Show Title) - (SS)x(EE) - (Episode Title)||(Show Title) - S(SS)E(EE) - (Episode Title)|(SS)x(EE) - Episode Title|S(SS)E(EE) - (Episode Title)
Gui, Add, Checkbox, x+30 Checked vZero, Pad with zero?
Gui, Add, Button, x+200 gButtonGetTVDBInfo, &Get TVDB Info
Gui, Add, Button, x+60 gButtonRename, &Rename files
Menu, MyContextMenu, Add, Open, ContextOpenFile
Menu, MyContextMenu, Add, Properties, ContextProperties
Menu, MyContextMenu, Add, Remove item, ContextRemoveItem
Gui, Show
Gosub, ButtonDefaultRenameFolder ;This is so it starts scaning once opened
Return
GuiClose:
ExitApp
GuiContextMenu:
If A_GuiControl <> MyListView
Return
Menu, MyContextMenu, Show, %A_GuiX%, %A_GuiY%
Return
ButtonSetDefaultFolder:
FocusedRowNumber := LV_GetNext(0, "F")
LV_GetText(LVFileDir, FocusedRowNumber, 3)
msgbox, 4, Set Default Directory, Set %LVFileDir% as the default directory for scanning? `nThis will make SceneRenamer scan this folder on start.
Ifmsgbox yes
IniWrite,%LVFileDir%, rules.ini, DefaultFolder, Folder
Return
;Add new rule to the rules.ini
ButtonAddRule:
;Get information on the Selected show
FocusedRowNumber := LV_GetNext(0, "F")
LV_GetText(PatternShowName, FocusedRowNumber, 7)
LV_GetText(ShowName, FocusedRowNumber, 6)
LV_GetText(SeriesIDNum, FocusedRowNumber, 5)
IniRead, AllRuleNames, rules.ini, Rules, AllRuleNames
;Check the rule doesnt already exist
Loop, Parse, AllRuleNames, |
{
thisRule = %A_LoopField%
if thisRule = %PatternShowName%
{
Msgbox %PatternShowName% already has a rule
Return
}
}
IniWrite, %AllRuleNames%%PatternShowName%|, rules.ini, Rules, AllRuleNames
IniWrite, %ShowName%, rules.ini, %PatternShowName%, SeriesName
IniWrite, %SeriesIDNum%, rules.ini, %PatternShowName%, SeriesIDNum
Return
ContextOpenFile:
ContextProperties:
FocusedRowNumber := LV_GetNext(0, "F")
If not FocusedRowNumber
return
LV_GetText(FileName, FocusedRowNumber, 1)
LV_GetText(FileDir, FocusedRowNumber, 3)
IfInString A_ThisMenuItem, Open
Run %FileDir%\%FileName%,, UseErrorLevel
Else
Run Properties "%FileDir%\%FileName%",, UseErrorLevel
If ErrorLevel
MsgBox Could not perform requested action on "%FileDir%\%FileName%".
return
ContextRemoveItem:
RowNumber = 0
Loop
{
RowNumber := LV_GetNext(RowNumber - 1)
If not RowNumber
Break
LV_Delete(RowNumber)
}
Return
ButtonAddFolder:
Gui +OwnDialogs
FileSelectFolder, TargetFolder,, 3, Select a folder:
If not TargetFolder ; The user pressed cancel
Return
StringRight, LastChar, TargetFolder, 1
If LastChar = \
StringTrimRight, TargetFolder, TargetFolder, 1
GuiControl, -Redraw, MyListView ; Improves performance by disabling redrawing
Loop %TargetFolder%\*.*
{
FileName := A_LoopFileFullPath
SplitPath, FileName,,, FileExt ; Get the file extension
If FileExt in avi,mkv,mp4
LV_Add(Row, A_LoopFileName, "", A_LoopFileDir, FileExt)
}
GuiControl, +Redraw, MyListView ; Turn redrawing back on
LV_ModifyCol() ; Resize each column to fit
LV_ModifyCol(2, 60) ; Expand "Preview" to keep the header visible
LV_ModifyCol(4, 60) ; Expand "Extension" to keep the header visible
LV_ModifyCol(5, 110) ; Expand "TVDB Series ID No" to keep the header visible
Return
;Auto load a default folder and scan it
ButtonDefaultRenameFolder:
If DefaultTargetFolder = ERROR
Return
Else
;Check a file exists before running Default scan
IfExist, %DefaultTargetFolder%*.avi
avi = 1
IfExist, %DefaultTargetFolder%*.mkv
mkv = 1
IfExist, %DefaultTargetFolder%*.mp4
mp4 = 1
If (mkv = "" AND avi = "" AND mp4 = "")
Return
TargetFolder := DefaultTargetFolder
;TargetFolder = "\\SPOON\Downloads\TV Rename"
Loop %TargetFolder%\*.*
{
FileName := A_LoopFileFullPath
SplitPath, FileName,,, FileExt ; Get the file extension
If FileExt in avi,mkv,mp4
LV_Add(Row, A_LoopFileName, "", A_LoopFileDir, FileExt)
}
GuiControl, +Redraw, MyListView ; Turn redrawing back on
LV_ModifyCol() ; Resize each column to fit
LV_ModifyCol(2, 60) ; Expand "Preview" to keep the header visible
LV_ModifyCol(4, 60) ; Expand "Extension" to keep the header visible
LV_ModifyCol(5, 110) ; Expand "TVDB Series ID No" to keep the header visible
Gosub, ButtonGetTVDBInfo
Return
ButtonAddFile:
Gui +OwnDialogs
FileSelectFile, TargetFile
If not TargetFile
Return
SplitPath, TargetFile,,, FileExt ; Get the file extension
If FileExt in avi,mkv,mp4
{
SplitPath, TargetFile, FileName, TargetFolder
LV_Add(Row, FileName, "", TargetFolder, FileExt)
LV_ModifyCol() ; Resize each column to fit
LV_ModifyCol(2, 60) ; Expand "Preview" to keep the header visible
LV_ModifyCol(4, 60) ; Expand "Extension" to keep the header visible
LV_ModifyCol(5, 110) ; Expand "TVDB Series ID No" to keep the header visible
}
Return
ButtonClear:
TargetFolder =
LV_Delete()
Return
ButtonGetTVDBInfo:
LV_GetText(LVFileName, 1, 1) ; Get the filename
If not LVFileName
{
MsgBox, 0,Fatal Error!, You must select a file or folder before connecting to TheTVDB.com!
Return
}
Loop
{
RowNumber = %A_Index%
LV_GetText(LVFileName, RowNumber, 1) ; Get the filename
If not LVFileName ; Break the loop at the end of the list
Break
LV_GetText(LVFileDir, RowNumber, 3) ; Get the file directory
LV_GetText(LVFileExt, RowNumber, 4) ; Get the file extension
ShowInfo := GetTVDBShows(GetShowName(LVFileName))
StringSplit, OutputArray, ShowInfo, `,
ShowName = %OutputArray1%
SeriesIDNo = %OutputArray2%
SeasonNumber := GetSeasonNumber(LVFileName)
EpisodeNumber := GetEpisodeNumber(LVFileName)
If SeriesIDNo
{
EpisodeInfo := GetTVDBEpisodes(SeriesIDNo, SeasonNumber, EpisodeNumber)
StringSplit, OutputArray, EpisodeInfo, `,
SeasonNumber = %OutputArray1%
EpisodeNumber = %OutputArray2%
EpisodeTitle = %OutputArray3%
}
GuiControlGet, NamingScheme,, NameScheme
If NamingScheme = 1
{
If EpisodeTitle
NewFileName = %ShowName% - %SeasonNumber%x%EpisodeNumber% - %EpisodeTitle%.%LVFileExt%
Else
NewFileName = %ShowName% - %SeasonNumber%x%EpisodeNumber%.%LVFileExt%
}
Else if NamingScheme = 2
{
If EpisodeTitle
NewFileName = %ShowName% - S%SeasonNumber%E%EpisodeNumber% - %EpisodeTitle%.%LVFileExt%
Else
NewFileName = %ShowName% - S%SeasonNumber%E%EpisodeNumber%.%LVFileExt%
}
Else If NamingScheme = 3
{
If EpisodeTitle
NewFileName = %SeasonNumber%x%EpisodeNumber% - %EpisodeTitle%.%LVFileExt%
Else
NewFileName = %SeasonNumber%x%EpisodeNumber%.%LVFileExt%
}
Else If NamingScheme = 4
{
If EpisodeTitle
NewFileName = S%SeasonNumber%E%EpisodeNumber% - %EpisodeTitle%.%LVFileExt%
Else
NewFileName = S%SeasonNumber%E%EpisodeNumber%.%LVFileExt%
}
LV_Modify(RowNumber, Col1, LVFileName, NewFileName, LVFileDir, LVFileExt, SeriesIDNo, ShowName, PatternShowName)
LV_ModifyCol()
LV_ModifyCol(4, 60)
LV_ModifyCol(5, 110) ; Expand "TVDB Series ID No" to keep the header visible
}
Return
ButtonRename:
Loop
{
RowNumber = %A_Index%
LV_GetText(LVFileName, RowNumber, 1) ; Get the original filename
If not LVFileName ; Break the loop at the end of the list
Break
LV_GetText(LVNewFileName, RowNumber, 2) ; Get the new filename
FileMove, %TargetFolder%\%LVFileName%, %TargetFolder%\%LVNewFileName%
}
RenameCount := RowNumber - 1
MsgBox, 0, Success!, %RenameCount% files have been renamed!`n`nRefreshing file list...
LV_Delete()
Loop %TargetFolder%\*.*
{
FileName := A_LoopFileFullPath
If not FileName
Break
SplitPath, FileName,,, FileExt ; Get the file extension
If FileExt in avi,mkv,mp4
LV_Add(Row, A_LoopFileName, "", A_LoopFileDir, FileExt)
}
GuiControl, +Redraw, MyListView ; Turn redrawing back on
LV_ModifyCol() ; Resize each column to fit
LV_ModifyCol(2, 60) ; Expand "Preview" to keep the header visible
LV_ModifyCol(4, 60) ; Expand "Extension" to keep the header visible
LV_ModifyCol(5, 110) ; Expand "TVDB Series ID No" to keep the header visible
Return
;Provide a filename (greek.s01e01.avi) and this will hopefully detect and return the show name
GetShowName(FileName)
{
IfInString, FileName, .
StringReplace, NormalName, FileName, ., %A_Space%, All
IfInString, NormalName, _
StringReplace, NormalName, NormalName, _, %A_Space%, All
If RegExMatch(NormalName, "i)s[0-9][0-9]? ?e[0-9][0-9]?", SeasonAndEpisode)
{
StringGetPos, SeasonAndEpisodePos, NormalName, %SeasonAndEpisode%
StringLeft, ShowName, NormalName, (SeasonAndEpisodePos - 1)
}
Else If RegExMatch(NormalName, "i)[0-9][0-9]?x[0-9][0-9]?", SeasonAndEpisode)
{
StringGetPos, SeasonAndEpisodePos, NormalName, %SeasonAndEpisode%
StringLeft, ShowName, NormalName, (SeasonAndEpisodePos - 1)
}
Else If RegExMatch(NormalName, "[0-9][0-9][0-9][0-9]?", SeasonAndEpisode)
{
StringGetPos, SeasonAndEpisodePos, NormalName, %SeasonAndEpisode%
StringLeft, ShowName, NormalName, (SeasonAndEpisodePos - 1)
}
Loop
{
StringRight, LastChar, ShowName, 1
If (LastChar = "-" or LastChar = " ")
StringTrimRight, ShowName, ShowName, 1
Else
Break
}
AutoTrim, off
global PatternShowName = ShowName
Return ShowName
}
;Provide a filename (greek.s01e01.avi) and this will hopefully detect and return the season number
GetSeasonNumber(FileName)
{
If RegExMatch(FileName, "i)s\K[0-9][0-9]?.?e[0-9][0-9]?", SeasonAndEpisode)
{
StringGetPos, EPos, SeasonAndEpisode, e
StringLeft, SeasonNumber, SeasonAndEpisode, %EPos%
IfInString, SeasonNumber, .
StringTrimRight, SeasonNumber, SeasonNumber, 1
IfInString, SeasonNumber, 0
{
StringGetPos, ZeroLoc, SeasonNumber, 0
If ZeroLoc = 0
StringTrimLeft, SeasonNumber, SeasonNumber, 1
}
}
Else If RegExMatch(FileName, "i)[0-9][0-9]?x[0-9][0-9]?", SeasonAndEpisode)
{
StringGetPos, XPos, SeasonAndEpisode, x
StringLeft, SeasonNumber, SeasonAndEpisode, %XPos%
IfInString, SeasonNumber, 0
{
StringGetPos, ZeroLoc, SeasonNumber, 0
If ZeroLoc = 0
StringTrimLeft, SeasonNumber, SeasonNumber, 1
}
}
Else If RegExMatch(FileName, "[0-9][0-9][0-9][0-9]?", SeasonAndEpisode)
{
StringTrimRight, SeasonNumber, SeasonAndEpisode, 2
IfInString, SeasonNumber, 0
{
StringGetPos, ZeroLoc, SeasonNumber, 0
If ZeroLoc = 0
StringTrimLeft, SeasonNumber, SeasonNumber, 1
}
}
Return SeasonNumber
}
;Provide a filename (greek.s01e01.avi) and this will hopefully detect and return the episode number
GetEpisodeNumber(FileName)
{
If RegExMatch(FileName, "i)s\K[0-9][0-9]?.?e[0-9][0-9]?", SeasonAndEpisode)
{
StringGetPos, EPos, SeasonAndEpisode, e
StringRight, EpisodeNumber, SeasonAndEpisode, %EPos%
IfInString, EpisodeNumber, e
StringTrimLeft, EpisodeNumber, EpisodeNumber, 1
IfInString, EpisodeNumber, 0
{
StringGetPos, ZeroLoc, EpisodeNumber, 0
If ZeroLoc = 0
StringTrimLeft, EpisodeNumber, EpisodeNumber, 1
}
}
Else If RegExMatch(FileName, "i)[0-9][0-9]?x[0-9][0-9]?", SeasonAndEpisode)
{
StringGetPos, XPos, SeasonAndEpisode, x
StringRight, EpisodeNumber, SeasonAndEpisode, %XPos%
IfInString, EpisodeNumber, 0
{
StringGetPos, ZeroLoc, EpisodeNumber, 0
If ZeroLoc = 0
StringTrimLeft, EpisodeNumber, EpisodeNumber, 1
}
}
Else If RegExMatch(FileName, "[0-9][0-9][0-9][0-9]?", SeasonAndEpisode)
{
StringRight, EpisodeNumber, SeasonAndEpisode, 2
IfInString, EpisodeNumber, 0
{
StringGetPos, ZeroLoc, EpisodeNumber, 0
If ZeroLoc = 0
StringTrimLeft, EpisodeNumber, EpisodeNumber, 1
}
}
Return EpisodeNumber
}
;Provide a show name (greek) and it will return the TVDB data for the show name (Greek) and series ID (80301) as a CSV (Greek,80301)
GetTVDBShows(ShowName)
{
;Check the rules.ini for a match
IniRead, AllRuleNames, rules.ini, Rules, AllRuleNames
Loop, Parse, AllRuleNames, |
{
thisRule = %A_LoopField%
;Continue on if there is no rule
if (thisRule = "ERROR") or (thisRule = "")
{
Continue
}
if (thisRule = ShowName)
{
IniRead, SeriesName, rules.ini, %thisRule%, SeriesName
IniRead, SeriesIDNum, rules.ini, %thisRule%, SeriesIDNum
;Jump to the end of GetTVDBShows if match is found in rules.
goto finish
}
}
;Check for match online
IfExist, Series_ID.xml
FileDelete, Series_ID.xml
UrlDownloadToFile, http://www.thetvdb.com/api/GetSeries.php?seriesname="%ShowName%", Series_ID.xml
XPath_Load(xml, "Series_ID.xml")
SeriesNameNodeCount := XPath(xml, "/Data/Series/SeriesName/Count()")
If SeriesNameNodeCount = 1
{
SeriesIDNum := XPath(xml, "/Data/Series/seriesid/text()")
SeriesName := XPath(xml, "/Data/Series/SeriesName/text()")
}
Else If SeriesNameNodeCount >= 2
{
SeriesIDNum := XPath(xml, "/Data/Series/seriesid/text()")
SeriesName := XPath(xml, "/Data/Series/SeriesName/text()")
SeriesOverview := XPath(xml, "/Data/Series/Overview/text()")
StringSplit, OutputArraySID, SeriesIDNum, `,
StringSplit, OutputArraySN, SeriesName, `,
StringSplit, OutputArraySO, SeriesOverview, `,
Loop, %SeriesNameNodeCount%
{
SeriesIDNum := OutputArraySID%A_Index%
SeriesName := OutputArraySN%A_Index%
SeriesOverview := OutputArraySO%A_Index%
IfInString, SeriesOverview, ,`;
StringReplace, SeriesOverview, SeriesOverview, ,`;, `,, All
If not SeriesOverview
SeriesOverview = (No show description provided)
MsgBox, 3, Which show do you want?, %SeriesName%`n`n%SeriesOverview%
IfMsgBox, Yes
Break
IfMsgBox, No
{
NoCount = %A_Index%
If NoCount = %SeriesNameNodeCount%
{
SeriesName =
SeriesIDNum =
MsgBox, 0, Fatal Error!, No more show name suggestions provided!`n`nI will use the show name parsed from the filename and move on to the next row...
}
}
IfMsgBox, Cancel
Return
}
}
finish:
If not SeriesName
SeriesName = %ShowName%
FileDelete, Series_ID.xml
Data = %SeriesName%,%SeriesIDNum%
Return Data
}
;Provide the "TVDB series ID", "season number" and "episode number" and this will return the matching TVDB season number,episode number, and episode title as a CSV (3,1,The Day After)
GetTVDBEpisodes(SeriesIDNum, SeasonNum, EpisodeNum)
{
IfExist, Episode_Info.xml
FileDelete, Episode_Info.xml
UrlDownloadToFile, http://www.thetvdb.com/api/563E39CDC7F666DF/series/%SeriesIDNum%/default/%SeasonNum%/%EpisodeNum%/en.xml, Episode_Info.xml
XPath_Load(xml, "Episode_Info.xml")
SeasonNumber := XPath(xml, "/Data/Episode/SeasonNumber/text()")
EpisodeNumber := XPath(xml, "/Data/Episode/EpisodeNumber/text()")
EpisodeTitle := XPath(xml, "/Data/Episode/EpisodeName/text()")
FileDelete, Episode_Info.xml
If not SeasonNumber
SeasonNumber = %SeasonNum%
If not EpisodeNumber
EpisodeNumber = %EpisodeNum%
IfInString, EpisodeTitle, ?
StringReplace, EpisodeTitle, EpisodeTitle, ?,, All
GuiControlGet, PadZero, , Zero
If PadZero = 1
{
SeasonLen := StrLen(SeasonNumber)
EpisodeLen := StrLen(EpisodeNumber)
;If SeasonLen = 1
; SeasonNumber = 0%SeasonNumber%
If EpisodeLen = 1
EpisodeNumber = 0%EpisodeNumber%
}
K = %SeasonNumber%,%EpisodeNumber%,%EpisodeTitle%
Return K
}
ButtonEditRules:
Gui, 1: Destroy
Gui, 1: Add, ListView, x22 y30 w140 h340 AltSubmit vAllRules gSelectRule, Rules
Gui, 1: Add, Edit, x252 y70 w220 h20 vSeriesName,
Gui, 1: Add, Edit, x272 y110 w200 h20 vSeriesIDNum,
Gui, 1: Add, Text, x182 y70 w70 h20 , Series Name
Gui, 1: Add, Text, x182 y110 w90 h20 , TVDB ID Number
Gui, 1: Add, Button, x182 y320 w100 h30 gButtonSave, Save
Gui, 1: Add, Button, x352 y250 w100 h30 gButtonRemoveRule, Remove Rule
Gui, 1: Add, Button, x352 y320 w100 h30 gButtonCancel, Back
Gui, Add, Text, x182 y140 w290 h100 , `nSeries Name: Is the title that will be used in the file name.`n`nTVDB ID Number: Is used to get the episode names.
; Generated using SmartGUI Creator 4.0
Gui, Show, Edit Rules
gosub AllRules
Return
AllRules:
LV_Delete()
GuiControl, -Redraw, AllRules
IniRead, AllRuleNames, rules.ini, Rules, AllRuleNames
Loop, Parse, Allrulenames, |
{
If (A_LoopField = "")
Break
Else
LV_Add(Row,A_LoopField)
}
LV_Modify(1, "Select")
LV_Modify(1, "Focus")
GuiControl, +Redraw, AllRules
gosub SelectRule
Return
SelectRule:
FocusedRowNumber := LV_GetNext(0, "F")
LV_GetText(RuleName, FocusedRowNumber)
IniRead, SeriesName, rules.ini, %RuleName%, SeriesName
IniRead, SeriesIDNum, rules.ini, %RuleName%, SeriesIDNum
GuiControl,, SeriesName, %SeriesName%
GuiControl,, SeriesIDNum, %SeriesIDNum%
Return
ButtonSave:
GuiControlGet, SeriesName
GuiControlGet, SeriesIDNum
IniWrite, %SeriesName%, rules.ini, %RuleName%, SeriesName
IniWrite, %SeriesIDNum%, rules.ini, %RuleName%, SeriesIDNum
Return
ButtonCancel:
Goto MainGUI
Return
ButtonRemoveRule:
FocusedRowNumber := LV_GetNext(0, "F")
LV_GetText(RemoveRule, FocusedRowNumber)
Msgbox, 4, Confirm Rule Removal, Are you sure you want to remove the rule for %RemoveRule%
IfMsgBox No
Return
IniRead, AllRuleNames, rules.ini, Rules, AllRuleNames
Loop, Parse, AllRuleNames,|
{
if (A_LoopField = RemoveRule)
{
StringReplace, AllRuleNames, AllRuleNames, |%A_LoopField%|,|, UseErrorLevel
If ErrorLevel = 0
{
StringReplace, AllRuleNames, AllRuleNames, %A_LoopField%|
}
IniDelete, rules.ini, %A_LoopField%
}
}
IniWrite, %AllRuleNames%, rules.ini, Rules, AllRuleNames
gosub AllRules
Return
Edit - Forgot to add the code that checks if any video files are in the default folder before scanning.
Edit 2 - Fixed a mistake with the variables for adding rules. General clean-up.