retroflix 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9d92944f606f69c813af81701e7d52e820c0dbb9
4
+ data.tar.gz: 94dc77c7a5c6cbe88cb0b1f0c747e656b438a325
5
+ SHA512:
6
+ metadata.gz: eaf6241347ddbcda5353d3e81e3ced4185469de262e848ce8962ddc2497cfd10a23bfc5b253452dc3b961042d0e5dccb22d7d6c80672fa9b89493831e76a58b5
7
+ data.tar.gz: 68e930eb461522a5ae63f1b35372cbe81c3d33dae6aa252f5dc0193b13273797700468e831b5106754cdbcbec3ee7fbf142319bc54f28f1cd32ea08f1dda5882
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2017 Mohammed Morsi
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,50 @@
1
+ # RetroFlix - Web based retro game downloader & library manager
2
+
3
+ **RetroFlix** is a frontend to download and manage legacy & retro applications and launch them using their target emulators.
4
+
5
+ Currently only one game db and a handfull of systems are supported, but more may be added at some point, depending on interest.
6
+
7
+ **Important**: This software should only be used for *Legal* purposes, inorder to retrieve copies
8
+ of applications which the user already owns or otherwise has a legitimate license to / rights to use.
9
+
10
+ Currently the following sites & systems are supported:
11
+
12
+ * [emuparadise](https://www.emuparadise.me/)
13
+ * N64
14
+ * SNES
15
+ * NES
16
+ * Sega Genesis / Master System
17
+
18
+ ## Install
19
+
20
+ RetroFlix is built as a [Sinatra](http://www.sinatrarb.com/) web service.
21
+
22
+ To use [install Ruby](https://www.ruby-lang.org/en/) and install the following gems:
23
+
24
+ ```$ gem install sinatra rubyzip curb nokogiri thin```
25
+
26
+
27
+ If the previous produces any errors, check the gem documentation for the corresponding
28
+ dependencies (curb requires libcurl-dev which may need to be installed seperately).
29
+
30
+ If a recent version of Ruby is not available for your system, you may want to
31
+ try out [rbenv](https://github.com/rbenv/rbenv).
32
+
33
+ Launch it with
34
+
35
+ ```$ ruby server.rb```
36
+
37
+ And navigate to [http://localhost:4567](http://localhost:4567) to download and manage games!
38
+
39
+ **Note**: Inorder to play games you will need to download the emulator for the corresponding systems. See the **emulators.rb** file for the current list of emulators used (may be configured there)
40
+
41
+
42
+ ## Screens
43
+
44
+ <img src="https://raw.githubusercontent.com/wiki/movitto/retroflix/screens/my_library.png" width="40%"/>
45
+
46
+ <img src="https://raw.githubusercontent.com/wiki/movitto/retroflix/screens/game_info.png" width="40%" />
47
+
48
+ <img src="https://raw.githubusercontent.com/wiki/movitto/retroflix/screens/game_previews.png" width="40%"/>
49
+
50
+ <img src="https://raw.githubusercontent.com/wiki/movitto/retroflix/screens/game_list.png" width="40%"/>
data/bin/rf ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/ruby
2
+ require 'daemons'
3
+
4
+ dir = File.expand_path("../..", __FILE__)
5
+ Daemons.run_proc 'server.rb' do
6
+ Dir.chdir dir
7
+ exec "./server.rb"
8
+ end
@@ -0,0 +1,22 @@
1
+ # Archive extraction utilities
2
+ # Part of RetroFlix
3
+
4
+ require 'tempfile'
5
+ require 'zip'
6
+
7
+ module RetroFlix
8
+ # Extract archive if valid format is detected, else simply return
9
+ # Currently only supports Zip files but other archive types may
10
+ # be added
11
+ def self.extract_archive(archive)
12
+ f = Tempfile.new('retroflix')
13
+ f.write archive
14
+
15
+ begin
16
+ zf = Zip::File.open(f.path).first
17
+ [zf.name, zf.get_input_stream.read]
18
+ rescue Zip::Error => e
19
+ archive
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ # Config
2
+ # Part of RetroFlix
3
+
4
+ require_relative './deep_struct'
5
+ require 'yaml'
6
+
7
+ module RetroFlix
8
+ CONFIG_FILES = [File.expand_path('~/.retroflix.yml'),
9
+ File.expand_path('../../retroflix.yml', __FILE__)]
10
+
11
+ unless CONFIG_FILES.any? { |cf| File.exists?(cf) }
12
+ raise RuntimeError, "cannot find config at #{CONFIG_FILES.join(", ")}"
13
+ end
14
+
15
+ CONFIG_FILES.each { |cf|
16
+ Config = DeepStruct.new(YAML.load_file(cf)) if File.exists?(cf) && !defined?(Config)
17
+ }
18
+ end
@@ -0,0 +1,27 @@
1
+ require 'ostruct'
2
+
3
+ class DeepStruct < OpenStruct
4
+ attr_accessor :keys
5
+
6
+ def initialize(hash=nil)
7
+ @table = {}
8
+ @hash_table = {}
9
+ @keys = []
10
+
11
+ if hash
12
+ hash.each do |k,v|
13
+ @keys << k
14
+
15
+ @table[k.to_sym] = (v.is_a?(Hash) ? self.class.new(v) : v)
16
+ @hash_table[k.to_sym] = v
17
+
18
+ new_ostruct_member(k)
19
+ end
20
+ end
21
+ end
22
+
23
+ def to_h
24
+ @hash_table
25
+ end
26
+
27
+ end
@@ -0,0 +1,23 @@
1
+ # Emulator configuration
2
+ # Part of RetroFlix
3
+
4
+ module RetroFlix
5
+ # Run on main display
6
+ DISPLAY="DISPLAY=:0"
7
+
8
+ # Return configured emulator for system
9
+ def self.emulator_for(system)
10
+ emulator = Config.emulators.send(system)
11
+ "#{Config.emulators.env} #{emulator.bin} #{emulator.flags}"
12
+ end
13
+
14
+
15
+ # fork / execs emaulator process & detaches
16
+ def self.play_game(system, game)
17
+ emulator = emulator_for(system).gsub("GAME", game_path_for(system, game))
18
+
19
+ pid = fork{ exec emulator }
20
+ Process.detach pid
21
+ nil
22
+ end
23
+ end
@@ -0,0 +1,39 @@
1
+ # Game library management routines.
2
+ # The library consists of games downloaded into the
3
+ # local games/ directory.
4
+ # Part of RetroFlix
5
+
6
+ require 'fileutils'
7
+
8
+ module RetroFlix
9
+ def self.game_dir_for(system)
10
+ "#{Config.meta.games_dir}#{system}"
11
+ end
12
+
13
+ def self.game_path_for(system, game)
14
+ dir = "#{game_dir_for(system)}/#{game}"
15
+ Dir.glob("#{dir}/*").first
16
+ end
17
+
18
+ def self.write_game(system, game, game_file, contents)
19
+ dir = game_dir_for(system)
20
+ FileUtils.mkdir_p("#{dir}/#{game}/") unless File.directory?("#{dir}/#{game}/")
21
+ File.write("#{dir}/#{game}/#{game_file}", contents)
22
+ end
23
+
24
+ def self.library_games_for(systems)
25
+ return library_games_for([systems]) unless systems.is_a?(Array)
26
+ systems.collect { |sys|
27
+ dir = "#{game_dir_for(sys)}/"
28
+ Dir.glob("#{dir}*").collect { |g| g.gsub(dir, "") }
29
+ }.flatten
30
+ end
31
+
32
+ def self.have_game?(system, game)
33
+ library_games_for(system).include?(game)
34
+ end
35
+
36
+ def self.delete_game(system, game)
37
+ FileUtils.rm_rf("#{game_dir_for(system)}/#{game}")
38
+ end
39
+ end
@@ -0,0 +1,133 @@
1
+ # Main scraper, retrieve game list, information,
2
+ # and contents from remote server.
3
+ # Part of RetroFlix
4
+
5
+ require 'curb'
6
+ require 'nokogiri'
7
+
8
+ module RetroFlix
9
+ def self.cached(id, &setter)
10
+ @cache ||= {}
11
+ @cache_timestamps ||= {}
12
+ return @cache[id] if @cache.key?(id) &&
13
+ ((Time.now - @cache_timestamps[id]) <
14
+ (60 * 60 * Config.meta.cache_time))
15
+ @cache_timestamps[id] = Time.now
16
+ @cache[id] = setter.call
17
+ end
18
+
19
+ ###
20
+
21
+ # TODO support using multiple databases (how resolve conflicts ?)
22
+ def self.db_config
23
+ @db_config ||= Config.databases[Config.databases.default]
24
+ end
25
+
26
+ def self.base_url
27
+ @base_url ||= db_config.url
28
+ end
29
+
30
+ ###
31
+
32
+ def self.systems
33
+ db_config.systems.keys
34
+ end
35
+
36
+ def self.system_url(system)
37
+ "#{base_url}/#{db_config.systems[system]}"
38
+ end
39
+
40
+ def self.valid_system?(sys)
41
+ systems.collect { |s| s.to_s }.include? sys
42
+ end
43
+
44
+ ###
45
+
46
+ # Return a hash of all games (name to relative url of info page) for given systems
47
+ def self.games_for(system)
48
+ url = system_url(system)
49
+ cached(url) do
50
+ http = Curl.get url
51
+ parser = Nokogiri::HTML(http.body_str)
52
+ games = {}
53
+ parser.xpath(db_config.paths.games).each { |node|
54
+ href = node.attribute("href").to_s
55
+ title = node.text
56
+ games[title] = href unless db_config.omit.any? { |o| title =~ Regexp.new(o) }
57
+ }
58
+
59
+ games
60
+ end
61
+ end
62
+
63
+ ###
64
+
65
+ # Return game url for given system / game
66
+ def self.game_url_for(system, game)
67
+ "#{base_url}#{games_for(system)[game]}"
68
+ end
69
+
70
+ # Return download url for given system / game
71
+ def self.download_url_for(system, game)
72
+ "#{game_url_for(system, game)}-download"
73
+ end
74
+
75
+ # Return parsed game info page
76
+ def self.game_page_for(system, game)
77
+ url = game_url_for(system, game)
78
+ cached(url) do
79
+ Nokogiri::HTML(Curl.get(url).body_str)
80
+ end
81
+ end
82
+
83
+ # Return full urls to all game screens
84
+ def self.screens_for(game_page)
85
+ game_page.xpath(db_config.paths.screens).collect { |node|
86
+ src = node.attribute("data-original").to_s
87
+ src == "" || src.nil? ? nil : "http:#{src}"
88
+ }.compact
89
+ end
90
+
91
+ # Return html content of game descriptions
92
+ def self.desc_for(game_page)
93
+ game_page.xpath(db_config.paths.desc).collect { |node|
94
+ node.inner_text.encode("UTF-8", invalid: :replace, undef: :replace)
95
+ }
96
+ end
97
+
98
+ # Return hash of all metadata (screens, desc)
99
+ # for system/game
100
+ def self.game_meta_for(system, game)
101
+ page = game_page_for(system, game)
102
+
103
+ {:screens => screens_for(page),
104
+ :descs => desc_for(page) }
105
+ end
106
+
107
+ ###
108
+
109
+ # Return link to donwload game
110
+ def self.download_link_for(system, game)
111
+ url = download_url_for(system, game)
112
+ http = Curl.get(url) { |http|
113
+ http.headers['Cookie'] = 'downloadcaptcha=1'
114
+ }
115
+
116
+ parser = Nokogiri::HTML(http.body_str)
117
+ parser.xpath(db_config.paths.dl).first.attribute('href').to_s
118
+ end
119
+
120
+ # Download game, saves to system/game file
121
+ def self.download(system, game)
122
+ dl_url = download_link_for(system, game)
123
+
124
+ url = "#{base_url}/#{dl_url}"
125
+
126
+ http = Curl.get(url) { |http|
127
+ http.headers['Referer'] = url
128
+ http.follow_location = true
129
+ }
130
+
131
+ http.body_str
132
+ end
133
+ end # module RetroFlix
@@ -0,0 +1,21 @@
1
+ # Helper script to download random game
2
+ # Part of RetroFlix
3
+
4
+ require_relative './lib/conf'
5
+ require_relative './lib/scrape'
6
+ require_relative './lib/library'
7
+
8
+ system = RetroFlix::systems.sample
9
+
10
+ games = RetroFlix.games_for(system)
11
+ game = games.to_a.sample
12
+ name = game[0]
13
+ url = game[1]
14
+
15
+ page = RetroFlix.game_page_for(system, game)
16
+ screens = RetroFlix.screens_for(page)
17
+ desc = RetroFlix.desc_for(page)
18
+
19
+ RetroFlix.write_game(system, name, name, RetroFlix.download(system, name))
20
+
21
+ puts "Downloaded #{name} for #{system}"
@@ -0,0 +1,42 @@
1
+ ---
2
+ # App Wide Metadata
3
+ meta:
4
+ games_dir: ./games/
5
+ games_per_page: 5
6
+ cache_time: 24 # hours
7
+
8
+ # Game Databases
9
+ databases:
10
+ default: emuparadise
11
+
12
+ emuparadise:
13
+ url: https://www.emuparadise.me
14
+ systems:
15
+ N64: 'Nintendo_64_ROMs/List-All-Titles/9'
16
+ NES: 'Nintendo_Entertainment_System_ROMs/List-All-Titles/13'
17
+ SNES: 'Super_Nintendo_Entertainment_System_(SNES)_ROMs/List-All-Titles/5'
18
+ Genesis: 'Sega_Genesis_-_Sega_Megadrive_ROMs/List-All-Titles/6'
19
+ paths:
20
+ games: "//a[contains(@class, 'gamelist')]"
21
+ screens: "//a[contains(@class, 'sc')]/img"
22
+ desc: "//div[contains(@id, 'game-descriptions')]/div[contains(@class, 'description-text')]"
23
+ dl: "//a[contains(@id, 'download-link')]"
24
+ omit:
25
+ - "/\\[BIOS\\].*/"
26
+ - "/\\[Program\\].*/"
27
+ - "/\\[SegaNet\\].*/"
28
+
29
+ # TODO coolroms, other dbs
30
+
31
+ # Emulator binaries
32
+ emulators:
33
+ env: "DISPLAY=:0"
34
+ NES:
35
+ bin: nestopia
36
+ flags: "\"GAME\""
37
+ SNES:
38
+ bin: znes
39
+ flags: "\"GAME\""
40
+ Genesis:
41
+ bin: gens
42
+ flags: "\"GAME\""
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/ruby
2
+ # Main sinatra application.
3
+ # Part of RetroFlix
4
+
5
+ require 'sinatra'
6
+ require_relative './lib/conf'
7
+ require_relative './lib/library'
8
+ require_relative './lib/scrape'
9
+ require_relative './lib/archive'
10
+ require_relative './lib/emulator'
11
+ require_relative './site'
12
+
13
+ # Landing page, just render layout & title
14
+ get '/' do
15
+ layout("/") { |doc|
16
+ doc.script(:type => "text/javascript",
17
+ :src => "slideshow.js")
18
+ doc.h1(:id => "main_title",
19
+ :class => "blink_me",
20
+ :style => "margin: auto;").text "RetroFlix"
21
+ 0.upto(5){ |n|
22
+ doc.img(:class => "slideshow", :src => "slideshow/#{n}.jpg")
23
+ }
24
+ }
25
+ end
26
+
27
+ # Render list of all games for a system
28
+ get "/system/:system" do
29
+ system = validate_system!(params)
30
+ games = RetroFlix.games_for(system.intern)
31
+
32
+ layout(system) { |doc|
33
+ doc.h3.text "Games for #{system}"
34
+ doc.a(:href => "/system/#{system}/1").text "Previews"
35
+
36
+ games.keys.each { |game|
37
+ doc.div {
38
+ game_path = "#{system}/#{URI.encode(game)}"
39
+ info_path = "/game/#{game_path}"
40
+ play_path = "/play/#{game_path}"
41
+ dl_path = "/download/#{game_path}"
42
+
43
+ doc.a(:href => info_path).text game
44
+
45
+ if RetroFlix.have_game?(system, game)
46
+ doc.text " - "
47
+ doc.a(:href => play_path, :class => "play_link").text "[P]"
48
+
49
+ else
50
+ doc.text " - "
51
+ doc.a(:href => dl_path, :class => "dl_link").text "[D]"
52
+ end
53
+ }
54
+ }
55
+ }
56
+ end
57
+
58
+ # Paginated list of games (w/ screenshots)
59
+ get "/system/:system/:num" do
60
+ system = validate_system!(params)
61
+ num = validate_num!(params)
62
+ games = RetroFlix.games_for(system.intern)
63
+
64
+ gpp = RetroFlix::Config.meta.games_per_page
65
+ start_index = (num-1)*gpp
66
+ end_index = [start_index+gpp-1, games.size-1].min
67
+
68
+ is_first = start_index == 0
69
+ is_last = end_index == (games.size - 1)
70
+ last_page = (games.size / gpp).to_i + 1
71
+
72
+ layout(system) { |doc|
73
+ doc.h3.text "Games for #{system}"
74
+
75
+ unless is_first
76
+ doc.a(:href => "/system/#{system}/1").text "<< "
77
+ doc.a(:href => "/system/#{system}/#{num-1}").text "< "
78
+ end
79
+
80
+ doc.a(:href => "/system/#{system}").text "All"
81
+
82
+ unless is_last
83
+ doc.a(:href => "/system/#{system}/#{num+1}").text " >"
84
+ doc.a(:href => "/system/#{system}/#{last_page}").text " >>"
85
+ end
86
+
87
+ games.keys[start_index..end_index].each { |game|
88
+ meta = RetroFlix::game_meta_for(system.intern, game)
89
+ game_path = "#{system}/#{URI.encode(game)}"
90
+
91
+ doc.div(:class => "game_preview"){
92
+ doc.a(:href => "/game/#{game_path}") {
93
+ doc.img(:src => meta[:screens].first,
94
+ :class => "preview_screen")
95
+ }
96
+
97
+ doc.div(:class => "preview_text") {
98
+ doc.a(:href => "/game/#{game_path}").text game
99
+ }
100
+
101
+ doc.div(:style => "clear: both;")
102
+ }
103
+ }
104
+ }
105
+ end
106
+
107
+ # Games downloaded locally
108
+ get "/library" do
109
+ layout("library") { |doc|
110
+ RetroFlix::systems.each_with_index { |sys, i|
111
+ doc.h3.text sys
112
+ RetroFlix.library_games_for(sys).each { |game|
113
+ game_path = "#{sys}/#{URI.encode(game)}"
114
+ info_path = "/game/#{game_path}"
115
+ play_path = "/play/#{game_path}"
116
+
117
+ doc.div { |doc|
118
+ doc.a(:href => "#{info_path}").text game
119
+ doc.text " "
120
+ doc.a(:href => "#{play_path}", :class => "play_link").text "P"
121
+ }
122
+ }
123
+
124
+ unless i == (RetroFlix::systems.size-1)
125
+ doc.br
126
+ doc.br
127
+ end
128
+ }
129
+ }
130
+ end
131
+
132
+ # Game info page
133
+ get "/game/:system/:game" do
134
+ system = validate_system!(params)
135
+ game = URI.decode(params[:game])
136
+ meta = RetroFlix::game_meta_for(system.intern, game)
137
+
138
+ layout("#{system}-game") { |doc|
139
+ doc.h3(:class => "game_title").text game
140
+ doc.text "for the "
141
+ doc.a(:href => "/system/#{system}/1").text system
142
+ doc.div {
143
+ game_path = "#{system}/#{URI.encode(game)}"
144
+ play_path = "/play/#{game_path}"
145
+ delete_path = "/delete/#{game_path}"
146
+ dl_path = "/download/#{game_path}"
147
+
148
+ if RetroFlix.have_game?(system, game)
149
+ doc.a(:href => "#{play_path}", :class => "play_link").text "[Play]"
150
+ doc.text " / "
151
+ doc.a(:href => "#{delete_path}", :class => "delete_link").text "[Delete]"
152
+
153
+ else
154
+ doc.a(:href => "#{dl_path}", :class => "dl_link").text "Download"
155
+ end
156
+ }
157
+
158
+ meta[:descs].each_with_index { |desc, i|
159
+ doc.div.text desc
160
+ doc.hr if i == 0
161
+ }
162
+
163
+ meta[:screens].each { |screen|
164
+ doc.img(:src => screen)
165
+ }
166
+ }
167
+ end
168
+
169
+ # Play specified game
170
+ get "/play/:system/:game" do
171
+ system = validate_system!(params)
172
+ game = URI.decode(params[:game])
173
+ puts "Playing #{game}"
174
+
175
+ RetroFlix::play_game(system.intern, game)
176
+
177
+ redirect "/game/#{system}/#{game}"
178
+ end
179
+
180
+ # Play specified game
181
+ get "/delete/:system/:game" do
182
+ system = validate_system!(params)
183
+ game = URI.decode(params[:game])
184
+ puts "Playing #{game}"
185
+
186
+ RetroFlix::delete_game(system.intern, game)
187
+
188
+ redirect "/game/#{system}/#{game}"
189
+ end
190
+
191
+ # Download specified game
192
+ get "/download/:system/:game" do
193
+ system = validate_system!(params)
194
+
195
+ game = URI.decode(params[:game])
196
+
197
+ puts "Downloading #{game} for #{system}"
198
+
199
+ downloaded = RetroFlix::download(system.intern, game)
200
+ name, extracted = *RetroFlix.extract_archive(downloaded)
201
+ RetroFlix.write_game(system, game, name, extracted)
202
+
203
+ redirect "/game/#{system}/#{URI.encode(game)}"
204
+ end
data/site.rb ADDED
@@ -0,0 +1,76 @@
1
+ # Sinatra website helpers
2
+ # Part of RetroFlix
3
+
4
+ # Constructs the main layout of the application,
5
+ # providing callback mechanism for main content area
6
+ def layout(section, &inner)
7
+ Nokogiri::HTML::Builder.new { |doc|
8
+ doc.html {
9
+ doc.head{
10
+ doc.title "RetroFlix"
11
+
12
+ doc.link(:rel => "stylesheet",
13
+ :type => "text/css",
14
+ :href => "/style.css")
15
+ }
16
+
17
+ doc.body {
18
+ doc.a(:href => "https://github.com/movitto/retroflix") {
19
+ doc.img(:style => "position: absolute; top: 0; right: 0; border: 0;",
20
+ :src => "https://camo.githubusercontent.com/a6677b08c955af8400f44c6298f40e7d19cc5b2d/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67",
21
+ :alt => "Fork me on GitHub",
22
+ :"data-canonical-src" => "https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png")
23
+ }
24
+
25
+ doc.div(:id => "sidebar") {
26
+ if section == "/"
27
+ doc.div.bold.text "RF"
28
+ else
29
+ doc.div {
30
+ doc.a(:href => "/").text "RF"
31
+ }
32
+ end
33
+
34
+ if section == "library"
35
+ doc.div.bold.text "My Library"
36
+ else
37
+ doc.div {
38
+ doc.a(:href => "/library").text "My Library"
39
+ }
40
+ end
41
+
42
+ RetroFlix::systems.each { |sys|
43
+ doc.div {
44
+ if section == sys.to_s
45
+ doc.div.bold.text sys
46
+ elsif section == "#{sys}-game"
47
+ doc.div.bold {
48
+ doc.a(:href => "/system/#{sys}/1").text sys
49
+ }
50
+ else
51
+ doc.a(:href => "/system/#{sys}/1").text sys
52
+ end
53
+ }
54
+ }
55
+ }
56
+
57
+ doc.div(:id => "main_content") {
58
+ inner.call(doc) if inner
59
+ }
60
+ }
61
+ }
62
+ }.to_html
63
+ end
64
+
65
+ # Validates the system param
66
+ def validate_system!(params)
67
+ system = params[:system]
68
+ raise ArgumentError if !RetroFlix.valid_system?(system)
69
+ system
70
+ end
71
+
72
+ # Validates the num param
73
+ def validate_num!(params)
74
+ num = params[:num]
75
+ Integer(num) # raises ArgumentError if invalid int
76
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: retroflix
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Mo Morsi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-06-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sinatra
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: nokogiri
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.8'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.8'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubyzip
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: curb
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.9.3
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.9.3
69
+ description: Retro Game Library Manager
70
+ email: mo@morsi.org
71
+ executables:
72
+ - rf
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - MIT-LICENSE
77
+ - README.md
78
+ - bin/rf
79
+ - lib/archive.rb
80
+ - lib/conf.rb
81
+ - lib/deep_struct.rb
82
+ - lib/emulator.rb
83
+ - lib/library.rb
84
+ - lib/scrape.rb
85
+ - random.rb
86
+ - retroflix.yml
87
+ - server.rb
88
+ - site.rb
89
+ homepage: http://github.com/movitto/retroflix
90
+ licenses:
91
+ - MIT
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.5.1
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Manage collections of Retro games using an easy web interface, launch them
113
+ right from the browser!
114
+ test_files: []
115
+ has_rdoc: