quartz_flow_plugin_shows 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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