quartz_flow_plugin_shows 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/plugins/shows/lib/showname_parse.rb +529 -0
- data/plugins/shows/links.rb +1 -0
- data/plugins/shows/routes.rb +57 -0
- data/plugins/shows/views/show_list_partial.haml +33 -0
- metadata +65 -0
@@ -0,0 +1,529 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# This code is used to parse filenames that have "standard" TV show format, such as
|
3
|
+
# Boardwalk.Empire.S02E01.720p.HDTV.x264-IMMERSE.mkv
|
4
|
+
# into seasons and episodes.
|
5
|
+
|
6
|
+
# Information that uniquely identifies an episode of a show.
|
7
|
+
class ShowEpisode
|
8
|
+
def initialize
|
9
|
+
@season = nil
|
10
|
+
@episode = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_accessor :season
|
14
|
+
attr_accessor :episode
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
"S#{season}E#{episode}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# This class represents a contiguous range of episodes of a show in a single season. For example
|
22
|
+
# Season 1 Episodes 1-5, or Season 2 Episodes 7-9
|
23
|
+
class ShowEpisodeRange
|
24
|
+
def initialize(season, startEpisode, endEpisode)
|
25
|
+
@season = season
|
26
|
+
@startEpisode = startEpisode
|
27
|
+
@endEpisode = endEpisode
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_accessor :startEpisode
|
31
|
+
attr_accessor :endEpisode
|
32
|
+
attr_accessor :season
|
33
|
+
|
34
|
+
def size
|
35
|
+
@endEpisode - @startEpisode + 1
|
36
|
+
end
|
37
|
+
|
38
|
+
# Given a list of ShowEpisode objects, return an array of ShowEpisodeRange objects
|
39
|
+
# that represents the passed episodes.
|
40
|
+
def self.createRanges(episodes)
|
41
|
+
ranges = []
|
42
|
+
sorted = episodes.sort{ |a,b|
|
43
|
+
if a.season < b.season
|
44
|
+
-1
|
45
|
+
elsif a.season > b.season
|
46
|
+
1
|
47
|
+
else
|
48
|
+
a.episode <=> b.episode
|
49
|
+
end
|
50
|
+
}
|
51
|
+
|
52
|
+
firstInRange = nil
|
53
|
+
lastProcessed = nil
|
54
|
+
sorted.each{ |s|
|
55
|
+
if ! firstInRange
|
56
|
+
firstInRange = s
|
57
|
+
else
|
58
|
+
if s.season != lastProcessed.season || s.episode > lastProcessed.episode + 1
|
59
|
+
# End of range!
|
60
|
+
ranges.push ShowEpisodeRange.new(firstInRange.season, firstInRange.episode, lastProcessed.episode)
|
61
|
+
firstInRange = s
|
62
|
+
end
|
63
|
+
end
|
64
|
+
lastProcessed = s
|
65
|
+
}
|
66
|
+
if firstInRange
|
67
|
+
ranges.push ShowEpisodeRange.new(firstInRange.season, firstInRange.episode, lastProcessed.episode)
|
68
|
+
end
|
69
|
+
ranges
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
# An object that stores a list of episode ranges for a single show.
|
75
|
+
class ShowEpisodes
|
76
|
+
def initialize
|
77
|
+
@showName = nil
|
78
|
+
@episodes = []
|
79
|
+
end
|
80
|
+
|
81
|
+
attr_accessor :showName
|
82
|
+
attr_accessor :episodes
|
83
|
+
|
84
|
+
def episodeRanges
|
85
|
+
ShowEpisodeRange.createRanges(@episodes)
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
class ParsedShowName
|
91
|
+
def initialize
|
92
|
+
end
|
93
|
+
|
94
|
+
attr_accessor :showName
|
95
|
+
attr_accessor :season
|
96
|
+
attr_accessor :episode
|
97
|
+
|
98
|
+
def self.create(showName, season, episode)
|
99
|
+
rc = ParsedShowName.new
|
100
|
+
rc.showName = showName
|
101
|
+
rc.season = season
|
102
|
+
rc.episode = episode
|
103
|
+
rc
|
104
|
+
end
|
105
|
+
|
106
|
+
# Given a single episode raw string (as from a filename) in rawName, and given some metaInfo
|
107
|
+
# (such as the file's parent directory) returns an array of ParsedShowName objects representing
|
108
|
+
# the episodes found in the string.
|
109
|
+
#
|
110
|
+
# Format 1: Show.Name.S01E01.whatever.avi
|
111
|
+
# Format 2: Show.Name.S01E01E02.whatever.avi
|
112
|
+
# Format 3: Show Name season 3 episode 2 whatever.avi
|
113
|
+
# Format 4: Show.Name.1x2.whatever.avi
|
114
|
+
def self.parse(rawName, metaInfo)
|
115
|
+
rc = []
|
116
|
+
|
117
|
+
|
118
|
+
if rawName =~ /^(.*)[sS](\d+)((?:[eE]\d+)+)/
|
119
|
+
showName = self.fixShowName($1, metaInfo)
|
120
|
+
season = $2.to_i
|
121
|
+
# Parse the episode part; it might be more than one episode
|
122
|
+
episodeStr = $3
|
123
|
+
|
124
|
+
episodeStr.scan(/[eE](\d+)/){ |ep|
|
125
|
+
episode = ep.first.to_i
|
126
|
+
rc.push ParsedShowName.create(showName, season, episode)
|
127
|
+
}
|
128
|
+
# Format 3: "The Vampire Diaries Season 3 Episode 2",
|
129
|
+
elsif rawName =~ /^(.*)season[^\d]+(\d+)[^\d]+episode[^\d]+(\d+)/i
|
130
|
+
showName = self.fixShowName($1, metaInfo)
|
131
|
+
season = $2.to_i
|
132
|
+
# Parse the episode part; it might be more than one episode
|
133
|
+
episode = $3.to_i
|
134
|
+
rc.push ParsedShowName.create(showName, season, episode)
|
135
|
+
# Format 4:
|
136
|
+
elsif rawName =~ /^(.*)\.(\d+)x(\d+)\./
|
137
|
+
showName = self.fixShowName($1, metaInfo)
|
138
|
+
season = $2.to_i
|
139
|
+
# Parse the episode part; it might be more than one episode
|
140
|
+
episode = $3.to_i
|
141
|
+
rc.push ParsedShowName.create(showName, season, episode)
|
142
|
+
end
|
143
|
+
rc
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
def self.fixShowName(name, metaInfo)
|
148
|
+
if name.length == 0 && metaInfo
|
149
|
+
if metaInfo.parentDir
|
150
|
+
name = File.basename(metaInfo.parentDir)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
name = name.tr('.',' ').strip
|
155
|
+
# Strip off trailing and leading wierd characters.
|
156
|
+
if name =~ /^[_\-]*([\sa-zA-Z0-9]+)[_\-]*$/
|
157
|
+
name = $1
|
158
|
+
end
|
159
|
+
|
160
|
+
parts = name.strip.split(/\s+/)
|
161
|
+
parts.collect!{ |e|
|
162
|
+
e.capitalize
|
163
|
+
}
|
164
|
+
parts.join(' ')
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class FilenameMetaInfo
|
169
|
+
def initialize
|
170
|
+
@parentDir = nil
|
171
|
+
end
|
172
|
+
|
173
|
+
attr_reader :parentDir
|
174
|
+
|
175
|
+
def setParentDir(p)
|
176
|
+
@parentDir = p
|
177
|
+
self
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# A class that can be used to parse a series of show names into an array of ShowEpisodes objects.
|
182
|
+
# To use this class, add shows using addName, then call processNames to get the result.
|
183
|
+
class ShowNameInterpreter
|
184
|
+
def initialize
|
185
|
+
@names = []
|
186
|
+
@metaInfo = []
|
187
|
+
end
|
188
|
+
|
189
|
+
def addName(name, metaInfo = nil)
|
190
|
+
@names.push name
|
191
|
+
@metaInfo.push metaInfo
|
192
|
+
end
|
193
|
+
|
194
|
+
# Process the episode names added with addName and return a Hsah of ShowEpisodes objects, keyed by show name.
|
195
|
+
def processNames
|
196
|
+
shows = {}
|
197
|
+
i = 0
|
198
|
+
@names.each{ |n|
|
199
|
+
parsedArray = ParsedShowName.parse(n, @metaInfo[i])
|
200
|
+
parsedArray.each{ |parsed|
|
201
|
+
episodes = shows[parsed.showName]
|
202
|
+
if ! episodes
|
203
|
+
episodes = ShowEpisodes.new
|
204
|
+
episodes.showName = parsed.showName
|
205
|
+
shows[parsed.showName] = episodes
|
206
|
+
end
|
207
|
+
episode = ShowEpisode.new
|
208
|
+
episode.season = parsed.season
|
209
|
+
episode.episode = parsed.episode
|
210
|
+
episodes.episodes.push episode
|
211
|
+
}
|
212
|
+
i += 1
|
213
|
+
}
|
214
|
+
shows
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
=begin
|
219
|
+
|
220
|
+
#########
|
221
|
+
# Testing
|
222
|
+
#########
|
223
|
+
|
224
|
+
|
225
|
+
if $0 =~ /ShowNameParse.rb/
|
226
|
+
|
227
|
+
testdata = [
|
228
|
+
"A-Ha - Take On Me.Mp3",
|
229
|
+
"Boardwalk.Empire.S01E01.Boardwalk.Empire.HDTV.XviD-FQM.avi",
|
230
|
+
"Boardwalk.Empire.S01E02.The.Ivory.Tower.HDTV.XviD-FQM.avi",
|
231
|
+
"Boardwalk.Empire.S01E03.720p.HDTV.x264-CTU.mkv",
|
232
|
+
"Boardwalk.Empire.S01E04.HDTV.XviD-2HD.avi",
|
233
|
+
"Boardwalk.Empire.S01E05.Nights.in.Ballygran.HDTV.XviD-FQM.avi",
|
234
|
+
"Boardwalk.Empire.S01E06.Family.Limitation.HDTV.XviD-FQM.avi",
|
235
|
+
"Boardwalk.Empire.S01E07.Home.HDTV.XviD-FQM.avi",
|
236
|
+
"Boardwalk.Empire.S01E08.Hold.Me.in.Paradise.HDTV.XviD-FQM.avi",
|
237
|
+
"Boardwalk.Empire.S01E09.Belle.Femme.HDTV.XviD-FQM.avi",
|
238
|
+
"Boardwalk.Empire.S01E10.HDTV.XviD-FEVER.avi",
|
239
|
+
"Boardwalk.Empire.S01E11.Paris.Green.HDTV.XviD-FQM.avi",
|
240
|
+
"Boardwalk.Empire.S01E12.HDTV.XviD-FEVER.avi",
|
241
|
+
"Boardwalk.Empire.S02E01.720p.HDTV.x264-IMMERSE.mkv",
|
242
|
+
"Boardwalk.Empire.S02E02.HDTV.XviD-ASAP.avi",
|
243
|
+
"Boardwalk.Empire.S02E03.720p.HDTV.x264-IMMERSE.mkv",
|
244
|
+
"Boardwalk.Empire.S02E04.HDTV.XviD-ASAP.avi",
|
245
|
+
"Boardwalk.Empire.S02E05.HDTV.XviD-LOL.avi",
|
246
|
+
"DataRescue IDA Pro 5.1.0 Lin Advanced Edition - professional disassembler [h33t] [Original]",
|
247
|
+
"DataRescue IDA Pro v5.2.0 Advanced Edition for WindowsLinuxMac professional disassembler, SDK and DataRescue Hex-Rays Decompiler v1.0 [h33t] [Original] [Must have]",
|
248
|
+
"Dexter.S05E01.My.Bad.HDTV.XviD-FQM.avi",
|
249
|
+
"Dexter.S06E01.HDTV.XviD-ASAP.avi",
|
250
|
+
"Dexter.S06E02.Once.Upon.a.Time.HDTV.XviD-FQM.avi",
|
251
|
+
"Dexter.S06E03.HDTV.XviD-ASAP.avi",
|
252
|
+
"Dexter.S06E04.HDTV.XviD-LOL.avi",
|
253
|
+
"Dexter.S06E06.HDTV.XviD-ASAP.avi",
|
254
|
+
"download at superseeds.org True.Blood.S04E02.You.Smell.Like.Dinner.HDTV.XviD-XS.avi",
|
255
|
+
"Family.Guy.S09E01.HDTV.XviD-LOL.avi",
|
256
|
+
"Family.Guy.S09E02.HDTV.XviD-LOL.avi",
|
257
|
+
"Family.Guy.S09E03.HDTV.XviD-LOL.avi",
|
258
|
+
"Family.Guy.S09E04.HDTV.XviD-LOL.avi",
|
259
|
+
"Family.Guy.S09E05.HDTV.XviD-LOL.avi",
|
260
|
+
"Family.Guy.S09E06.HDTV.XviD-LOL.avi",
|
261
|
+
"Family.Guy.S09E07E08.HDTV.XviD-LOL.avi",
|
262
|
+
"Family.Guy.S09E10.HDTV.XviD-LOL.avi",
|
263
|
+
"Family.Guy.S09E11.HDTV.XviD-LOL.avi",
|
264
|
+
"Family.Guy.S09E12.HDTV.XviD-LOL.avi",
|
265
|
+
"Family.Guy.S09E13.HDTV.XviD-LOL.avi",
|
266
|
+
"Family.Guy.S09E14.HDTV.XviD-LOL.avi",
|
267
|
+
"Family.Guy.S09E15.HDTV.XviD-LOL.avi",
|
268
|
+
"Family.Guy.S09E18.HDTV.XviD-LOL.avi",
|
269
|
+
"Farscape - Season 1-4+The.Peacekeeper.Wars.Part.1&2",
|
270
|
+
"Firefly",
|
271
|
+
"Frasier Season 9",
|
272
|
+
"Futurama.S06E13.Holiday.Spectacular.HDTV.XviD-aAF.[VTV].avi",
|
273
|
+
"Futurama.S06E14.Neutopia.HDTV.XviD-FQM.avi",
|
274
|
+
"Futurama.S06E15.Benderama.HDTV.XviD-FQM.avi",
|
275
|
+
"Futurama.S06E16.Ghost.in.the.Machines.HDTV.XviD-FQM.avi",
|
276
|
+
"Futurama.S06E17.HDTV.XviD-ASAP.avi",
|
277
|
+
"Futurama.S06E19.HDTV.XviD-ASAP.avi",
|
278
|
+
"Futurama.S06E20.All.the.Presidents.Heads.HDTV.XviD-FQM.avi",
|
279
|
+
"Futurama.S06E21.HDTV.XviD-ASAP.avi",
|
280
|
+
"Futurama.S06E22.HDTV.XviD-ASAP.avi",
|
281
|
+
"Futurama.S06E23.HDTV.XviD-ASAP.avi",
|
282
|
+
"Futurama.S06E25.HDTV.XviD-ASAP.avi",
|
283
|
+
"Futurama.S06E26.HDTV.XviD-ASAP.avi",
|
284
|
+
"Game Of Thrones Season 1 - Complete",
|
285
|
+
"Hanna [2011] BRRip XviD - CODY",
|
286
|
+
"Homeland.S01E01.HDTV.XviD-ASAP.[VTV].avi",
|
287
|
+
"Homeland.S01E02.Grace.HDTV.XviD-FQM.[VTV].avi",
|
288
|
+
"Homeland.S01E03.HDTV.XviD-ASAP.[VTV].avi",
|
289
|
+
"Homeland.S01E04.HDTV.XviD-ASAP.[VTV].avi",
|
290
|
+
"House.S07E17.PROPER.HDTV.XviD-2HD.avi",
|
291
|
+
"House.S07E18.HDTV.XviD-LOL.avi",
|
292
|
+
"House.S07E19.HDTV.XviD-LOL.avi",
|
293
|
+
"House.S07E20.HDTV.XviD-LOL.avi",
|
294
|
+
"House.S07E21.HDTV.XviD-LOL.avi",
|
295
|
+
"House.S07E22.HDTV.XviD-LOL.avi",
|
296
|
+
"House.S07E23.Moving.On.HDTV.XviD-2HD.avi",
|
297
|
+
"House.S08E01.HDTV.XviD-LOL.avi",
|
298
|
+
"House.S08E02.HDTV.XviD-LOL.avi",
|
299
|
+
"House.S08E03.HDTV.XviD-LOL.avi",
|
300
|
+
"House.S08E04.HDTV.XviD-LOL.avi",
|
301
|
+
"House.S08E05.HDTV.XviD-LOL.avi",
|
302
|
+
"How.I.Met.Your.Mother.S05E11.Last.Cigarette.Ever.HDTV.XviD-FQM.[VTV].avi",
|
303
|
+
"informants-1hr17min-md5-682bdf4a9c7e1918a286e00c0bc88c16.ogg",
|
304
|
+
"KNOPPIX_V6.7.1CD-2011-09-14-EN",
|
305
|
+
"Lord of Illusions",
|
306
|
+
"Minority_Report.avi",
|
307
|
+
"Necessary.Roughness.S01E09.HDTV.XviD-LOL.avi",
|
308
|
+
"Nurse.Jackie.S03E06.When.The.Saints.Go.HDTV.XviD-FQM.avi",
|
309
|
+
"Nurse.Jackie.S03E07.Orchids.and.Salami.PROPER.HDTV.XviD-FQM.avi",
|
310
|
+
"Nurse.Jackie.S03E08.HDTV.XviD-ASAP.avi",
|
311
|
+
"Nurse.Jackie.S03E09.HDTV.XviD-FEVER.avi",
|
312
|
+
"Nurse.Jackie.S03E10.HDTV.XviD-LOL.avi",
|
313
|
+
"Nurse.Jackie.S03E11.Batting.Practice.HDTV.XviD-FQM.avi",
|
314
|
+
"Nurse.Jackie.S03E12.HDTV.XviD-ASAP.avi",
|
315
|
+
"Rescue.Me.S07E07.Jeter.HDTV.XviD-FQM.avi",
|
316
|
+
"Robot.Chicken.S05E01.HDTV.XviD-2HD.avi",
|
317
|
+
"Robot.Chicken.S05E02.HDTV.XviD-2HD.avi",
|
318
|
+
"Robot.Chicken.S05E03.HDTV.XviD-2HD.avi",
|
319
|
+
"Robot.Chicken.S05E04.HDTV.XviD-2HD.avi",
|
320
|
+
"Robot.Chicken.S05E05.HDTV.XviD-2HD.avi",
|
321
|
+
"SlackerUprising_640x360.avi",
|
322
|
+
"Terra.Nova.S01E01.Genesis.HDTV.XviD-FQM.avi",
|
323
|
+
"Terra.Nova.S01E03.HDTV.XviD-LOL.avi",
|
324
|
+
"Terra.Nova.S01E04.HDTV.XviD-LOL.avi",
|
325
|
+
"Terra.Nova.S01E06.HDTV.XviD-LOL.avi",
|
326
|
+
"The.Big.Bang.Theory.S04E19.PROPER.HDTV.XviD-FEVER.avi",
|
327
|
+
"The.Big.Bang.Theory.S04E20.HDTV.XviD-ASAP.avi",
|
328
|
+
"The.Big.Bang.Theory.S04E21.HDTV.XviD-ASAP.avi",
|
329
|
+
"The.Big.Bang.Theory.S04E22.HDTV.XviD-ASAP.avi",
|
330
|
+
"The.Big.Bang.Theory.S04E23.The.Engagement.Reaction.HDTV.XviD-FQM.avi",
|
331
|
+
"The.Big.Bang.Theory.S04E24.The.Roomate.Transmogrification.HDTV.XviD-FQM.avi",
|
332
|
+
"The.Big.Bang.Theory.S05E01.HDTV.XviD-ASAP.avi",
|
333
|
+
"The.Big.Bang.Theory.S05E02.HDTV.XviD-ASAP.avi",
|
334
|
+
"The.Big.Bang.Theory.S05E03.HDTV.XviD-ASAP.avi",
|
335
|
+
"The Big Bang Theory S05E03 The Pulled Groin Extrapolation HDTV Xvid DutchReleaseTeam (dutch subs nl)",
|
336
|
+
"The.Big.Bang.Theory.S05E04.The.Wiggly.Finger.Catalyst.HDTV.XviD-FQM.avi",
|
337
|
+
"The.Big.Bang.Theory.S05E05.The.Russian.Rocket.Reaction.HDTV.XviD-FQM.avi",
|
338
|
+
"The.Big.Bang.Theory.S05E06.HDTV.XviD-ASAP.avi",
|
339
|
+
"The.Big.Bang.Theory.S05E07.HDTV.XviD-2HD.avi",
|
340
|
+
"The.Big.Bang.Theory.S05E08.HDTV.XviD-ASAP.avi",
|
341
|
+
"The Big Bang Theory Season 5 Episode 6 - The Rhinitis Revelation",
|
342
|
+
"The.Men.Who.Stares.At.Goats.2009.R5.Line.XviD-PrisM.avi",
|
343
|
+
"The NeverEnding Story[1984]DvDrip[720x436]AC3[6ch][Eng]-RHooD",
|
344
|
+
"The.Simpsons.S23E01.HDTV.XviD-LOL.avi",
|
345
|
+
"The.Simpsons.S23E03.HDTV.XviD-LOL.avi",
|
346
|
+
"The.Simpsons.S23E04.HDTV.XviD-LOL.avi",
|
347
|
+
"The.Vampire.Diaries.S01E18.HDTV.XviD-2HD.avi",
|
348
|
+
"The.Vampire.Diaries.S02E15.The.Dinner.Party.HDTV.XviD-FQM.avi",
|
349
|
+
"The.Vampire.Diaries.S02E16.HDTV.XviD-2HD.avi",
|
350
|
+
"The.Vampire.Diaries.S02E17.HDTV.XviD-2HD.avi",
|
351
|
+
"The.Vampire.Diaries.S02E18.HDTV.XviD-2HD.jHONY.avi",
|
352
|
+
"The.Vampire.Diaries.S02E19.PROPER.HDTV.XviD-2HD.avi",
|
353
|
+
"The.Vampire.Diaries.S02E20.HDTV.XviD-2HD.avi",
|
354
|
+
"The.Vampire.Diaries.S02E21.HDTV.XviD-ASAP.avi",
|
355
|
+
"The.Vampire.Diaries.S02E22.As.I.Lay.Dying.HDTV.XviD-FQM.avi",
|
356
|
+
"The.Vampire.Diaries.S03E01.HDTV.XviD-2HD",
|
357
|
+
"the.vampire.diaries.s03e03.hdtv.xvid-2hd.avi",
|
358
|
+
"The Vampire Diaries S03E04.avi",
|
359
|
+
"The.Vampire.Diaries.S03E05.HDTV.XviD-P0W4.avi",
|
360
|
+
"The.Vampire.Diaries.S03E06.HDTV.XviD-2HD.avi",
|
361
|
+
"The.Vampire.Diaries.S03E07.HDTV.XviD-2HD.avi",
|
362
|
+
"The.Vampire.Diaries.S03E08.REPACK.HDTV.XviD-2HD.avi",
|
363
|
+
"The Vampire Diaries Season 3 Episode 2",
|
364
|
+
"The.Walking.Dead.S01E01.Days.Gone.Bye.HDTV.XviD-FQM.[VTV].avi",
|
365
|
+
"True.Blood.S04E01.HDTV.XviD-LOL.avi",
|
366
|
+
"True.Blood.S04E02.You.Smell.Like.Dinner.PROPER.HDTV.XviD-FQM.avi",
|
367
|
+
"True.Blood.S04E03.HDTV.XviD-LOL.avi",
|
368
|
+
"True.Blood.S04E04.720p.HDTV.x264-IMMERSE.mkv",
|
369
|
+
"True.Blood.S04E05.HDTV.XviD-ASAP.avi",
|
370
|
+
"True.Blood.S04E06.HDTV.XviD-ASAP.avi",
|
371
|
+
"True.Blood.S04E07.HDTV.XviD-ASAP.avi",
|
372
|
+
"True.Blood.S04E08.HDTV.XviD-ASAP.avi",
|
373
|
+
"True.Blood.S04E09.HDTV.XviD-LOL.avi",
|
374
|
+
"True.Blood.S04E10.Burning.Down.the.House.PROPER.HDTV.XviD-FQM.avi",
|
375
|
+
"True.Blood.S04E11.HDTV.XviD-LOL.avi",
|
376
|
+
"True.Blood.S04E12.720p.HDTV.X264-DIMENSION.mkv",
|
377
|
+
"Two.and.a.Half.Men.S09E01.HDTV.XviD-ASAP.avi",
|
378
|
+
"Two.and.a.Half.Men.S09E02.HDTV.XviD-ASAP.avi",
|
379
|
+
"Two.and.a.Half.Men.S09E03.HDTV.XviD-ASAP.avi",
|
380
|
+
"Two.and.a.Half.Men.S09E04.HDTV.XviD-ASAP.avi",
|
381
|
+
"Two.and.a.Half.Men.S09E05.A.Giant.Cat.Holding.a.Churro.HDTV.XviD-FQM.avi",
|
382
|
+
"Two.and.a.Half.Men.S09E06.HDTV.XviD-ASAP.avi",
|
383
|
+
"Two.and.a.Half.Men.S09E07.HDTV.XviD-ASAP.avi",
|
384
|
+
"Wild.Boys.S01E01.WS.PDTV.XviD-BWB.avi",
|
385
|
+
"YTMND - The Soundtrack (Remastered)"
|
386
|
+
]
|
387
|
+
|
388
|
+
if ARGV.size > 0
|
389
|
+
directory = ARGV[0]
|
390
|
+
if ! File.directory?(directory)
|
391
|
+
puts "'#{dir}' is not a directory."
|
392
|
+
exit 1
|
393
|
+
end
|
394
|
+
|
395
|
+
puts "Processing files under #{directory}"
|
396
|
+
|
397
|
+
interp = ShowNameInterpreter.new
|
398
|
+
|
399
|
+
def filesUnder(dir)
|
400
|
+
Dir.new(dir).each{ |e|
|
401
|
+
next if e[0,1] == '.'
|
402
|
+
path = dir + "/" + e
|
403
|
+
if File.directory?(path)
|
404
|
+
filesUnder(path){ |f,d|
|
405
|
+
yield f,d
|
406
|
+
}
|
407
|
+
else
|
408
|
+
yield e, dir
|
409
|
+
end
|
410
|
+
}
|
411
|
+
end
|
412
|
+
|
413
|
+
filesUnder(directory){ |e, dir|
|
414
|
+
if e[0,1] != '.'
|
415
|
+
interp.addName(e, FilenameMetaInfo.new.setParentDir(dir))
|
416
|
+
end
|
417
|
+
}
|
418
|
+
|
419
|
+
shows = interp.processNames
|
420
|
+
shows.each{ |k,v|
|
421
|
+
print k + ":"
|
422
|
+
ranges = v.episodeRanges
|
423
|
+
season = nil
|
424
|
+
comma = true
|
425
|
+
ranges.each{ |r|
|
426
|
+
if ! season || season != r.season
|
427
|
+
puts
|
428
|
+
print " Season #{r.season}: "
|
429
|
+
season = r.season
|
430
|
+
comma = false
|
431
|
+
end
|
432
|
+
print "," if comma
|
433
|
+
if r.size > 1
|
434
|
+
print " #{r.startEpisode}-#{r.endEpisode}"
|
435
|
+
else
|
436
|
+
print " #{r.startEpisode}"
|
437
|
+
end
|
438
|
+
comma = true
|
439
|
+
}
|
440
|
+
puts
|
441
|
+
}
|
442
|
+
|
443
|
+
exit 0
|
444
|
+
end
|
445
|
+
|
446
|
+
class Test
|
447
|
+
def initialize(caption)
|
448
|
+
@caption = caption
|
449
|
+
end
|
450
|
+
|
451
|
+
def assert(bool, text = nil)
|
452
|
+
if bool
|
453
|
+
pass
|
454
|
+
else
|
455
|
+
fail(text)
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
def equals(expected, actual)
|
460
|
+
if expected == actual
|
461
|
+
pass
|
462
|
+
else
|
463
|
+
fail("expected #{expected} but was #{actual}")
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
def pass
|
468
|
+
puts "[PASS] #{@caption}"
|
469
|
+
end
|
470
|
+
|
471
|
+
def fail(text = nil)
|
472
|
+
rc = "[FAIL] #{@caption}"
|
473
|
+
rc = rc << ": #{text}" if text
|
474
|
+
puts rc
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
def dotest(shows)
|
479
|
+
# Check family guy
|
480
|
+
episodes = shows["Family Guy"]
|
481
|
+
t = Test.new("Family Guy was parsed")
|
482
|
+
t.assert(episodes)
|
483
|
+
if episodes
|
484
|
+
t = Test.new("Family Guy ranges")
|
485
|
+
ranges = episodes.episodeRanges
|
486
|
+
t.equals(3,ranges.size)
|
487
|
+
if 3 == ranges.size
|
488
|
+
t.equals(8,ranges[0].size)
|
489
|
+
t.equals(6,ranges[1].size)
|
490
|
+
t.equals(1,ranges[2].size)
|
491
|
+
t.equals(1,ranges[0].startEpisode)
|
492
|
+
t.equals(8,ranges[0].endEpisode)
|
493
|
+
t.equals(10,ranges[1].startEpisode)
|
494
|
+
t.equals(15,ranges[1].endEpisode)
|
495
|
+
t.equals(18,ranges[2].startEpisode)
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
# Check vampire diaries
|
500
|
+
episodes = shows["The Vampire Diaries"]
|
501
|
+
t = Test.new("Vampire Diaries was parsed")
|
502
|
+
t.assert(episodes)
|
503
|
+
if episodes
|
504
|
+
t = Test.new("Vampire Diaries ranges")
|
505
|
+
ranges = episodes.episodeRanges
|
506
|
+
t.equals(3,ranges.size)
|
507
|
+
if 3 == ranges.size
|
508
|
+
t.equals(1,ranges[0].size)
|
509
|
+
t.equals(8,ranges[1].size)
|
510
|
+
t.equals(8,ranges[2].size)
|
511
|
+
t.equals(18,ranges[0].startEpisode)
|
512
|
+
t.equals(18,ranges[0].endEpisode)
|
513
|
+
t.equals(1,ranges[0].season)
|
514
|
+
|
515
|
+
t.equals(15,ranges[1].startEpisode)
|
516
|
+
t.equals(22,ranges[1].endEpisode)
|
517
|
+
t.equals(2,ranges[1].season)
|
518
|
+
|
519
|
+
t.equals(1,ranges[2].startEpisode)
|
520
|
+
t.equals(8,ranges[2].endEpisode)
|
521
|
+
t.equals(3,ranges[2].season)
|
522
|
+
end
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
dotest(shows)
|
527
|
+
|
528
|
+
end
|
529
|
+
=end
|
@@ -0,0 +1 @@
|
|
1
|
+
link "Shows", "/#/show_list"
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require './plugins/shows/lib/showname_parse'
|
2
|
+
|
3
|
+
get "/show_list" do
|
4
|
+
interp = ShowNameInterpreter.new
|
5
|
+
def filesUnder(dir)
|
6
|
+
Dir.new(dir).each do |e|
|
7
|
+
next if e[0,1] == '.'
|
8
|
+
path = dir + "/" + e
|
9
|
+
if File.directory?(path)
|
10
|
+
filesUnder(path){ |f,d|
|
11
|
+
yield f,d
|
12
|
+
}
|
13
|
+
else
|
14
|
+
yield e, dir
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
filesUnder(settings.basedir) do |e, dir|
|
20
|
+
if e[0,1] != '.'
|
21
|
+
interp.addName(e, FilenameMetaInfo.new.setParentDir(dir))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
shows = interp.processNames
|
26
|
+
|
27
|
+
showsForDisplay = []
|
28
|
+
shows.keys.sort.each do |k|
|
29
|
+
# Show name
|
30
|
+
heading = k
|
31
|
+
# Show episodes
|
32
|
+
body = ""
|
33
|
+
ranges = shows[k]
|
34
|
+
season = nil
|
35
|
+
comma = true
|
36
|
+
ranges.episodeRanges.each do |r|
|
37
|
+
if ! season || season != r.season
|
38
|
+
body << "<br/>" if season
|
39
|
+
body << " Season #{r.season}: "
|
40
|
+
season = r.season
|
41
|
+
comma = false
|
42
|
+
end
|
43
|
+
body << "," if comma
|
44
|
+
if r.size > 1
|
45
|
+
body << " #{r.startEpisode}-#{r.endEpisode}"
|
46
|
+
else
|
47
|
+
body << " #{r.startEpisode}"
|
48
|
+
end
|
49
|
+
comma = true
|
50
|
+
end
|
51
|
+
showsForDisplay.push [heading, body]
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
menu = haml :menu_partial, :locals => { :links => settings.menuLinks, :active_link => "Shows" }
|
56
|
+
haml :show_list_partial, :views => "plugins/shows/views", :locals => {:menu => menu, :showsForDisplay => showsForDisplay}
|
57
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
=locals[:menu]
|
2
|
+
|
3
|
+
#accordion{:class => "panel-group" }
|
4
|
+
- showsForDisplay = locals[:showsForDisplay]
|
5
|
+
- num = 0
|
6
|
+
- showsForDisplay.each do |head, body|
|
7
|
+
%div{:class => "panel panel-default"}
|
8
|
+
.panel-heading
|
9
|
+
%h4{:class => "panel-title"}
|
10
|
+
%a{ "data-toggle" => "collapse", "data-parent" => "#accordion", :href => "#collapse#{num}" }
|
11
|
+
= head
|
12
|
+
%div{ :id => "collapse#{num}", :class => "panel-collapse collapse #{num == 0 ? "in" : ""}"}
|
13
|
+
.panel-body
|
14
|
+
= body
|
15
|
+
- num += 1
|
16
|
+
|
17
|
+
|
18
|
+
-# Tile-based display:
|
19
|
+
-#- showsForDisplay = locals[:showsForDisplay]
|
20
|
+
-#- colCount = 6
|
21
|
+
-#- showsForDisplay.each do |head, body|
|
22
|
+
-# - if colCount >= 6
|
23
|
+
-# %div{ :class => :row }
|
24
|
+
-# - colCount = 0
|
25
|
+
-# %div{ :class => "col-md-2" }
|
26
|
+
-# %div{ :class => "list-group" }
|
27
|
+
-# %div{:class => "list-group-item" }
|
28
|
+
-# %h4{:class => "list-group-item-heading"}
|
29
|
+
-# = head
|
30
|
+
-# %p{:class => "list-group-item-text"}
|
31
|
+
-# = body
|
32
|
+
-# - colCount += 1
|
33
|
+
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: quartz_flow_plugin_shows
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jeff Williams
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-11-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: quartz_flow
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.0.2
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.0.2
|
30
|
+
description: Plugin for quartz_flow that displays downloaded shows
|
31
|
+
email:
|
32
|
+
executables: []
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- plugins/shows/lib/showname_parse.rb
|
37
|
+
- plugins/shows/routes.rb
|
38
|
+
- plugins/shows/views/show_list_partial.haml
|
39
|
+
- plugins/shows/links.rb
|
40
|
+
homepage: https://github.com/jeffwilliams/quartz-flow
|
41
|
+
licenses: []
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.8.23
|
61
|
+
signing_key:
|
62
|
+
specification_version: 3
|
63
|
+
summary: Plugin for quartz_flow that displays downloaded shows
|
64
|
+
test_files: []
|
65
|
+
has_rdoc: false
|