--- /dev/null
+
+class Author
+ def initialize(surname, givenNames)
+ @surname = surname
+ @givenNames = givenNames
+ end
+end
--- /dev/null
+
+require './author.rb'
+
+class Book
+ def initialize(fileName)
+ @author = nil
+ @cover = nil
+ @series = nil
+ @title = nil
+ @volume = nil
+
+ parseFileName!(fileName)
+ end
+
+ def self.canHandle?(fileName)
+ if nil == fileName
+ return false
+ end
+
+ lowerName = fileName.downcase()
+
+ if lowerName.end_with?(".epub")
+ return true
+ end
+
+ return false
+ end
+
+ def inspect
+ data = []
+ if nil != @series
+ data.push('series="' + @series + '"')
+ end
+ if nil != @volume
+ data.push('volume="' + @volume + '"')
+ end
+ if nil != @title
+ data.push('title="' + @title + '"')
+ end
+ if nil != @author
+ data.push('author="' + @author + '"')
+ end
+ return '(Book:' + data.join(',') + ')'
+ end
+
+ def to_s
+ return inspect()
+ end
+
+ protected
+ def isUpper?(c)
+ return /[[:upper:]]/.match(c)
+ end
+
+ protected
+ def massageAuthor(input)
+ if nil == input
+ return nil
+ end
+
+ result = ""
+ input.each_char do |c|
+ if isUpper?(c) and (result.length > 0)
+ result += " "
+ end
+ result += c
+ end
+
+ return result
+ end
+
+ # Returns (series, volumeNo, titleText)
+ protected
+ def processTitle(input)
+ if nil == input
+ return nil
+ end
+
+ arr = input.split('_')
+
+ series = nil
+ vol = nil
+
+ first = arr[0]
+ matchData = (arr[0]).match(/^([A-Z]+)([0-9]+)$/)
+ if nil != matchData
+ capt = matchData.captures
+ series = capt[0]
+ vol = capt[1]
+ arr.shift
+ end
+
+ pos = arr[-1].rindex('.')
+ if nil != pos
+ arr[-1] = arr[-1].slice(0, pos)
+ end
+
+ title = arr.join(' ')
+
+ return series, vol, title
+ end
+
+ protected
+ def parseFileName!(fileName)
+ parts = fileName.split('/')
+ (@series, @volume, @title) = processTitle(parts[-1])
+ if parts.length > 1
+ @author = massageAuthor(parts[-2])
+ end
+ end
+end
--- /dev/null
+require './walkdir.rb'
+
+books = []
+
+for arg in ARGV
+ w = WalkDir.new(arg)
+ books.push(w.books)
+end
+
+puts books
--- /dev/null
+# Walk the directory (and subdirectories), identifying books.
+#
+# Expected format:
+# .../AuthorName/Title_of_the_Awesome_Book.ext
+#
+# Author is given as FirstLast. For example,
+# Robert Anson Heinlein is RoberHeinlein, and
+# JKRowling is JoanneRowling.
+#
+# Book titles have spaces replaced with underscores,
+# and punctuation [,!?'] replaced with hyphens.
+#
+# If the book forms part of a series, then an all-capitals
+# series designator, followed by a numeric volume number,
+# followed by an underscore, is prefixed to the name.
+# For example, Hardy Boys' volume 1, The Tower Treasure,
+# is rendered as .../FranklinDixon/HB001_The_Tower_Treasure.epub
+# and Mrs. Pollifax volume 6, On the China Station, is
+# .../DorothyGilman/P06_On_the_China_Station.epub.
+
+require './book.rb'
+
+class WalkDir
+ def initialize(root)
+ @root = root
+ @files = walk(@root)
+ end
+
+ def books
+ result = []
+ for file in @files
+ if Book.canHandle?(file)
+ book = Book.new(file)
+ result.push(book)
+ end
+ end
+ return result
+ end
+
+ def walk(path)
+ result = []
+ children = Dir.entries(path)
+ for child in children
+ fullName = (path.chomp("/")) + "/" + child
+ if (File.directory?(fullName)) and (child != ".") and (child != "..")
+ sub = walk(fullName)
+ if (sub != nil) and (sub.length > 0)
+ result.concat(sub)
+ end
+ elsif (! File.directory?(fullName))
+ result.push(fullName)
+ end
+ end
+ return result
+ end
+end