imdb-terminal 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +60 -0
- data/bin/imdb +787 -0
- metadata +69 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 668a4e977795d0075d70382890a72e911688c452d67cb02ba432f1cfefdacd01
|
4
|
+
data.tar.gz: 4d1ac685489c8b509349841fc55e00285576ed9be0815802740731e5762cb954
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 627fbd556497607248a0ebb78012d0ce9b5577eab31a0b8f8446c0b65b1aaba6bb78cc25216d0c115db672a6b179e21b468f86727ce09a2f397d98a0a8ceb893
|
7
|
+
data.tar.gz: 87531c704b0b7b1d5bea5d2a8bd91b656f08a6ded7d4639c2441ca3923a6c282920bdbe74c430a70615e117e58c272fa468f7f27bda0a7c1735ec5f8e4a6fbb8
|
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
|
2
|
+
![Ruby](https://img.shields.io/badge/language-Ruby-red) [![Gem Version](https://badge.fury.io/rb/imdb-terminal.svg)](https://badge.fury.io/rb/imdb-terminal) ![Unlicense](https://img.shields.io/badge/license-Unlicense-green) ![Stay Amazing](https://img.shields.io/badge/Stay-Amazing-important)
|
3
|
+
|
4
|
+
# IMDB for the terminal
|
5
|
+
|
6
|
+
Finding the right moviie to watch or series to binge can be time consuming. My
|
7
|
+
girlfriend asked if I could create an application that would cut down the time
|
8
|
+
spent on searching in favor of time spent watching and cuddling. Meet
|
9
|
+
IMDB for the terminal.
|
10
|
+
|
11
|
+
Narrow down your preferences from a 1000 movies and almost 500 series. Select
|
12
|
+
a minimum IMDB rating, range of production years, genres you like and dislike
|
13
|
+
to get your preferred list. Get detailed information on movies and series and
|
14
|
+
where you can stream them. Even the movie poster in the terminal.
|
15
|
+
|
16
|
+
IMDB for the terminal uses three APIs - one to scrape IMDB for the top 1000
|
17
|
+
lists, one to give you details on the movies and series and one to show you
|
18
|
+
where you can stream it. When you first run the application, it will guide you
|
19
|
+
through the process of registering free accounts and get the three API keys
|
20
|
+
needed. I would be happy to keys in the application, but there is a limit to
|
21
|
+
the number of calls each day, so you need your own keys. The key to scrape the
|
22
|
+
IMDB top 1000 is via `page2api`. You will get a certain amounts of credits for
|
23
|
+
free when you sign up, and it will last you a very long time since the program
|
24
|
+
caches the movies and series lists. You could probably do well with refreshing
|
25
|
+
the lists once per month or so (using the key `I`).
|
26
|
+
|
27
|
+
As for the application itself, it's a terminal curses program that is best
|
28
|
+
installed with `gem install imdb-terminal` and run the program `imdb`in the
|
29
|
+
terminal. Or you can clone this repo and run the single file `imdb`. As usual,
|
30
|
+
I try to keep the requirements to a minimum and the program to a single file.
|
31
|
+
|
32
|
+
# Screenshot
|
33
|
+
The screenshot gives you an overview of the layout of the panes:
|
34
|
+
![IMDB-terminal screenshot](img/screenshot.png)
|
35
|
+
|
36
|
+
# Keymap
|
37
|
+
|
38
|
+
Keys | Function
|
39
|
+
-----------|--------------------
|
40
|
+
TAB | Cycle the panes (active pane is gray)
|
41
|
+
Shift-TAB | Cycle backward
|
42
|
+
Arrow keys | UP, DOWN, PgUP, PgDOWN, HOME, END in lists
|
43
|
+
\+ or - | Depends on pane (intuitive)
|
44
|
+
I | Load fresh IMDB data (be patient)
|
45
|
+
m or s | Show MOVIES or SERIES
|
46
|
+
r | Select MINimum IMDB rating (in bottom line)
|
47
|
+
y or Y | Select MINimum or MAXimum production year
|
48
|
+
/ or \\ | Enter or clear search for movie/series title
|
49
|
+
G | Set genres to match every movie/series
|
50
|
+
d | Get details on selected movie/series
|
51
|
+
D | Show where you can stream the movie/series
|
52
|
+
R | Refresh all panes
|
53
|
+
w or W | Write changes to config or save IMDB data
|
54
|
+
q or Q | Quit w/saving config or w/o saving config
|
55
|
+
|
56
|
+
# Suggestions and improvements
|
57
|
+
If you have any suggestions for improvement or cool features, create an issue
|
58
|
+
in this repository.
|
59
|
+
|
60
|
+
For more stuff, check out https://isene.com
|
data/bin/imdb
ADDED
@@ -0,0 +1,787 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
# PROGRAM INFO
|
5
|
+
# Name: IMDB-term
|
6
|
+
# Language: Pure Ruby, best viewed in VIM
|
7
|
+
# Author: Geir Isene <g@isene.com>
|
8
|
+
# Web_site: http://isene.com/
|
9
|
+
# Github: https://github.com/isene/imdb-term
|
10
|
+
# License: I release all copyright claims. This code is in the public domain.
|
11
|
+
# Permission is granted to use, copy modify, distribute, and sell
|
12
|
+
# this software for any purpose. I make no guarantee about the
|
13
|
+
# suitability of this software for any purpose and I am not liable
|
14
|
+
# for any damages resulting from its use. Further, I am under no
|
15
|
+
# obligation to maintain or extend this software. It is provided
|
16
|
+
# on an 'as is' basis without any expressed or implied warranty.
|
17
|
+
|
18
|
+
# PRELIMINARIES
|
19
|
+
@help = <<HELPTEXT
|
20
|
+
IMDB-term (https://github.com/isene/IMDB) Help text:
|
21
|
+
|
22
|
+
Keys | Function
|
23
|
+
-----------+--------------------
|
24
|
+
TAB | Cycle the panes (active pane is gray)
|
25
|
+
Shift-TAB | Cycle backward
|
26
|
+
Arrow keys | UP, DOWN, PgUP, PgDOWN, HOME, END in lists
|
27
|
+
+ or - | Depends on pane (intuitive)
|
28
|
+
I | Load fresh IMDB data (be patient)
|
29
|
+
m or s | Show MOVIES or SERIES
|
30
|
+
r | Select MINimum IMDB rating (in bottom line)
|
31
|
+
y or Y | Select MINimum or MAXimum production year
|
32
|
+
/ or \\ | Enter or clear search for movie/series title
|
33
|
+
G | Set genres to match every movie/series
|
34
|
+
d | Get details on selected movie/series
|
35
|
+
D | Show where you can stream the movie/series
|
36
|
+
R | Refresh all panes
|
37
|
+
w or W | Write changes to config or save IMDB data
|
38
|
+
q or Q | Quit w/saving config or w/o saving config
|
39
|
+
HELPTEXT
|
40
|
+
begin # BASIC SETUP
|
41
|
+
require 'net/http'
|
42
|
+
require 'open-uri'
|
43
|
+
require 'rest_client'
|
44
|
+
require 'json'
|
45
|
+
require 'readline'
|
46
|
+
require 'io/console'
|
47
|
+
require 'curses'
|
48
|
+
include Curses
|
49
|
+
|
50
|
+
def cmd?(command)
|
51
|
+
system("which #{command} > /dev/null 2>&1")
|
52
|
+
end
|
53
|
+
if cmd?('/usr/lib/w3m/w3mimgdisplay')
|
54
|
+
@w3mimgdisplay = "/usr/lib/w3m/w3mimgdisplay"
|
55
|
+
@showimage = true
|
56
|
+
else
|
57
|
+
@showimage = false
|
58
|
+
end
|
59
|
+
@showimage = false unless (cmd?('xwininfo') and cmd?('xdotool'))
|
60
|
+
|
61
|
+
begin # Check if network is available
|
62
|
+
URI.open("https://www.google.com/", :open_timeout=>5)
|
63
|
+
rescue
|
64
|
+
puts "\nNo network. Running offline.\n\n"
|
65
|
+
end
|
66
|
+
|
67
|
+
# INITIALIZE BASIC VARIABLES
|
68
|
+
## These can be set in .imdb.conf
|
69
|
+
@rating = 0
|
70
|
+
@yearMin = 0
|
71
|
+
@yearMax = 2100
|
72
|
+
@myMY = []
|
73
|
+
@myMN = []
|
74
|
+
@mySY = []
|
75
|
+
@mySN = []
|
76
|
+
## These are IMDB specific
|
77
|
+
@urlmovies = "https://www.imdb.com/search/title/?groups=top_1000&start=[1, 1001, 50]"
|
78
|
+
@urlseries = "https://www.imdb.com/search/title/?title_type=tv_series&start=[1, 1001, 50]"
|
79
|
+
@country = "no"
|
80
|
+
@lang = "en"
|
81
|
+
@genres = ["Action", "Adult", "Adventure", "Animation", "Biography", "Comedy", "Crime", "Documentary", "Drama",
|
82
|
+
"Family", "Fantasy", "Film Noir", "Game Show", "History", "Horror", "Musical", "Music", "Mystery",
|
83
|
+
"News", "Reality-TV", "Romance", "Sci-Fi", "Short", "Sport", "Talk-Show", "Thriller", "War", "Western"]
|
84
|
+
@genY = @genres.map(&:clone)
|
85
|
+
@genN = []
|
86
|
+
## These are internal variables
|
87
|
+
@imdbmovies = []
|
88
|
+
@imdbseries = []
|
89
|
+
@imdbsel = []
|
90
|
+
@search = ''
|
91
|
+
@new = false
|
92
|
+
@movies = true
|
93
|
+
@noimage = false
|
94
|
+
|
95
|
+
## Color constants (e.g. Window x fg)
|
96
|
+
WtMfg = 232
|
97
|
+
WtSfg = 255
|
98
|
+
WtMbg = 214
|
99
|
+
WtMbgS = 202
|
100
|
+
WtSbg = 23
|
101
|
+
WtSbgS = 58
|
102
|
+
Wifg = 230
|
103
|
+
Wgfg = 195
|
104
|
+
WgYfg = 34
|
105
|
+
WgNfg = 241
|
106
|
+
Wmfg = 117
|
107
|
+
Wnfg = 204
|
108
|
+
Wdfg = 255
|
109
|
+
Wpfg = 242
|
110
|
+
Wbfg = 252
|
111
|
+
Wbbg = 238
|
112
|
+
Mark = 240
|
113
|
+
WAbg = 233
|
114
|
+
WIbg = 232
|
115
|
+
end
|
116
|
+
|
117
|
+
# CLASSES
|
118
|
+
class Curses::Window # CLASS EXTENSION
|
119
|
+
attr_accessor :fg, :bg, :attr, :text, :index, :list, :update
|
120
|
+
# General extensions (see https://github.com/isene/Ruby-Curses-Class-Extension)
|
121
|
+
def clr
|
122
|
+
self.setpos(0, 0)
|
123
|
+
self.maxy.times {self.deleteln()}
|
124
|
+
self.refresh
|
125
|
+
self.setpos(0, 0)
|
126
|
+
end
|
127
|
+
def fill # Fill window with color as set by :bg
|
128
|
+
self.setpos(0, 0)
|
129
|
+
self.bg = 0 if self.bg == nil
|
130
|
+
self.fg = 255 if self.fg == nil
|
131
|
+
xor = self.fg ^ self.bg
|
132
|
+
init_pair(xor, self.fg, self.bg)
|
133
|
+
blank = " " * self.maxx
|
134
|
+
self.maxy.times {self.attron(color_pair(xor)) {self << blank}}
|
135
|
+
self.refresh
|
136
|
+
self.setpos(0, 0)
|
137
|
+
end
|
138
|
+
def write # Write context of :text to window with attributes :attr
|
139
|
+
self.bg = 0 if self.bg == nil
|
140
|
+
self.fg = 255 if self.fg == nil
|
141
|
+
xor = self.fg ^ self.bg
|
142
|
+
init_pair(xor, self.fg, self.bg)
|
143
|
+
self.attr = 0 if self.attr == nil
|
144
|
+
self.attron(color_pair(xor) | self.attr) { self << self.text }
|
145
|
+
self.refresh
|
146
|
+
self.text = ""
|
147
|
+
end
|
148
|
+
def p(fg, bg, attr, text)
|
149
|
+
init_pair(fg ^ bg, fg, bg)
|
150
|
+
self.attron(color_pair(fg ^ bg) | attr) { self << text }
|
151
|
+
self.refresh
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# GENERIC FUNCTIONS
|
156
|
+
def firstrun
|
157
|
+
puts "Welcome to IMDB-term, the IMDB application for the terminal."
|
158
|
+
puts "\nFind your next movie or series to binge. Narrow down your preferences from a 1000 movies and almost 500 series."
|
159
|
+
puts "Select a minimum IMDB rating, range of production years, genres you like and dislike to get your preferred list."
|
160
|
+
puts "Get detailed information on movies and series and where you can stream them. Even the movie poster in the terminal."
|
161
|
+
puts "\nLet's first look at the help text (accessible in the program via the key '?'):\n\n"
|
162
|
+
puts @help
|
163
|
+
print "\nPress any key... "; STDIN.getch
|
164
|
+
system("clear")
|
165
|
+
puts "We will now walk you through the steps you need to do to make use of this application:"
|
166
|
+
puts "\n 1. Go to the website https://www.page2api.com/"
|
167
|
+
print " Create a free account and paste your API_KEY here (then press ENTER): "
|
168
|
+
conf = "@imdbkey = '"
|
169
|
+
conf += gets.chomp + "'\n"
|
170
|
+
puts "\n 2. Go to the website https://www.omdbapi.com/apikey.aspx"
|
171
|
+
print " Create a free account and paste your API KEY here (then press ENTER): "
|
172
|
+
conf += "@omdbkey = '"
|
173
|
+
conf += gets.chomp + "'\n"
|
174
|
+
puts "\n 3. Go to the website https://rapidapi.com/movie-of-the-night-movie-of-the-night-default/api/streaming-availability"
|
175
|
+
print " Create a free account and paste your X-RapidAPI-Key here (then press ENTER): "
|
176
|
+
conf += "@streamkey = '"
|
177
|
+
conf += gets.chomp + "'\n"
|
178
|
+
File.write(Dir.home+'/.imdb.conf', conf)
|
179
|
+
puts "\n\nYour keys have now been written to the configuration file (.imdb.conf). You can edit this file manually if needed."
|
180
|
+
print "\nPress 'y' to start imdb-term "; y = STDIN.getch
|
181
|
+
exit if y != "y"
|
182
|
+
end
|
183
|
+
def getchr # PROCESS KEY PRESSES
|
184
|
+
c = STDIN.getch #(min: 0, time: 1)
|
185
|
+
case c
|
186
|
+
when "\e" # ANSI escape sequences
|
187
|
+
case $stdin.getc
|
188
|
+
when '[' # CSI
|
189
|
+
case $stdin.getc
|
190
|
+
when 'A' then chr = "UP"
|
191
|
+
when 'B' then chr = "DOWN"
|
192
|
+
when 'C' then chr = "RIGHT"
|
193
|
+
when 'D' then chr = "LEFT"
|
194
|
+
when 'Z' then chr = "S-TAB"
|
195
|
+
when '2' then chr = "INS" ; STDIN.getc
|
196
|
+
when '3' then chr = "DEL" ; STDIN.getc
|
197
|
+
when '5' then chr = "PgUP" ; STDIN.getc
|
198
|
+
when '6' then chr = "PgDOWN" ; STDIN.getc
|
199
|
+
when '7' then chr = "HOME" ; STDIN.getc
|
200
|
+
when '8' then chr = "END" ; STDIN.getc
|
201
|
+
end
|
202
|
+
end
|
203
|
+
when "", "" then chr = "BACK"
|
204
|
+
when "" then chr = "C-C"
|
205
|
+
when "" then chr = "C-G"
|
206
|
+
when "" then chr = "C-T"
|
207
|
+
when "" then chr = "LDEL"
|
208
|
+
when "" then chr = "WBACK"
|
209
|
+
when "\r" then chr = "ENTER"
|
210
|
+
when "\t" then chr = "TAB"
|
211
|
+
when /./ then chr = c
|
212
|
+
end
|
213
|
+
return chr
|
214
|
+
end
|
215
|
+
def getkey # GET KEY FROM USER
|
216
|
+
chr = getchr
|
217
|
+
case chr
|
218
|
+
when '?' # Show helptext in right window
|
219
|
+
@w_d.fill
|
220
|
+
@w_d.text = @help
|
221
|
+
@w_d.write
|
222
|
+
@w_d.update = false
|
223
|
+
when 'UP'
|
224
|
+
@active.index = @active.index <= 0 ? @active.list.size - 1 : @active.index - 1
|
225
|
+
when 'DOWN'
|
226
|
+
@active.index = @active.index >= @active.list.size - 1 ? 0 : @active.index + 1
|
227
|
+
when 'PgUP'
|
228
|
+
@active.index -= @active.maxy - 2
|
229
|
+
@active.index = 0 if @active.index < 0
|
230
|
+
when 'PgDOWN'
|
231
|
+
@active.index += @active.maxy - 2
|
232
|
+
@active.index = @active.list.size - 1 if @active.index > @active.list.size
|
233
|
+
when 'HOME'
|
234
|
+
@active.index = 0
|
235
|
+
when 'END'
|
236
|
+
@active.index = @active.list.size - 1
|
237
|
+
when 'TAB'
|
238
|
+
case @active
|
239
|
+
when @w_i
|
240
|
+
@active = @w_g
|
241
|
+
when @w_g
|
242
|
+
@active = @w_m
|
243
|
+
when @w_m
|
244
|
+
@active = @w_n
|
245
|
+
when @w_n
|
246
|
+
@active = @w_i
|
247
|
+
end
|
248
|
+
when 'S-TAB'
|
249
|
+
case @active
|
250
|
+
when @w_i
|
251
|
+
@active = @w_n
|
252
|
+
when @w_n
|
253
|
+
@active = @w_m
|
254
|
+
when @w_m
|
255
|
+
@active = @w_g
|
256
|
+
when @w_g
|
257
|
+
@active = @w_i
|
258
|
+
end
|
259
|
+
when 'I'
|
260
|
+
w_b("Loading IMDB data...")
|
261
|
+
loadimdb
|
262
|
+
when 'm'
|
263
|
+
@movies = true
|
264
|
+
when 's'
|
265
|
+
@movies = false
|
266
|
+
when 'r'
|
267
|
+
r = w_b_getstr(" Set MINimum Rating: ", "")
|
268
|
+
@rating = r.to_f unless r == ""
|
269
|
+
when 'y'
|
270
|
+
y = w_b_getstr(" Set MINimum Year: ", "")
|
271
|
+
@yearMin = y.to_i unless y == ""
|
272
|
+
when 'Y'
|
273
|
+
y = w_b_getstr(" Set MAXimum Year: ", "")
|
274
|
+
@yearMax = y.to_i unless y == ""
|
275
|
+
when '/'
|
276
|
+
@search = w_b_getstr(" Search for title: ", "")
|
277
|
+
when '\\'
|
278
|
+
@search = ''
|
279
|
+
when 'G'
|
280
|
+
@genY = @genres.map(&:clone)
|
281
|
+
@genN = []
|
282
|
+
when '+'
|
283
|
+
case @active
|
284
|
+
when @w_i
|
285
|
+
@myY.push(@active.list[@active.index])
|
286
|
+
@active.index = 0 if @active.index > @active.list.size - 2
|
287
|
+
when @w_g
|
288
|
+
@genY.push(@active.list[@active.index]) unless @genN.include?(@active.list[@active.index])
|
289
|
+
@genN.delete(@active.list[@active.index])
|
290
|
+
@active.index = @active.index >= @active.list.size - 1 ? 0 : @active.index + 1
|
291
|
+
when @w_m
|
292
|
+
@myN.delete(@active.list[@active.index])
|
293
|
+
when @w_n
|
294
|
+
@myY.push(@active.list[@active.index])
|
295
|
+
@myN.delete(@active.list[@active.index])
|
296
|
+
@active.index -= 1 if @active.index == @active.list.size
|
297
|
+
end
|
298
|
+
when '-'
|
299
|
+
case @active
|
300
|
+
when @w_i
|
301
|
+
@myN.push(@active.list[@active.index])
|
302
|
+
@active.index = 0 if @active.index > @active.list.size - 2
|
303
|
+
when @w_g
|
304
|
+
@genN.push(@active.list[@active.index]) unless @genY.include?(@active.list[@active.index])
|
305
|
+
@genY.delete(@active.list[@active.index])
|
306
|
+
@active.index = @active.index >= @active.list.size - 1 ? 0 : @active.index + 1
|
307
|
+
when @w_m
|
308
|
+
@myY.delete(@active.list[@active.index])
|
309
|
+
@active.index -= 1 if @active.index == @active.list.size
|
310
|
+
when @w_n
|
311
|
+
@myN.delete(@active.list[@active.index])
|
312
|
+
@active.index -= 1 if @active.index == @active.list.size
|
313
|
+
end
|
314
|
+
when 'd'
|
315
|
+
@w_d.fill
|
316
|
+
w_d(1)
|
317
|
+
@w_d.update = false
|
318
|
+
when 'D'
|
319
|
+
@w_d.fill
|
320
|
+
w_d(2)
|
321
|
+
@w_d.update = false
|
322
|
+
when 'R' # Refresh all windows
|
323
|
+
@break = true
|
324
|
+
when '@' # Enter "Ruby debug"
|
325
|
+
cmd = w_b_getstr("◆ ", "")
|
326
|
+
begin
|
327
|
+
@w_b.text = eval(cmd)
|
328
|
+
@w_b.fill
|
329
|
+
@w_b.write
|
330
|
+
rescue StandardError => e
|
331
|
+
w_b("Error: #{e.inspect}")
|
332
|
+
end
|
333
|
+
@w_b.update = false
|
334
|
+
when 'w'
|
335
|
+
saveconf
|
336
|
+
when 'W'
|
337
|
+
saveimdb
|
338
|
+
when 'q' # Exit
|
339
|
+
saveconf
|
340
|
+
exit 0
|
341
|
+
when 'Q' # EXIT
|
342
|
+
exit 0
|
343
|
+
end
|
344
|
+
end
|
345
|
+
def getimdb(url)
|
346
|
+
api_url = "https://www.page2api.com/api/v1/scrape"
|
347
|
+
payload = {
|
348
|
+
api_key: @imdbkey,
|
349
|
+
batch: {
|
350
|
+
urls: url,
|
351
|
+
concurrency: 1,
|
352
|
+
merge_results: true
|
353
|
+
},
|
354
|
+
parse: {
|
355
|
+
movies: [
|
356
|
+
{
|
357
|
+
title: ".lister-item-header >> text",
|
358
|
+
url: ".lister-item-header a >> href",
|
359
|
+
id: ".userRatingValue >> data-tconst",
|
360
|
+
year: ".lister-item-year >> text",
|
361
|
+
genre: ".genre >> text",
|
362
|
+
votes: "[name=nv] >> text",
|
363
|
+
rating: ".ratings-imdb-rating >> data-value",
|
364
|
+
_parent: ".lister-item",
|
365
|
+
runtime: ".runtime >> text",
|
366
|
+
certificate: ".certificate >> text"
|
367
|
+
}
|
368
|
+
]
|
369
|
+
},
|
370
|
+
datacenter_proxy: "us"
|
371
|
+
}
|
372
|
+
response = RestClient::Request.execute(
|
373
|
+
method: :post,
|
374
|
+
payload: payload.to_json,
|
375
|
+
url: api_url,
|
376
|
+
headers: { "Content-type" => "application/json" },
|
377
|
+
).body
|
378
|
+
res = JSON.parse(response)
|
379
|
+
imdb = []
|
380
|
+
res["result"]["movies"].each {|m| imdb.push [m["title"].sub(/^\d+\. /, ''), m["rating"].to_f, m["year"].delete("^0-9").to_i, m["genre"], m["id"]]}
|
381
|
+
imdb
|
382
|
+
end
|
383
|
+
def loadimdb
|
384
|
+
@imdbmovies = getimdb(@urlmovies)
|
385
|
+
@imdbseries = getimdb(@urlseries)
|
386
|
+
@new = true
|
387
|
+
end
|
388
|
+
def imdbmovies
|
389
|
+
if @search == ''
|
390
|
+
@imdbsel = @imdbmovies.map{|m| m if (m[1] >= @rating) and ((@yearMin..@yearMax) === m[2])}
|
391
|
+
else
|
392
|
+
@imdbsel = @imdbmovies.map{|m| m if (m[0] =~ /#{@search}/)}
|
393
|
+
end
|
394
|
+
@imdbsel.select! do |i|
|
395
|
+
ig = i[3].split(", ") unless i == nil
|
396
|
+
(ig & @genY).any? unless ig == nil
|
397
|
+
end
|
398
|
+
@imdbsel.select! {|i| (i[3].split(", ") & @genN).empty?}
|
399
|
+
@imdbsel = @imdbsel - @myMN - @myMY
|
400
|
+
@myMYsel = @myMY - @myMN
|
401
|
+
[@imdbsel, @myMYsel, @myMY, @myMN, @genY, @genN].each do |arr|
|
402
|
+
arr.uniq!
|
403
|
+
arr.compact!
|
404
|
+
arr.sort_by! {|m| m[0]}
|
405
|
+
end
|
406
|
+
@w_i.list = @imdbsel
|
407
|
+
@w_i.index = 0 if @w_i.index > @w_i.list.size - 2
|
408
|
+
@w_m.list = @myMYsel
|
409
|
+
@w_n.list = @myMN
|
410
|
+
@myN = @myMN
|
411
|
+
@myY = @myMY
|
412
|
+
end
|
413
|
+
def imdbseries
|
414
|
+
if @search == ''
|
415
|
+
@imdbsel = @imdbseries.map{|m| m if (m[1] > @rating) and ((@yearMin..@yearMax) === m[2])}
|
416
|
+
else
|
417
|
+
@imdbsel = @imdbseries.map{|m| m if (m[0] =~ /#{@search}/)}
|
418
|
+
end
|
419
|
+
@imdbsel.select! do |i|
|
420
|
+
ig = i[3].split(", ") unless i == nil
|
421
|
+
(ig & @genY).any? unless ig == nil
|
422
|
+
end
|
423
|
+
@imdbsel.select! {|i| (i[3].split(", ") & @genN).empty?}
|
424
|
+
@imdbsel = @imdbsel - @mySN - @mySY
|
425
|
+
@mySYsel = @mySY - @mySN
|
426
|
+
[@imdbsel, @mySYsel, @mySY, @mySN, @genY, @genN].each do |arr|
|
427
|
+
arr.uniq!
|
428
|
+
arr.compact!
|
429
|
+
arr.sort_by! {|m| m[0]}
|
430
|
+
end
|
431
|
+
@w_i.list = @imdbsel
|
432
|
+
@w_i.index = 0 if @w_i.index > @w_i.list.size - 2
|
433
|
+
@w_m.list = @mySYsel
|
434
|
+
@w_n.list = @mySN
|
435
|
+
@myN = @mySN
|
436
|
+
@myY = @mySY
|
437
|
+
end
|
438
|
+
def getomdb(id)
|
439
|
+
@urldetails = "http://www.omdbapi.com/?apikey=#{@omdbkey}&i=#{id}"
|
440
|
+
details = Net::HTTP.get(URI(@urldetails))
|
441
|
+
det = JSON.parse(details)
|
442
|
+
return det
|
443
|
+
end
|
444
|
+
def getstreaming(id) # Returns array of outlets
|
445
|
+
url = URI("https://streaming-availability.p.rapidapi.com/v2/get/basic?country=#{@country}&imdb_id=#{id}&output_language=#{@lang}")
|
446
|
+
http = Net::HTTP.new(url.host, url.port)
|
447
|
+
http.use_ssl = true
|
448
|
+
request = Net::HTTP::Get.new(url)
|
449
|
+
request["X-RapidAPI-Key"] = @streamkey
|
450
|
+
request["X-RapidAPI-Host"] = 'streaming-availability.p.rapidapi.com'
|
451
|
+
response = http.request(request)
|
452
|
+
res = JSON.parse(response.read_body)
|
453
|
+
outlets = []
|
454
|
+
begin
|
455
|
+
res["result"]["streamingInfo"][@country].each{|k,v| outlets.push(k)}
|
456
|
+
return outlets
|
457
|
+
rescue
|
458
|
+
return ["No outlet info"]
|
459
|
+
end
|
460
|
+
end
|
461
|
+
def saveconf
|
462
|
+
if File.exist?(Dir.home+'/.imdb.conf')
|
463
|
+
conf = File.read(Dir.home+'/.imdb.conf')
|
464
|
+
else
|
465
|
+
conf = ""
|
466
|
+
end
|
467
|
+
conf.gsub!(/^@rating.*\n/, "")
|
468
|
+
conf += "@rating = #{@rating}\n"
|
469
|
+
conf.gsub!(/^@yearMin.*\n/, "")
|
470
|
+
conf += "@yearMin = #{@yearMin}\n"
|
471
|
+
conf.gsub!(/^@yearMax.*\n/, "")
|
472
|
+
conf += "@yearMax = #{@yearMax}\n"
|
473
|
+
conf.gsub!(/^@genY.*\n/, "")
|
474
|
+
conf += "@genY = #{@genY}\n"
|
475
|
+
conf.gsub!(/^@genN.*\n/, "")
|
476
|
+
conf += "@genN = #{@genN}\n"
|
477
|
+
conf.gsub!(/^@myMY.*\n/, "")
|
478
|
+
conf += "@myMY = #{@myMY}\n"
|
479
|
+
conf.gsub!(/^@myMN.*\n/, "")
|
480
|
+
conf += "@myMN = #{@myMN}\n"
|
481
|
+
conf.gsub!(/^@mySY.*\n/, "")
|
482
|
+
conf += "@mySY = #{@mySY}\n"
|
483
|
+
conf.gsub!(/^@mySN.*\n/, "")
|
484
|
+
conf += "@mySN = #{@mySN}\n"
|
485
|
+
w_b("Configuration written to .imdb.conf")
|
486
|
+
File.write(Dir.home+'/.imdb.conf', conf)
|
487
|
+
end
|
488
|
+
def saveimdb
|
489
|
+
if File.exist?(Dir.home+'/.imdb')
|
490
|
+
data = File.read(Dir.home+'/.imdb')
|
491
|
+
else
|
492
|
+
data = ""
|
493
|
+
end
|
494
|
+
data.gsub!(/^@imdbmovies.*\n/, "")
|
495
|
+
data += "@imdbmovies = #{@imdbmovies}\n"
|
496
|
+
data.gsub!(/^@imdbseries.*\n/, "")
|
497
|
+
data += "@imdbseries = #{@imdbseries}\n"
|
498
|
+
w_b("IMDB data written to .imdb")
|
499
|
+
File.write(Dir.home+'/.imdb', data)
|
500
|
+
end
|
501
|
+
|
502
|
+
# BASIC WINDOW FUNCTIONS
|
503
|
+
def w_t # SHOW INFO IN @w_t
|
504
|
+
@movies ? @w_t.text = " MOVIES :: " : @w_t.text = " SERIES :: "
|
505
|
+
@w_t.text += "Rating MIN: #{@rating} - Year MIN: #{@yearMin} - Year MAX: #{@yearMax} :: Selection = #{@imdbsel.size}"
|
506
|
+
@w_t.write
|
507
|
+
end
|
508
|
+
def w_list(win) # LIST IN WINDOW
|
509
|
+
win == @active ? win.bg = WAbg : win.bg = WIbg
|
510
|
+
win.fill
|
511
|
+
ix = 0
|
512
|
+
ix = win.index - win.maxy/2 if win.index > win.maxy/2 and win.list.size > win.maxy - 1
|
513
|
+
while ix < win.list.size and ix < win.maxy do
|
514
|
+
str = win.list[ix][0]
|
515
|
+
str = win.list[ix] if win == @w_g
|
516
|
+
if ix == win.index and win == @active
|
517
|
+
win.text = "→ "
|
518
|
+
win.write
|
519
|
+
else
|
520
|
+
win.attr = 0
|
521
|
+
win.text = " "
|
522
|
+
win.write
|
523
|
+
end
|
524
|
+
if win == @w_g
|
525
|
+
if @genY.include?(str)
|
526
|
+
win.text = "+"
|
527
|
+
win.fg = WgYfg
|
528
|
+
win.attr = Curses::A_BOLD
|
529
|
+
win.write
|
530
|
+
elsif @genN.include?(str)
|
531
|
+
win.text = "-"
|
532
|
+
win.fg = WgNfg
|
533
|
+
win.write
|
534
|
+
else
|
535
|
+
win.text = " "
|
536
|
+
win.write
|
537
|
+
win.fg = Wgfg
|
538
|
+
end
|
539
|
+
end
|
540
|
+
win.attr = win.attr | Curses::A_UNDERLINE if ix == win.index
|
541
|
+
str = str[0..(win.maxx - 6)] + "…" if str.length > win.maxx - 4
|
542
|
+
win.text = str
|
543
|
+
win.write
|
544
|
+
win.attr = 0
|
545
|
+
win.text = " " * (win.maxx - str.length - 2)
|
546
|
+
win.text = win.text[0..-2] if win == @w_g
|
547
|
+
win.write
|
548
|
+
ix += 1
|
549
|
+
end
|
550
|
+
if win.index > win.maxy/2
|
551
|
+
win.setpos(0, win.maxx - 1)
|
552
|
+
win.text = "∆"
|
553
|
+
fg0 = win.fg
|
554
|
+
win.fg = Mark
|
555
|
+
win.write
|
556
|
+
win.fg = fg0
|
557
|
+
end
|
558
|
+
if win.list.length > win.maxy - 1 and win.list.length > win.index + win.maxy/2 - 1
|
559
|
+
win.setpos(win.maxy - 1, win.maxx - 1)
|
560
|
+
win.text = "∇"
|
561
|
+
fg0 = win.fg
|
562
|
+
win.fg = Mark
|
563
|
+
win.write
|
564
|
+
win.fg = fg0
|
565
|
+
end
|
566
|
+
end
|
567
|
+
def w_d(ext = 0) # SHOW INFO IN @w_d and @w_p
|
568
|
+
return if @active == @w_g
|
569
|
+
list = @active.list
|
570
|
+
return if list.empty? # Skip if list is empty
|
571
|
+
id = list[@active.index][4]
|
572
|
+
@w_d.text = "#{list[@active.index][0]}\n\n"
|
573
|
+
@w_d.attr = Curses::A_BOLD
|
574
|
+
@w_d.write
|
575
|
+
@w_d.attr = 0
|
576
|
+
@w_d.text += "Rating: " + list[@active.index][1].to_s.ljust(14) + "Genres: #{list[@active.index][3]}\n"
|
577
|
+
@w_d.write
|
578
|
+
return unless ext > 0 # Skip if no details are to be displayed
|
579
|
+
det = getomdb(id)
|
580
|
+
otl = getstreaming(id)
|
581
|
+
@w_d.text = "Rated: " + det["Rated"].ljust(14) + "Runtime: #{det["Runtime"]}"
|
582
|
+
@w_d.text += " (#{det["totalSeasons"]})" unless det["totalSeasons"] == nil
|
583
|
+
@w_d.text += "\n"
|
584
|
+
@w_d.text += "Released: " + det["Released"].ljust(14) + "(#{id})\n\n"
|
585
|
+
width = Curses.cols - 104
|
586
|
+
@w_d.text += det["Plot"].gsub!(/(.{1,#{width}})( +|$\n?)|(.{1,#{width}})/, "\\1\\3\n")
|
587
|
+
@w_d.text += "\n"
|
588
|
+
@w_d.text += "Awards: " + det["Awards"] + "\n"
|
589
|
+
@w_d.text += "Director: " + det["Director"] + "\n"
|
590
|
+
@w_d.text += "Actors: " + det["Actors"] + "\n\n"
|
591
|
+
@w_d.text += "Metascore: " + det["Metascore"] + "\n\n"
|
592
|
+
outlets = ""
|
593
|
+
@w_d.write
|
594
|
+
# Display the poster
|
595
|
+
poster = det["Poster"]
|
596
|
+
`curl -s "#{poster}" > /tmp/imdb.jpg`
|
597
|
+
image = "/tmp/imdb.jpg"
|
598
|
+
begin
|
599
|
+
terminfo = `xwininfo -id $(xdotool getactivewindow)`
|
600
|
+
term_w = terminfo.match(/Width: (\d+)/)[1].to_i
|
601
|
+
term_h = terminfo.match(/Height: (\d+)/)[1].to_i
|
602
|
+
char_w = term_w / Curses.cols
|
603
|
+
char_h = term_h / Curses.lines
|
604
|
+
img_x = char_w * 105
|
605
|
+
img_y = char_h * (Curses.lines / 2 + 2)
|
606
|
+
img_max_w = char_w * (Curses.cols - width - 2)
|
607
|
+
img_max_h = char_h * (@w_d.maxy - 2)
|
608
|
+
# Clear previous images
|
609
|
+
`echo "6;#{img_x};#{img_y};#{img_max_w+2};#{img_max_h+2};\n4;\n3;" | #{@w3mimgdisplay} 2>/dev/null`
|
610
|
+
img_w,img_h = `identify -format "%[fx:w]x%[fx:h]" #{image} 2>/dev/null`.split('x')
|
611
|
+
img_w = img_w.to_i
|
612
|
+
img_h = img_h.to_i
|
613
|
+
if img_w > img_max_w
|
614
|
+
img_h = img_h * img_max_w / img_w
|
615
|
+
img_w = img_max_w
|
616
|
+
end
|
617
|
+
if img_h > img_max_h
|
618
|
+
img_w = img_w * img_max_h / img_h
|
619
|
+
img_h = img_max_h
|
620
|
+
end
|
621
|
+
`echo "0;1;#{img_x};#{img_y};#{img_w};#{img_h};;;;;\"#{image}\"\n4;\n3;" | #{@w3mimgdisplay} 2>/dev/null`
|
622
|
+
rescue
|
623
|
+
w_b("Error showing image")
|
624
|
+
end
|
625
|
+
return unless ext > 1 # Skip if no outlets are to be displayed
|
626
|
+
otl.each{|o| outlets += "#{o} "}
|
627
|
+
@w_d.text += "Outlets: " + outlets
|
628
|
+
@w_d.write
|
629
|
+
end
|
630
|
+
def imageclear
|
631
|
+
begin
|
632
|
+
terminfo = `xwininfo -id $(xdotool getactivewindow)`
|
633
|
+
term_w = terminfo.match(/Width: (\d+)/)[1].to_i
|
634
|
+
term_h = terminfo.match(/Height: (\d+)/)[1].to_i
|
635
|
+
char_w = term_w / Curses.cols
|
636
|
+
char_h = term_h / Curses.lines
|
637
|
+
img_x = char_w * 105
|
638
|
+
img_y = char_h * (Curses.lines / 2 + 2)
|
639
|
+
img_max_w = char_w * (Curses.cols - (Curses.cols - 104) - 2)
|
640
|
+
img_max_h = char_h * (@w_d.maxy - 2)
|
641
|
+
`echo "6;#{img_x};#{img_y};#{img_max_w+2};#{img_max_h+2};\n4;\n3;" | #{@w3mimgdisplay} 2>/dev/null`
|
642
|
+
rescue
|
643
|
+
w_b("Error clearing image")
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
# BOTTOM WINDOW FUNCTIONS
|
648
|
+
def w_b(info) # SHOW INFO IN @W_B
|
649
|
+
@w_b.clr
|
650
|
+
info = "Choose window: i=IMDB list (+/- to add/remove from My list), g=Genres (+/- to add/remove), m=My list. " if info == nil
|
651
|
+
info = info[1..(@w_b.maxx - 3)] + "…" if info.length + 3 > @w_b.maxx
|
652
|
+
info += " " * (@w_b.maxx - info.length) if info.length < @w_b.maxx
|
653
|
+
@w_b.text = info
|
654
|
+
@w_b.write
|
655
|
+
@w_b.update = false
|
656
|
+
end
|
657
|
+
def w_b_getstr(pretext, text) # A SIMPLE READLINE-LIKE ROUTINE
|
658
|
+
Curses.curs_set(1)
|
659
|
+
Curses.echo
|
660
|
+
stk = 0
|
661
|
+
pos = text.length
|
662
|
+
chr = ""
|
663
|
+
while chr != "ENTER"
|
664
|
+
@w_b.setpos(0,0)
|
665
|
+
@w_b.text = pretext + text
|
666
|
+
@w_b.text += " " * (@w_b.maxx - text.length) if text.length < @w_b.maxx
|
667
|
+
@w_b.write
|
668
|
+
@w_b.setpos(0,pretext.length + pos)
|
669
|
+
@w_b.refresh
|
670
|
+
chr = getchr
|
671
|
+
case chr
|
672
|
+
when 'C-C', 'C-G'
|
673
|
+
return ""
|
674
|
+
when 'RIGHT'
|
675
|
+
pos += 1 unless pos > text.length
|
676
|
+
when 'LEFT'
|
677
|
+
pos -= 1 unless pos == 0
|
678
|
+
when 'HOME'
|
679
|
+
pos = 0
|
680
|
+
when 'END'
|
681
|
+
pos = text.length
|
682
|
+
when 'DEL'
|
683
|
+
text[pos] = ""
|
684
|
+
when 'BACK'
|
685
|
+
unless pos == 0
|
686
|
+
pos -= 1
|
687
|
+
text[pos] = ""
|
688
|
+
end
|
689
|
+
when 'LDEL'
|
690
|
+
text = ""
|
691
|
+
pos = 0
|
692
|
+
when /^.$/
|
693
|
+
text.insert(pos,chr)
|
694
|
+
pos += 1
|
695
|
+
end
|
696
|
+
end
|
697
|
+
Curses.curs_set(0)
|
698
|
+
Curses.noecho
|
699
|
+
return text
|
700
|
+
end
|
701
|
+
|
702
|
+
# BEFORE WE START
|
703
|
+
begin
|
704
|
+
if File.exist?(Dir.home+'/.imdb.conf')
|
705
|
+
load(Dir.home+'/.imdb.conf')
|
706
|
+
else
|
707
|
+
firstrun
|
708
|
+
end
|
709
|
+
if File.exist?(Dir.home+'/.imdb')
|
710
|
+
load(Dir.home+'/.imdb')
|
711
|
+
else
|
712
|
+
puts "Loading IMDB data... (this may take som time, go get some coffee)"
|
713
|
+
loadimdb
|
714
|
+
saveimdb
|
715
|
+
end
|
716
|
+
## Curses setup
|
717
|
+
Curses.init_screen
|
718
|
+
Curses.start_color
|
719
|
+
Curses.curs_set(0)
|
720
|
+
Curses.noecho
|
721
|
+
Curses.cbreak
|
722
|
+
Curses.stdscr.keypad = true
|
723
|
+
end
|
724
|
+
|
725
|
+
# MAIN PROGRAM
|
726
|
+
loop do # OUTER LOOP - (catching refreshes via 'r')
|
727
|
+
@break = false # Initialize @break variable (set if user hits 'r')
|
728
|
+
begin # Create the windows/panels
|
729
|
+
Curses.stdscr.bg = 234 # Use for borders
|
730
|
+
Curses.stdscr.fill
|
731
|
+
maxx = Curses.cols
|
732
|
+
maxy = Curses.lines
|
733
|
+
# Curses::Window.new( h, w, y, x )
|
734
|
+
@w_t = Curses::Window.new( 1, maxx, 0, 0 )
|
735
|
+
@w_i = Curses::Window.new( maxy-2, 40, 1, 0 )
|
736
|
+
@w_g = Curses::Window.new( maxy-2, 20, 1, 41 )
|
737
|
+
@w_m = Curses::Window.new( maxy/2-1, 40, 1, 62 )
|
738
|
+
@w_n = Curses::Window.new( maxy/2-1, 40, maxy/2+1, 62 )
|
739
|
+
@w_d = Curses::Window.new( maxy/2-1, maxx-103, 1, 103 )
|
740
|
+
@w_p = Curses::Window.new( maxy/2-1, maxx-103, maxy/2+1, 103 )
|
741
|
+
@w_b = Curses::Window.new( 1, maxx, maxy-1, 0 )
|
742
|
+
@w_i.fg, @w_i.bg = Wifg, WIbg
|
743
|
+
@w_g.fg, @w_g.bg = Wgfg, WIbg
|
744
|
+
@w_m.fg, @w_m.bg = Wmfg, WIbg
|
745
|
+
@w_n.fg, @w_n.bg = Wnfg, WIbg
|
746
|
+
@w_d.fg, @w_d.bg = Wdfg, WIbg
|
747
|
+
@w_p.fg, @w_p.bg = Wpfg, WIbg
|
748
|
+
@w_b.fg, @w_b.bg = Wbfg, Wbbg
|
749
|
+
[@w_i, @w_g, @w_m, @w_n].each{|w| w.index = 0}
|
750
|
+
@w_b.fill
|
751
|
+
@w_g.list = @genres
|
752
|
+
@w_b.update = true
|
753
|
+
@w_d.update = true
|
754
|
+
@active = @w_i
|
755
|
+
loop do # INNER, CORE LOOP
|
756
|
+
if @movies
|
757
|
+
imdbmovies
|
758
|
+
@w_t.fg = WtMfg
|
759
|
+
@search == '' ? @w_t.bg = WtMbg : @w_t.bg = WtMbgS
|
760
|
+
@w_t.attr = Curses::A_BOLD
|
761
|
+
else
|
762
|
+
imdbseries
|
763
|
+
@w_t.fg = WtSfg
|
764
|
+
@search == '' ? @w_t.bg = WtSbg : @w_t.bg = WtSbgS
|
765
|
+
@w_t.attr = Curses::A_BOLD
|
766
|
+
end
|
767
|
+
@w_t.fill; @w_i.fill; @w_g.fill; @w_m.fill; @w_n.fill; @w_p.fill
|
768
|
+
w_t; w_list(@w_i); w_list(@w_g); w_list(@w_m); w_list(@w_n)
|
769
|
+
if @w_d.update
|
770
|
+
@w_d.fill
|
771
|
+
w_d
|
772
|
+
end
|
773
|
+
imageclear if @w_d.update
|
774
|
+
@w_d.update = true
|
775
|
+
w_b(nil) if @w_b.update
|
776
|
+
@w_b.update = true
|
777
|
+
getkey # Get key from user
|
778
|
+
break if @break # Break to outer loop, redrawing windows, if user hit 'r'
|
779
|
+
break if Curses.cols != maxx or Curses.lines != maxy # break on terminal resize
|
780
|
+
end
|
781
|
+
ensure # On exit: clear image, close curses
|
782
|
+
image_show("clear")
|
783
|
+
close_screen
|
784
|
+
end
|
785
|
+
end
|
786
|
+
|
787
|
+
# vim: set sw=2 sts=2 et fdm=syntax fdn=2 fcs=fold\:\ :
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: imdb-terminal
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Geir Isene
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-07-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: curses
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.3.2
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.3.2
|
33
|
+
description: Narrow down your preferences from a 1000 movies and almost 500 series.
|
34
|
+
Select a minimum IMDB rating, range of production years, genres you like and dislike
|
35
|
+
to get your preferred list. Get detailed information on movies and series and where
|
36
|
+
you can stream them. Even the movie poster in the terminal.
|
37
|
+
email: g@isene.com
|
38
|
+
executables:
|
39
|
+
- imdb
|
40
|
+
extensions: []
|
41
|
+
extra_rdoc_files: []
|
42
|
+
files:
|
43
|
+
- README.md
|
44
|
+
- bin/imdb
|
45
|
+
homepage: https://isene.com/
|
46
|
+
licenses:
|
47
|
+
- Unlicense
|
48
|
+
metadata:
|
49
|
+
source_code_uri: https://github.com/isene/imdb
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
requirements: []
|
65
|
+
rubygems_version: 3.3.5
|
66
|
+
signing_key:
|
67
|
+
specification_version: 4
|
68
|
+
summary: IMDB for the terminal
|
69
|
+
test_files: []
|