retroflix 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: