├── .coveralls.yml ├── .dockerignore ├── .gitignore ├── .ruby-gemset ├── .ruby-version ├── .travis.yml ├── CHANGELOG.md ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── Rakefile ├── app ├── assets │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ ├── images │ │ └── .keep │ ├── javascripts │ │ ├── application.js │ │ ├── carousel.js │ │ ├── notification_handler.js.coffee.erb │ │ ├── services.coffee │ │ └── skycons.js │ └── stylesheets │ │ ├── application.scss │ │ ├── carousel.scss │ │ └── custom.scss ├── controllers │ ├── application_controller.rb │ ├── concerns │ │ └── .keep │ ├── info_controller.rb │ ├── notifications_controller.rb │ ├── plex_services_controller.rb │ ├── services_controller.rb │ └── weather_controller.rb ├── helpers │ ├── api_helper.rb │ ├── application_helper.rb │ ├── notifications_helper.rb │ ├── plex_object_helper.rb │ ├── plex_service_helper.rb │ ├── plex_session_helper.rb │ ├── plex_user.rb │ ├── service_helper.rb │ └── weather_helper.rb ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── application_record.rb │ ├── concerns │ │ └── .keep │ ├── plex_object.rb │ ├── plex_recently_added.rb │ ├── plex_service.rb │ ├── plex_session.rb │ ├── server_load.rb │ ├── service.rb │ └── weather.rb └── views │ ├── info │ ├── _weatherform.html.erb │ ├── about.html.erb │ ├── configuration.html.erb │ └── new_weather.html.erb │ ├── layouts │ ├── _footer.html.erb │ ├── _header.html.erb │ ├── _navbar.html.erb │ ├── _rightbar.html.erb │ ├── _servicepanel.html.erb │ └── application.html.erb │ ├── plex_recently_addeds │ ├── _plex_recently_added.html.erb │ └── _plex_recently_added.json.jbuilder │ ├── plex_services │ ├── _now_playing.html.erb │ ├── _now_playing_navbar.html.erb │ ├── _plex_session.html.erb │ ├── _plex_session.json.jbuilder │ ├── _plexserviceform.html.erb │ ├── _recently_added.html.erb │ ├── all_plex_services.html.erb │ ├── edit.html.erb │ ├── new.html.erb │ └── show.html.erb │ ├── services │ ├── _service.html.erb │ ├── _service.json.jbuilder │ ├── _serviceform.html.erb │ ├── all_services.html.erb │ ├── choose_service_type.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ ├── index.json.jbuilder │ ├── new.html.erb │ ├── show.html.erb │ └── show.json.jbuilder │ ├── shared │ ├── _error_messages.html.erb │ └── _plex_object.json.jbuilder │ └── weathers │ ├── _weather.html.erb │ └── _weather.json.jbuilder ├── bin ├── bundle ├── rails ├── rake ├── setup ├── spring ├── update └── yarn ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── application_controller_renderer.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── geocoder.rb │ ├── inflections.rb │ ├── lograge.rb │ ├── mime_types.rb │ ├── new_framework_defaults_5_1.rb │ ├── server_config.rb │ ├── session_store.rb │ ├── simple_form.rb │ ├── simple_form_bootstrap.rb │ ├── version.rb │ └── wrap_parameters.rb ├── locales │ ├── en.yml │ └── simple_form.en.yml ├── puma.rb ├── routes.rb └── spring.rb ├── db ├── migrate │ ├── 20151123041100_create_services.rb │ ├── 20151123041932_create_plex_services.rb │ ├── 20151123042532_create_plex_objects.rb │ ├── 20151123051045_create_plex_sessions.rb │ ├── 20151124030511_add_plex_service_reference_to_plex_objects.rb │ ├── 20151124030640_add_index_to_service_flavors_for_services.rb │ ├── 20151124041208_add_polymorphic_columns_to_plex_objects.rb │ ├── 20151125020147_add_media_title_to_plex_object.rb │ ├── 20151125020347_add_session_key_to_plex_session.rb │ ├── 20151125020455_remove_plex_user_name_from_plex_objects.rb │ ├── 20151125020909_remove_columns_from_plex_objects.rb │ ├── 20151125021341_add_index_to_plex_objects.rb │ ├── 20151125021356_add_index_to_plex_sessions.rb │ ├── 20151205191615_add_added_date_to_plex_recently_added.rb │ ├── 20151206220720_add_plex_object_to_plex_service.rb │ ├── 20151208024529_remove_plex_service_from_plex_object.rb │ ├── 20151208024559_remove_plex_object_from_plex_service.rb │ ├── 20151208025103_add_plex_service_to_plex_recently_added.rb │ ├── 20151208025136_add_plex_service_to_plex_session.rb │ ├── 20151227040353_add_uuid_to_plex_recently_added.rb │ ├── 20151230044756_add_auth_successful_to_plex_service.rb │ ├── 20160121032317_rename_auth_successful_to_api_error.rb │ ├── 20160124024211_remove_username_from_plex_service.rb │ ├── 20160124024307_remove_password_from_plex_service.rb │ ├── 20160124033003_remove_api_error_from_plex_service.rb │ ├── 20160318030058_create_server_loads.rb │ ├── 20160507224352_create_weathers.rb │ ├── 20160507224801_add_address_to_weather.rb │ ├── 20160510020651_add_units_to_weather.rb │ ├── 20160513022103_change_weather_units_from_string_to_text.rb │ ├── 20160707043046_add_city_to_weather.rb │ ├── 20160707043058_add_state_to_weather.rb │ ├── 20170325204752_remove_online_status_from_service.rb │ ├── 20170329020117_add_stream_type_to_plex_session.rb │ ├── 20170404205426_remove_last_seen_from_services.rb │ └── 20170428012623_add_null_false_to_database_fields.rb ├── schema.rb └── seeds.rb ├── example.server_config.yml ├── example.windows.server_config.yml ├── lib ├── assets │ └── .keep ├── tasks │ ├── .keep │ └── version.rake └── templates │ └── erb │ └── scaffold │ └── _form.html.erb ├── public ├── 404.html ├── 422.html ├── 500.html ├── favicon.ico ├── placeholder.png └── robots.txt ├── serverSetup.sh ├── startServer.sh ├── stopServer.sh ├── test ├── controllers │ ├── .keep │ ├── info_controller_test.rb │ ├── notifications_controller_test.rb │ ├── plex_services_controller_test.rb │ ├── services_controller_test.rb │ └── weather_controller_test.rb ├── fabricators │ ├── fabrication.rb │ ├── plex_object_fabricator.rb │ ├── plex_recently_added_fabricator.rb │ ├── plex_service_fabricator.rb │ ├── plex_session_fabricator.rb │ ├── server_load_fabricator.rb │ ├── service_fabricator.rb │ └── weather_fabricator.rb ├── fixtures │ ├── .keep │ ├── JSON │ │ ├── forecast.json │ │ ├── google_location_api.json │ │ ├── plex_one_session.json │ │ ├── plex_one_session_updated_viewOffset.json │ │ ├── plex_recently_added_all.json │ │ ├── plex_recently_added_movie.json │ │ ├── plex_recently_added_tv_show.json │ │ ├── plex_two_sessions.json │ │ └── sign_in.json │ ├── images │ │ └── placeholder.png │ ├── plex404.html │ ├── plex_objects.yml │ ├── plex_recently_addeds.yml │ ├── plex_services.yml │ ├── plex_sessions.yml │ └── services.yml ├── helpers │ ├── .keep │ └── webmock_helper.rb ├── integration │ ├── .keep │ ├── behavior_test.rb │ └── site_layout_test.rb ├── integration_test_config_files │ └── example_service_test_config.yml ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── plex_object_test.rb │ ├── plex_recently_added_test.rb │ ├── plex_service_test.rb │ ├── plex_session_test.rb │ ├── server_load_test.rb │ ├── service_test.rb │ └── weather_test.rb └── test_helper.rb ├── travisSetup.sh ├── update.sh └── vendor └── assets ├── javascripts └── .keep └── stylesheets └── .keep /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .dockerignore 3 | config/secrets.yml 4 | coverage/ 5 | .bundle/ 6 | tmp/ 7 | public/images/ 8 | public/assets/ 9 | test/ 10 | log/* 11 | db/*.sqlite3 12 | server_config.yml -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | /coverage 14 | # Ignore all logfiles, tempfiles and secrets.yml 15 | /log/* 16 | !/log/.keep 17 | /tmp 18 | /config/secrets.yml 19 | server_config.yml 20 | 21 | #Ignore public/images 22 | /public/images/* 23 | /public/assets 24 | #Ignore Spring files. 25 | /spring/*.pid 26 | /.idea/ 27 | 28 | #Ignore test images 29 | /test/test_images/*.jpeg 30 | /test/test_images/*.png 31 | 32 | #Ignore service_test_config.yml to keep private safe 33 | /test/integration_test_config_files/service_test_config.yml -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | plexdashboard 2 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.4.2 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | dist: trusty 3 | sudo: false 4 | services: 5 | - redis 6 | rvm: 7 | - 2.3.0 8 | - 2.3.4 9 | - 2.4.0 10 | - 2.4.2 11 | cache: bundler 12 | 13 | script: 14 | - /bin/bash travisSetup.sh 15 | - bundle exec rake 16 | 17 | notifications: 18 | webhooks: 19 | urls: 20 | - https://webhooks.gitter.im/e/b32edaad628354a304be 21 | on_success: change # options: [always|never|change] default: always 22 | on_failure: always # options: [always|never|change] default: always 23 | on_start: never # options: [always|never|change] default: always -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Base our image on an official, minimal image of our preferred Ruby 2 | FROM ruby:2.4.1-slim 3 | 4 | ENV RAILS_ROOT /app 5 | ENV RAILS_ENV production 6 | ENV DOCKER true 7 | ENV LOG_TO_STDOUT true 8 | 9 | # Install essential Linux packages 10 | RUN apt-get update -qq \ 11 | && apt-get install -y \ 12 | bundler \ 13 | nodejs \ 14 | libsqlite3-dev \ 15 | && rm -rf /var/lib/apt/lists/* 16 | 17 | # Set our working directory inside the image 18 | RUN mkdir $RAILS_ROOT 19 | WORKDIR $RAILS_ROOT 20 | 21 | # Use the Gemfiles as Docker cache markers. Always bundle before copying app src. 22 | # (the src likely changed and we don't want to invalidate Docker's cache too early) 23 | # http://ilikestuffblog.com/2014/01/06/how-to-skip-bundle-install-when-deploying-a-rails-app-to-docker/ 24 | COPY Gemfile Gemfile 25 | 26 | COPY Gemfile.lock Gemfile.lock 27 | 28 | # Prevent bundler warnings; ensure that the bundler version executed is >= that which created Gemfile.lock 29 | RUN gem install bundler rake \ 30 | && bundle install --jobs=5 --without development test \ 31 | && bundle clean --force 32 | 33 | # Copy the Rails application into place 34 | COPY . . 35 | 36 | RUN $RAILS_ROOT/serverSetup.sh 37 | 38 | EXPOSE 3000 39 | 40 | # Define the script we want run once the container boots 41 | # Use the "exec" form of CMD so our script shuts down gracefully on SIGTERM (i.e. `docker stop`) 42 | CMD exec bundle exec puma -e production -C $RAILS_ROOT/config/puma.rb $RAILS_ROOT/config.ru 43 | 44 | VOLUME $RAILS_ROOT/db/ 45 | 46 | 47 | # Notes to remind me how to build this thing when I forget 48 | # docker build -t plex-board . 49 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem 'rails', '5.1.4' 3 | gem 'rake', '~> 12.1.0' 4 | gem 'sqlite3', '~> 1.3.13' 5 | gem 'sass-rails', '~> 5.0.6' 6 | gem 'uglifier', '~> 3.1.9' 7 | gem 'coffee-rails', '~> 4.2.1' 8 | gem 'jquery-rails', '~> 4.3.1' 9 | gem 'turbolinks', '~> 5.0.0' 10 | gem 'autoprefixer-rails', '~> 6.7.7.1' 11 | gem 'jbuilder', '~> 2.6.3' 12 | gem 'bootstrap-sass', '~> 3.3.7' 13 | gem 'sprockets-rails', '~> 3.2.0' 14 | gem 'simple_form', '~> 3.5.0' 15 | gem 'strip_attributes', '~> 1.8.0' 16 | gem 'rest-client', '~> 2.0.2' 17 | gem 'json', '~> 2.1.0' 18 | gem 'sass', '~> 3.4.23' 19 | gem 'execjs', '~> 2.7.0' 20 | gem 'parallel', '~> 1.11.1' 21 | gem 'forecast_io', '~> 2.0' 22 | gem 'geocoder', '~> 1.4.3' 23 | gem 'tzinfo', '~> 1.2.3' 24 | gem 'lograge', '~> 0.6.0' 25 | # gem 'browserlog', git: 'https://github.com/scytherswings/browserlog' 26 | 27 | group :development, :test do 28 | gem 'spring', '~> 2.0.1' 29 | gem 'parallel_tests', '~> 2.14.0' 30 | end 31 | 32 | group :development do 33 | gem 'annotate', require: false 34 | # gem 'web-console', '~> 2.0' 35 | end 36 | 37 | group :test do 38 | gem 'rails-controller-testing', '~> 1.0.1' 39 | gem 'simplecov', '~> 0.14.1' 40 | gem 'coveralls', '~> 0.8.21' 41 | gem 'minitest-reporters', '~> 1.1.14' 42 | gem 'webmock', '~> 2.3.2' 43 | gem 'vcr', '~> 3.0.3' 44 | gem 'minitest-vcr', '~> 1.4.0' 45 | gem 'faker', '~> 1.8.4' 46 | gem 'fabrication', '~> 2.16.1' 47 | end 48 | 49 | group :production do 50 | gem 'puma', '~> 3.10' 51 | end 52 | 53 | gem 'tzinfo-data' -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 scytherswings 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/app/assets/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /app/assets/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/app/assets/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /app/assets/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/app/assets/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /app/assets/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/app/assets/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require turbolinks 16 | //= require bootstrap-sprockets 17 | //= require_tree . 18 | -------------------------------------------------------------------------------- /app/assets/javascripts/carousel.js: -------------------------------------------------------------------------------- 1 | // Carousel Auto-Cycle 2 | // Interval controls how fast the carousel revolves 3 | $(document).ready(function() { 4 | $('.carousel').carousel({ 5 | interval: 8000 6 | }); 7 | }); 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/assets/javascripts/notification_handler.js.coffee.erb: -------------------------------------------------------------------------------- 1 | "<% require 'yaml' %>" 2 | # Yes, this will blow up if server_config.yml doesn't exist. Sucks to suck. RTFM 3 | source = new EventSource("<%= (YAML.load_file(Rails.root.join 'server_config.yml')['relative_root'] || '') + '/notifications' %>") 4 | 5 | ########### Service Online Status ############ 6 | 7 | source.addEventListener 'online_status', (e) -> 8 | service = $.parseJSON(e.data) 9 | # console.log(service) 10 | $('#service_' + service.id).replaceWith(service.html) 11 | 12 | # $.get service.self_uri, (data) -> 13 | # $('#service_' + service.id).replaceWith(data) 14 | # console.log(data) 15 | # return 16 | 17 | ############# Plex Now Playing ################ 18 | 19 | source.addEventListener 'plex_now_playing', (e) -> 20 | plex_now_playing_data = $.parseJSON(e.data) 21 | # console.log plex_now_playing_data 22 | # console.log "Plex - Now Playing - session #{plex_session.session_id}" 23 | # console.log $('[id^="streams_for_plex_service_"]').length && plex_now_playing_data.active_streams_html && plex_now_playing_data.active_streams_html.length 24 | 25 | if $('[id^="streams_for_plex_service_"]').length && plex_now_playing_data.active_streams_html && plex_now_playing_data.active_streams_html.length 26 | console.log "Updating Active Streams" 27 | $('#streams_for_plex_service_' + plex_now_playing_data.plex_service_id).replaceWith(plex_now_playing_data.active_streams_html) 28 | $('#streams_for_plex_service_' + plex_now_playing_data.plex_service_id).show() 29 | else if $('[id^="streams_for_plex_service_"]').length 30 | console.log "Hiding active stream" 31 | $('[id^="streams_for_plex_service_"]').hide() 32 | else if plex_now_playing_data.active_streams_html && plex_now_playing_data.active_streams_html.length 33 | console.log "Adding new active stream" 34 | $('#navbar_links').prepend('
  • ' + plex_now_playing_data.active_streams_html + '
  • ') 35 | 36 | updated_progressbar = """ 37 |
    43 | """ 44 | if $('#plex_session_' + plex_now_playing_data.session_id).length 45 | # console.log "Updating progress bar" 46 | $('#plex_progressbar_' + plex_now_playing_data.session_id).replaceWith(updated_progressbar) 47 | else 48 | # console.log "Didn't find the element \"session #{plex_session.session_id}\"" 49 | # console.log "Adding new session element.." 50 | if $("[id^=plex_recently_added_]") 51 | $("[id^=plex_recently_added_]").last().after(plex_now_playing_data.html) 52 | else if $("[id^=plex_session_]") 53 | $("[id^=plex_session_]").last().after(plex_now_playing_data.html) 54 | else 55 | $("[id^=carousel-inner]").append(plex_now_playing_data.html) 56 | 57 | #Find all the existing elements on the page and compare them to the active ids we got in the SSE 58 | 59 | #set every plex_session as stale so we can remove the sessions that aren't found in the active sessions from the server 60 | stale_sessions = $.find("[id^=plex_session_]") 61 | #iterate over known sessions 62 | if plex_now_playing_data.active_sessions 63 | for i in [0...plex_now_playing_data.active_sessions.length] 64 | #console.log "Active session is: plex_session_" + plex_session.active_sessions[i] 65 | for j in [0...stale_sessions.length] 66 | #console.log "Matching active session against: " + stale_sessions[j].id 67 | if stale_sessions[j].id == ("plex_session_" + plex_now_playing_data.active_sessions[i]) 68 | #at position i, remove one element from the array 69 | stale_sessions.splice(j, 1) 70 | #console.log "Updated stale sessions length should be one less: " + stale_sessions.length 71 | break 72 | for k in [0...stale_sessions.length] 73 | #find the elements by id and remove them from the page 74 | if !$("##{stale_sessions[k].id}").hasClass("active") && !$("##{stale_sessions[k].id}").hasClass("next") 75 | #console.log "Stale element found, removing.." 76 | console.log "Removing element " + stale_sessions[k].id 77 | $("#" + stale_sessions[k].id).remove() 78 | 79 | source.addEventListener 'plex_recently_added', (e) -> 80 | pra = $.parseJSON(e.data) 81 | if $('div[id^=plex_session_]').length < 3 82 | #console.log "There were less than 3 plex_sessions, adding plex recently added" 83 | if $('#plex_recently_added_' + pra.id).length 84 | #console.log "The PRA element was found. Not adding it again." 85 | else 86 | console.log "ID of new pra: #{pra.id}" 87 | 88 | if $("[id^=plex_session_]") 89 | $("[id^=plex_session_]").last().after(pra.html) 90 | else if $("[id^=plex_recently_added_]") 91 | $("[id^=plex_recently_added_]").last().after(pra.html) 92 | else 93 | $("[id^=carousel-inner]").append(pra.html) 94 | # else 95 | # console.log "There were more than three PlexSessions, not showing RecentlyAdded" 96 | 97 | 98 | ############# Weather ################ 99 | 100 | source.addEventListener 'weathers', (e) -> 101 | weather = $.parseJSON(e.data) 102 | console.log("Here's the weather object id we got: " + weather.id) 103 | console.log(weather) 104 | $.get weather.self_uri, (data) -> 105 | $('#weather_' + weather.id).replaceWith(data) 106 | console.log(data) 107 | return -------------------------------------------------------------------------------- /app/assets/javascripts/services.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | 5 | $.rails.allowAction = (link) -> 6 | return true unless link.attr('data-confirm') 7 | $.rails.showConfirmDialog(link) 8 | false 9 | 10 | $.rails.confirmed = (link) -> 11 | link.removeAttr('data-confirm') 12 | link.trigger('click.rails') 13 | 14 | $.rails.showConfirmDialog = (link) -> 15 | message = link.attr 'data-confirm' 16 | html = """ 17 | 31 | """ 32 | $(html).modal() 33 | $('#confirmationDialog .confirm').on 'click', -> $.rails.confirmed(link) -------------------------------------------------------------------------------- /app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | @import "bootstrap-sprockets"; 2 | @import "bootstrap"; 3 | @import "carousel"; 4 | @import "custom"; 5 | -------------------------------------------------------------------------------- /app/assets/stylesheets/carousel.scss: -------------------------------------------------------------------------------- 1 | .carousel-inner > .item > img { 2 | margin: 0 auto; 3 | } 4 | 5 | .progress-bar { 6 | -webkit-transition: width 5.0s ease !important; 7 | -moz-transition: width 5.0s ease !important; 8 | -o-transition: width 5.0s ease !important; 9 | transition: width 5.0s ease !important; 10 | } -------------------------------------------------------------------------------- /app/assets/stylesheets/custom.scss: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | } 4 | // .table th, .table td { 5 | // border-top: none !important; 6 | // } 7 | 8 | body { 9 | padding-top: 70px; /* Required padding for .navbar-fixed-top. Remove if using .navbar-static-top. Change if height of navigation changes. */ 10 | } 11 | 12 | 13 | .thumbnail img { 14 | width: 100%; 15 | } 16 | 17 | 18 | pre { 19 | background-color: #eee; 20 | padding: 10px; 21 | font-size: 11px; 22 | } 23 | 24 | #notice { 25 | color: green; 26 | } 27 | 28 | // #error_explanation { 29 | // color: red; 30 | // ul { 31 | // color: red; 32 | // margin: 0 0 30px 0; 33 | // } 34 | // } 35 | 36 | // .field_with_errors { 37 | // @extend .has-error; 38 | // // .form-control { 39 | // // color: $state-danger-text; 40 | // // } 41 | // } 42 | 43 | #error_explanation { 44 | color: #a94442; 45 | // color: #a94442; 46 | // background-color: #f0f0f0; 47 | // ul { 48 | // list-style: none; 49 | // margin: 0 0 18px 0; 50 | // } 51 | 52 | } 53 | 54 | .field_with_errors { 55 | @extend .has-error; 56 | } 57 | 58 | .h2_index_link { 59 | color: #000; 60 | } 61 | 62 | .h2_index_link:focus { 63 | color: #777; 64 | text-decoration: none; 65 | } 66 | 67 | .h2_index_link:hover { 68 | color: #777; 69 | text-decoration: none; 70 | } 71 | 72 | 73 | #wrap { 74 | min-height: 100%; 75 | } 76 | 77 | #main { 78 | overflow:auto; 79 | padding-bottom:75px; /* this needs to be bigger than footer height*/ 80 | } 81 | 82 | .footer { 83 | position: relative; 84 | margin-top: -70px; /* negative value of footer height */ 85 | height: 70px; 86 | clear:both; 87 | padding-top:20px; 88 | } 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | # Prevent CSRF attacks by raising an exception. 3 | # For APIs, you may want to use :null_session instead. 4 | protect_from_forgery with: :exception 5 | end 6 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/info_controller.rb: -------------------------------------------------------------------------------- 1 | class InfoController < ApplicationController 2 | before_action :set_sidebar_values 3 | 4 | def set_sidebar_values 5 | @services = Service.all 6 | @plex_services = PlexService.all 7 | # @weathers = Weather.all 8 | end 9 | 10 | def configuration 11 | end 12 | 13 | def about 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/controllers/notifications_controller.rb: -------------------------------------------------------------------------------- 1 | class NotificationsController < ApplicationController 2 | include ActionController::Live 3 | # How to do SSE properly: 4 | # https://github.com/rails/rails/blob/6061c540ac7880233a6e32de85cec72c20ed8778/actionpack/lib/action_controller/metal/live.rb#L23 5 | 6 | def notifications 7 | response.headers['Content-Type'] = 'text/event-stream' 8 | sse = SSE.new(response.stream, retry: 2000) 9 | i = 0 10 | begin 11 | loop do 12 | i+= 1 13 | events = Array.new 14 | @plex_services = PlexService.all 15 | @services = Service.all 16 | # @weathers = Weather.all 17 | 18 | if @plex_services.empty? && @services.empty? 19 | logger.info 'There were no PlexServices or Generic Services, sleeping for 10s.' 20 | sleep(10) 21 | end 22 | 23 | @plex_services.try(:each) do |plex_service| 24 | plex_service.update_plex_data 25 | all_active_sessions = [] 26 | plex_service.plex_sessions.try(:each) do |plex_session| 27 | if plex_session.plex_object.nil? 28 | logger.error {"PlexSession: #{plex_session.id} had a nil plex_object. Destroying."} 29 | plex_session.destroy! 30 | next 31 | end 32 | all_active_sessions << {session_id: plex_session.id, 33 | html: render_to_string(partial: 'plex_services/now_playing', 34 | formats: [:html], 35 | locals: {plex_session: plex_session, 36 | active: ''}), 37 | progress: plex_session.get_percent_done, 38 | plex_service_id: plex_service.id, 39 | active_streams_html: render_to_string(partial: 'plex_services/now_playing_navbar', 40 | formats: [:html], 41 | locals: {plex_service: plex_service}), 42 | active_sessions: PlexSession.all.ids} 43 | 44 | end 45 | if !all_active_sessions.empty? 46 | all_active_sessions.each do |active_session| 47 | events << {data: active_session, event: 'plex_now_playing'} 48 | end 49 | elsif (i % 5).zero? 50 | events << {data: [], event: 'plex_now_playing'} 51 | end 52 | if (i % 10).zero? 53 | plex_service.plex_recently_addeds.order('added_date DESC').first(5).each do |pra| 54 | data = {id: pra.id, 55 | html: render_to_string(partial: 'plex_services/recently_added', 56 | formats: [:html], 57 | locals: {plex_recently_added: pra, 58 | active: ''})} 59 | events << {data: data, event: 'plex_recently_added'} 60 | end 61 | end 62 | end 63 | 64 | @services.try(:each) do |service| 65 | if (i % 5).zero? 66 | logger.debug('Looped 5 times, sending all service statuses.') 67 | service.ping 68 | events << {data: {id: service.id, html: render_to_string(partial: 'services/service', formats: [:html], locals: {service: service})}.to_json, event: 'online_status'} 69 | else 70 | unless service.ping_for_status_change.nil? 71 | events << {data: {id: service.id, html: render_to_string(partial: 'services/service', formats: [:html], locals: {service: service})}.to_json, event: 'online_status'} 72 | end 73 | end 74 | end 75 | 76 | # @weathers.try(:each) do |weather| 77 | # weather.get_weather 78 | # events << {data: weather, event: 'weather'} 79 | # end 80 | 81 | events.each do |e| 82 | sse.write(e[:data], event: e[:event]) 83 | end 84 | sleep 2 85 | end 86 | rescue IOError 87 | logger.warn 'Stream closed: IO Error' 88 | rescue ClientDisconnected 89 | logger.warn 'Stream closed: Client Disconnect' 90 | # rescue StandardError => e 91 | # logger.error "An error occurred during the loop: #{e.message}" 92 | ensure 93 | sse.close 94 | end 95 | end 96 | end 97 | -------------------------------------------------------------------------------- /app/controllers/plex_services_controller.rb: -------------------------------------------------------------------------------- 1 | class PlexServicesController < ApplicationController 2 | before_action :set_plex_service, only: [:show, :edit, :update, :destroy] 3 | before_action :set_sidebar_values, except: [:now_playing, :recently_added, :create, :update, :destroy] 4 | 5 | def set_sidebar_values 6 | @services = Service.all 7 | @plex_services = PlexService.all 8 | # @weathers = Weather.all 9 | end 10 | 11 | def index 12 | end 13 | 14 | def edit 15 | end 16 | 17 | def show 18 | end 19 | 20 | def all_plex_services 21 | end 22 | 23 | # GET /now_playings/1 24 | # GET /now_playings/1.json 25 | def now_playing 26 | if params[:id].nil? && params[:plex_service_id] 27 | @plex_sessions = PlexService.find(params[:plex_service_id]).plex_sessions 28 | @active = '' 29 | respond_to do |format| 30 | format.html { render partial: 'plex_services/plex_session', collection: @plex_sessions } 31 | format.json { render json: @plex_sessions } 32 | end 33 | else 34 | @plex_session = PlexSession.find(params[:id]) 35 | @active = '' 36 | 37 | if params[:active] && params[:active].casecmp('true').zero? 38 | @active = 'active' 39 | end 40 | 41 | respond_to do |format| 42 | format.html { render @plex_session } 43 | format.json { render @plex_session } 44 | end 45 | end 46 | end 47 | 48 | # GET /recently_addeds/1 49 | # GET /recently_addeds/1.json 50 | def recently_added 51 | @plex_recently_added = PlexRecentlyAdded.find(params[:id]) 52 | @active = '' 53 | 54 | if params[:active] && params[:active].casecmp('true').zero? 55 | @active = 'active' 56 | end 57 | 58 | respond_to do |format| 59 | format.html { render @plex_recently_added } 60 | format.json { render @plex_recently_added } 61 | end 62 | end 63 | 64 | 65 | # need to use build_service!!!!! This article sparked my thoughts to use build 66 | # http://stackoverflow.com/questions/26458417/rails-polymorphic-posts-associations-and-form-for-in-views 67 | 68 | def new 69 | @plex_service = PlexService.new 70 | @service = @plex_service.build_service 71 | end 72 | 73 | def create 74 | @plex_service = PlexService.new(plex_service_params) 75 | 76 | respond_to do |format| 77 | if @plex_service.save 78 | format.html { redirect_to @plex_service, notice: 'Service was successfully created.' } 79 | format.json { render :show, status: :created, location: @plex_service } 80 | else 81 | format.html { render :new } 82 | format.json { render json: @plex_service.errors, status: :unprocessable_entity } 83 | end 84 | end 85 | end 86 | 87 | 88 | def update 89 | respond_to do |format| 90 | if @plex_service.update(plex_service_params) 91 | format.html { redirect_to @plex_service, notice: 'Plex Service was successfully updated.' } 92 | format.json { render :show, status: :ok, location: @plex_service } 93 | else 94 | format.html { render :edit } 95 | format.json { render json: @plex_service.errors, status: :unprocessable_entity } 96 | end 97 | end 98 | end 99 | 100 | # DELETE /services/1 101 | # DELETE /services/1.json 102 | def destroy 103 | @plex_service.destroy 104 | respond_to do |format| 105 | format.html { redirect_to root_url, notice: 'Plex Service was successfully destroyed.' } 106 | format.json { head :no_content } 107 | end 108 | end 109 | 110 | 111 | private 112 | # Use callbacks to share common setup or constraints between actions. 113 | def set_plex_service 114 | @plex_service = PlexService.find(params[:id]) 115 | end 116 | 117 | # Never trust parameters from the scary internet, only allow the white list through. 118 | # Going off of this site as a guide: http://astockwell.com/blog/2014/03/polymorphic-associations-in-rails-4-devise/ 119 | def plex_service_params 120 | params.require(:plex_service).permit(:username, :password, service_attributes: [:id, :name, :ip, :dns_name, :port, :url]) 121 | end 122 | end 123 | -------------------------------------------------------------------------------- /app/controllers/services_controller.rb: -------------------------------------------------------------------------------- 1 | class ServicesController < ApplicationController 2 | before_action :set_service, only: [:show, :edit, :update, :destroy] 3 | before_action :set_sidebar_values, except: [:online_status, :create, :update, :destroy] 4 | 5 | def set_sidebar_values 6 | @services = Service.all 7 | @plex_services = PlexService.all 8 | # @weathers = Weather.all 9 | end 10 | 11 | # GET /services 12 | # GET /services.json 13 | def index 14 | end 15 | 16 | # GET /services/:id/online_status 17 | # GET /services/:id/online_status.json 18 | def online_status 19 | @service = Service.find(params[:service_id]) 20 | 21 | respond_to do |format| 22 | format.html { render @service } 23 | format.json { render @service } 24 | end 25 | end 26 | 27 | 28 | # GET /services/1 29 | # GET /services/1.json 30 | def show 31 | end 32 | 33 | def all_services 34 | end 35 | 36 | # GET /services/new 37 | def new 38 | @service = Service.new 39 | end 40 | 41 | def choose_service_type 42 | end 43 | 44 | # GET /services/1/edit 45 | def edit 46 | end 47 | 48 | # POST /services 49 | # POST /services.json 50 | def create 51 | @service = Service.new(service_params) 52 | 53 | respond_to do |format| 54 | if @service.save 55 | format.html { redirect_to @service, notice: 'Service was successfully created.' } 56 | format.json { render :show, status: :created, location: @service } 57 | else 58 | format.html { render :new } 59 | format.json { render json: @service.errors, status: :unprocessable_entity } 60 | end 61 | end 62 | end 63 | 64 | # PATCH/PUT /services/1 65 | # PATCH/PUT /services/1.json 66 | def update 67 | respond_to do |format| 68 | if @service.update(service_params) 69 | format.html { redirect_to @service, notice: 'Service was successfully updated.' } 70 | format.json { render :show, status: :ok, location: @service } 71 | else 72 | format.html { render :edit } 73 | format.json { render json: @service.errors, status: :unprocessable_entity } 74 | end 75 | end 76 | end 77 | 78 | 79 | # DELETE /services/1 80 | # DELETE /services/1.json 81 | def destroy 82 | @service.destroy 83 | respond_to do |format| 84 | format.html { redirect_to root_url, notice: 'Service was successfully destroyed.' } 85 | format.json { head :no_content } 86 | end 87 | end 88 | 89 | private 90 | # Use callbacks to share common setup or constraints between actions. 91 | def set_service 92 | @service = Service.find(params[:id]) 93 | end 94 | 95 | # Never trust parameters from the scary internet, only allow the white list through. 96 | def service_params 97 | params.require(:service).permit(:name, :ip, :dns_name, :port, :url) 98 | end 99 | 100 | end 101 | -------------------------------------------------------------------------------- /app/controllers/weather_controller.rb: -------------------------------------------------------------------------------- 1 | class WeatherController < ApplicationController 2 | before_action :set_weather, only: [:show, :edit, :update, :destroy] 3 | 4 | def index 5 | @weathers = Weather.all 6 | end 7 | 8 | def edit 9 | 10 | end 11 | 12 | def show 13 | 14 | end 15 | 16 | def new 17 | @weather = Weather.new 18 | end 19 | 20 | def create 21 | @weather = Weather.new(weather_params) 22 | respond_to do |format| 23 | if @weather.save 24 | format.html { redirect_to @weather, notice: 'Weather was successfully created.' } 25 | format.json { render :show, status: :created, location: @weather } 26 | else 27 | format.html { render :new } 28 | format.json { render json: @weather.errors, status: :unprocessable_entity } 29 | end 30 | end 31 | end 32 | 33 | 34 | def update 35 | respond_to do |format| 36 | if @weather.update(weather_params) 37 | format.html { redirect_to @weather, notice: 'Weather was successfully updated.' } 38 | format.json { render :show, status: :ok, location: @weather } 39 | else 40 | format.html { render :edit } 41 | format.json { render json: @weather.errors, status: :unprocessable_entity } 42 | end 43 | end 44 | end 45 | 46 | # DELETE /services/1 47 | # DELETE /services/1.json 48 | def destroy 49 | @weather.destroy 50 | respond_to do |format| 51 | format.html { redirect_to root_url, notice: 'Weather was successfully destroyed.' } 52 | format.json { head :no_content } 53 | end 54 | end 55 | 56 | 57 | private 58 | # Use callbacks to share common setup or constraints between actions. 59 | def set_weather 60 | @weather = Weather.find(params[:id]) 61 | end 62 | 63 | # Never trust parameters from the scary internet, only allow the white list through. 64 | # Going off of this site as a guide: http://astockwell.com/blog/2014/03/polymorphic-associations-in-rails-4-devise/ 65 | def weather_params 66 | params.require(:weathers).permit(:address, :latitude, :longitude, :api_key, :units) 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /app/helpers/api_helper.rb: -------------------------------------------------------------------------------- 1 | module ApiHelper 2 | def api_request(method:, url:, headers:, payload: nil, user: nil, verify_ssl: true) 3 | if url.nil? || url.blank? 4 | raise ArgumentError, 'api_request was called with a nil/blank url' 5 | end 6 | if user.nil? 7 | api_call(method, url, headers, payload, verify_ssl) 8 | else 9 | basic_auth(user, method, url, headers, payload, verify_ssl) 10 | end 11 | end 12 | 13 | def api_call(method, url, headers, payload, verify_ssl) 14 | response = RestClient::Request.execute method: method, 15 | url: url, 16 | headers: headers, 17 | payload: payload, 18 | verify_ssl: verify_ssl, 19 | timeout: 1 20 | JSON.parse(response) 21 | rescue JSON::ParserError 22 | logger.error 'There was an error parsing an API response. See debug logs for details.' 23 | # log_request_data(request: request, response: response) 24 | logger.debug "The error was caused by: #{response.to_s}" 25 | nil 26 | end 27 | 28 | def basic_auth(user, method, url, headers, payload, verify_ssl) 29 | response = RestClient::Request.execute method: method, 30 | url: url, 31 | user: user.username, 32 | password: user.password, 33 | headers: headers, 34 | payload: payload, 35 | verify_ssl: verify_ssl, 36 | timeout: 1 37 | JSON.parse(response) 38 | rescue JSON::ParserError 39 | logger.error 'There was an error parsing an API response. See debug logs for details.' 40 | # log_request_data(request: request, response: response) 41 | logger.debug "The error was caused by: #{response.to_s}" 42 | nil 43 | end 44 | 45 | # def log_request_data(request:, response:, log_level: Logger::DEBUG) 46 | # logger.add(log_level){'Request and response data below...'} 47 | # logger.add(log_level){"Request method: #{request.method.upcase}"} 48 | # 49 | # #logging the payload isn't possible currently because of 50 | # # https://github.com/rest-client/rest-client/issues/357 51 | # unless request.payload.nil? 52 | # # logger.add(log_level){"Request Payload: #{request.payload}"} 53 | # logger.add(log_level){"Request Payload: #{request.args[:payload].inspect}"} 54 | # end 55 | # 56 | # logger.add(log_level){"Headers: #{request.headers}"} 57 | # 58 | # unless request.cookies.empty? 59 | # logger.add(log_level){"Cookies: #{request.cookies}"} 60 | # end 61 | # logger.add(log_level){"URL: #{request.url}"} 62 | # 63 | # if log_level == Logger::DEBUG || log_level > Logger::WARN 64 | # logger.add(log_level){"Response: #{response}"} 65 | # end 66 | # 67 | # unless response.cookies.empty? 68 | # logger.add(log_level){"Response cookies: #{response.cookies}"} 69 | # end 70 | # end 71 | end -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | 3 | # Returns the full title on a per-page basis. # Documentation comment 4 | def full_title(page_title = '') # Method def, optional arg 5 | base_title = 'Plex-Board' # Variable assignment 6 | if page_title.empty? # Boolean test 7 | base_title # Implicit return 8 | else 9 | base_title + ' | ' + page_title # String concatenation 10 | end 11 | end 12 | 13 | 14 | def bootstrap_class_for flash_type 15 | { success: 'alert-success', error: 'alert-danger', alert: 'alert-warning', notice: 'alert-info' }[flash_type.to_sym] || flash_type.to_s 16 | end 17 | 18 | def flash_messages(opts = {}) 19 | flash.each do |msg_type, message| 20 | concat(content_tag(:div, message, class: "alert #{bootstrap_class_for(msg_type)} alert-dismissible", role: 'alert') do 21 | concat(content_tag(:button, class: 'close', data: { dismiss: 'alert' }) do 22 | concat content_tag(:span, '×'.html_safe, 'aria-hidden' => true) 23 | concat content_tag(:span, 'Close', class: 'sr-only') 24 | end) 25 | concat message 26 | end) 27 | end 28 | nil 29 | end 30 | end -------------------------------------------------------------------------------- /app/helpers/notifications_helper.rb: -------------------------------------------------------------------------------- 1 | module NotificationsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/plex_object_helper.rb: -------------------------------------------------------------------------------- 1 | module PlexObjectHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/plex_service_helper.rb: -------------------------------------------------------------------------------- 1 | module PlexServiceHelper 2 | def generate_stream_link(plex_service) 3 | streams = plex_service.plex_sessions.where(stream_type: 'Stream').count 4 | streams = streams.zero? ? false : streams 5 | transcodes = plex_service.plex_sessions.where(stream_type: 'Transcode').count 6 | transcodes = transcodes.zero? ? false : transcodes 7 | 8 | if streams || transcodes 9 | base_string = "#{plex_service.service.name}: " 10 | if streams 11 | base_string += "Streams: #{streams} " 12 | end 13 | if transcodes 14 | base_string += "Transcodes: #{transcodes} " 15 | end 16 | base_string.strip 17 | else 18 | false 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/helpers/plex_session_helper.rb: -------------------------------------------------------------------------------- 1 | module PlexSessionHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/plex_user.rb: -------------------------------------------------------------------------------- 1 | class PlexUser 2 | attr_accessor :username, :password 3 | def initialize(username, password) 4 | @username = username 5 | @password = password 6 | end 7 | end -------------------------------------------------------------------------------- /app/helpers/service_helper.rb: -------------------------------------------------------------------------------- 1 | module ServiceHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/weather_helper.rb: -------------------------------------------------------------------------------- 1 | module WeatherHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/app/mailers/.keep -------------------------------------------------------------------------------- /app/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/app/models/.keep -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/plex_recently_added.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: plex_recently_addeds 4 | # 5 | # id :integer not null, primary key 6 | # added_date :datetime not null 7 | # created_at :datetime not null 8 | # updated_at :datetime not null 9 | # plex_service_id :integer 10 | # uuid :string not null 11 | # 12 | 13 | class PlexRecentlyAdded < ActiveRecord::Base 14 | belongs_to :plex_service 15 | has_one :plex_object, as: :plex_object_flavor, dependent: :destroy 16 | accepts_nested_attributes_for :plex_object 17 | 18 | validates_associated :plex_service 19 | validates_presence_of :added_date 20 | validates_presence_of :uuid 21 | 22 | def get_added_date 23 | self.added_date.to_date.strftime("%m/%d/%Y") 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/models/plex_session.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: plex_sessions 4 | # 5 | # id :integer not null, primary key 6 | # progress :integer not null 7 | # total_duration :integer not null 8 | # plex_user_name :string 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # session_key :string not null 12 | # plex_service_id :integer 13 | # stream_type :string not null 14 | # 15 | 16 | class PlexSession < ActiveRecord::Base 17 | 18 | belongs_to :plex_service 19 | has_one :plex_object, as: :plex_object_flavor, dependent: :destroy 20 | accepts_nested_attributes_for :plex_object 21 | 22 | validates_presence_of :plex_user_name, :session_key, :progress, :total_duration 23 | validates :session_key, uniqueness: {scope: :plex_service}, allow_blank: false 24 | 25 | def get_percent_done 26 | ((self.progress.to_f / self.total_duration.to_f) * 100).to_i 27 | end 28 | 29 | def as_json(options) 30 | json = super(only: [:id, :plex_user_name, :session_key]) 31 | json[:percent_done] = get_percent_done 32 | json[:plex_object] = plex_object.as_json(options) 33 | 34 | json[:self_uri] = Rails.application.routes.url_helpers.plex_service_now_playing_path(self.id) 35 | json[:created_at] = created_at 36 | json[:update_at] = updated_at 37 | json 38 | end 39 | 40 | def self.determine_stream_type(videoDecision) 41 | case videoDecision.try(:downcase) 42 | when 'copy' 43 | 'Stream' 44 | when 'transcode' 45 | 'Transcode' 46 | else 47 | logger.warn { "Got PlexSession with videoDecision that has no known state. Data: '#{videoDecision}'. Defaulting to 'Stream'" } 48 | 'Stream' 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /app/models/server_load.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: server_loads 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # created_at :datetime not null 8 | # updated_at :datetime not null 9 | # service_host_id :integer 10 | # 11 | 12 | class ServerLoad < ActiveRecord::Base 13 | belongs_to :service 14 | 15 | end 16 | -------------------------------------------------------------------------------- /app/models/service.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: services 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # dns_name :string 8 | # ip :string 9 | # url :string not null 10 | # port :integer not null 11 | # service_flavor_id :integer 12 | # service_flavor_type :string 13 | # created_at :datetime not null 14 | # updated_at :datetime not null 15 | # 16 | 17 | require 'resolv' 18 | class Service < ActiveRecord::Base 19 | belongs_to :service_flavor, polymorphic: :true, optional: true 20 | has_one :server_load 21 | # before_destroy :destroy_associated 22 | after_initialize :init 23 | # after_create :ping 24 | after_validation :clear_ping_cache! 25 | 26 | attr_accessor :timeout 27 | strip_attributes only: [:ip, :url, :dns_name], collapse_spaces: true 28 | 29 | validates_associated :service_flavor 30 | validates_presence_of :name 31 | validates_uniqueness_of :name 32 | validates_presence_of :url 33 | validates_uniqueness_of :url 34 | validates_inclusion_of :port, in: 1..65_535 35 | validates :ip, 36 | length: {minimum: 7, maximum: 45}, 37 | format: {with: Resolv::IPv4::Regex}, 38 | uniqueness: {scope: :port}, 39 | allow_blank: true 40 | validates :dns_name, 41 | length: {minimum: 2, maximum: 127}, 42 | uniqueness: {scope: :port}, 43 | allow_blank: true 44 | validates :ip, presence: true, if: :ip_and_dns_name_dont_exist 45 | validates :dns_name, presence: true, if: :ip_and_dns_name_dont_exist 46 | 47 | def init 48 | @timeout ||= 3 49 | self.online_status ||= false 50 | end 51 | 52 | def ip_and_dns_name_dont_exist 53 | if (ip.blank? || ip.to_s.empty?) && (dns_name.blank? || dns_name.to_s.empty?) 54 | errors.add(:base, 'IP Address or DNS Name must exist') 55 | true 56 | else 57 | false 58 | end 59 | end 60 | 61 | def clear_ping_cache! 62 | if Rails.cache.delete("service_#{id}/online") 63 | logger.debug('Successfully deleted cache!') 64 | end 65 | self 66 | end 67 | 68 | def ping 69 | Rails.cache.fetch("service_#{id}/online", expires_in: 10.seconds, race_condition_ttl: 7.seconds) do 70 | check_online_status 71 | self.online_status 72 | end 73 | end 74 | 75 | def online_status_string 76 | online?(online_status) 77 | end 78 | 79 | def online?(boolean) 80 | boolean ? 'online' : 'offline' 81 | end 82 | 83 | def ping_for_status_change 84 | before_ping = self.online_status 85 | ping_result = ping 86 | if before_ping != ping_result 87 | logger.info("Detected status change from #{online?(before_ping)} to #{online_status_string} for service: #{name}") 88 | self.online_status 89 | else 90 | nil 91 | end 92 | end 93 | 94 | def connect_method 95 | if !dns_name.blank? 96 | dns_name 97 | else 98 | ip 99 | end 100 | end 101 | 102 | def as_json(options) 103 | json = super(only: [:id]) 104 | json[:self_uri] = Rails.application.routes.url_helpers.service_online_status_path(id) 105 | json 106 | end 107 | 108 | def online! 109 | self.online_status = true 110 | self 111 | end 112 | 113 | def offline! 114 | self.online_status = false 115 | self 116 | end 117 | 118 | def last_seen_now! 119 | self.last_seen = Time.now 120 | self 121 | end 122 | 123 | def online_status 124 | Rails.cache.read("service/#{id}/online_status") 125 | end 126 | 127 | def online_status=(boolean_status) 128 | Rails.cache.write("service/#{id}/online_status", boolean_status) 129 | end 130 | 131 | def last_seen 132 | Rails.cache.read("service/#{id}/last_seen") 133 | end 134 | 135 | def last_seen=(timestamp) 136 | Rails.cache.write("service/#{id}/last_seen", timestamp) 137 | end 138 | 139 | private 140 | 141 | def check_online_status 142 | ping_destination = connect_method 143 | begin 144 | Timeout.timeout(@timeout) do 145 | s = TCPSocket.new(ping_destination, port) 146 | s.close 147 | online! 148 | last_seen_now! 149 | end 150 | #TODO: Use connection refused to indicate that the server itself is still responding. 151 | rescue Errno::ECONNREFUSED 152 | offline! 153 | rescue Timeout::Error, Errno::ENETUNREACH, Errno::EHOSTUNREACH, SocketError 154 | offline! 155 | end 156 | end 157 | end 158 | -------------------------------------------------------------------------------- /app/models/weather.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: weathers 4 | # 5 | # id :integer not null, primary key 6 | # api_key :string 7 | # latitude :float 8 | # longitude :float 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # address :string 12 | # units :text 13 | # city :string 14 | # state :string 15 | # 16 | 17 | class Weather < ActiveRecord::Base 18 | require 'geocoder' 19 | require 'forecast_io' 20 | serialize :units, Hash 21 | attr_accessor :address 22 | 23 | before_validation :get_precise_location, if: :location_empty? 24 | before_validation :config 25 | 26 | validates_presence_of :address, if: :location_empty? 27 | validates_presence_of :latitude, :longitude, :api_key, :units 28 | 29 | DEFAULT_UNITS = {'US Customary Units': 'us'} 30 | SUPPORTED_UNITS = {'SI': 'ca', 'US Customary Units': 'us'} 31 | 32 | def config 33 | if units.nil? || units.empty? || !(SUPPORTED_UNITS.keys.include? units) 34 | logger.warn "The supplied units: '#{units}' were either nil or not found in this list: #{SUPPORTED_UNITS}. Setting to the default of: #{DEFAULT_UNITS}." 35 | self.units = DEFAULT_UNITS 36 | end 37 | end 38 | 39 | def location_empty? 40 | latitude.nil? || latitude.blank? || longitude.nil? || longitude.blank? 41 | end 42 | 43 | def get_weather 44 | #TODO: Add this to configurable options add warning that going below 2 minutes will exceed the default 1000 calls/day limit that forecast.io has 45 | Rails.cache.fetch("weather_#{self.id}/forecast", expires_in: 2.minutes, race_condition_ttl: 1.minute) do 46 | ForecastIO.api_key = api_key 47 | ForecastIO.default_params = {units: units.values.first} 48 | ForecastIO.forecast(latitude, longitude) 49 | end 50 | end 51 | 52 | def get_city_and_state 53 | resolve_city_and_state 54 | "#{city}, #{state}" 55 | end 56 | 57 | def as_json(options) 58 | json = super(only: [:id]) 59 | json[:self_uri] = Rails.application.routes.url_helpers.weather_path(self.id) 60 | json 61 | end 62 | 63 | private #################################################### 64 | 65 | def get_precise_location 66 | logger.debug "Getting precise location for address: #{address}" 67 | geocoded = Geocoder.search(self.address).first 68 | 69 | if geocoded.nil? 70 | logger.error "Getting geolocation failed on address: #{self.address}" 71 | self.errors.add(:base, 'Fetching the precise location failed. Please check that the address is valid.') 72 | return 73 | end 74 | 75 | update!(latitude: geocoded.latitude, longitude: geocoded.longitude) 76 | end 77 | 78 | def resolve_city_and_state 79 | if city && state 80 | return 81 | end 82 | 83 | geo_search = Geocoder.search("#{self.latitude}, #{self.longitude}").first 84 | update!(city: geo_search.city, state: geo_search.state) 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /app/views/info/_weatherform.html.erb: -------------------------------------------------------------------------------- 1 | <%= simple_form_for @weather do |f| %> 2 |
    3 |
    4 | <%= f.input :api_key, label: 'API Key', placeholder: 'Forecast.io API key', required: false %> 5 |
    6 |
    7 |
    8 |
    9 | <%= f.input :address, placeholder: '1600 Amphitheatre Pkwy, Mountain View, CA 94043', required: false %> 10 |
    11 |
    12 | <%= f.input :latitude, label: 'Latitude', required: false %> 13 |
    14 |
    15 | <%= f.input :longitude, label: 'Longitude', required: false %> 16 |
    17 |
    18 | 19 |
    20 |
    21 | <% if request.original_url.include? 'edit' %> 22 | <% str = "Are you sure you want to delete: #{@weather.name}?" %> 23 | <%= link_to 'Delete Service', @weather, method: :delete, data: {confirm: str}, class: 'btn btn-md btn-danger' %> 24 | <% end %> 25 | <%= f.button :submit, class: 'btn-success pull-right' %> 26 |
    27 |
    28 | <% end %> -------------------------------------------------------------------------------- /app/views/info/about.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'About') %> 2 | 3 | <%= render 'layouts/servicepanel' %> 4 | <%= render 'layouts/rightbar' %> 5 |
    6 |

    About

    7 |

    Plex-Board is an open-source project meant to provide a simple-to-use dashboard for administrators and users of Plex servers. 8 | Monitors of services is done by opening a TCP socket to the port you specify when configuring them.

    9 |

    The project is hosted on GitHub here: https://github.com/scytherswings/Plex-Board

    10 |

    If you have any problems, feel free to ask in the gitter chat room. 11 | Or create an issue here.

    12 | 13 |

    Feel free to send a donation my way if you like what you see!

    14 | 15 | 16 | 17 | 18 |
    19 |

    20 |
    21 |

    Version <%= PlexBoard::VERSION %>

    22 |
    23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/views/info/configuration.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Configuration') %> 2 | 3 | <%= render 'layouts/servicepanel' %> 4 | <%= render 'layouts/rightbar' %> 5 | 6 |
    7 |

    Configuration

    8 |

    This page hasn't been built yet..

    9 |
    10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/views/info/new_weather.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'New Weather') %> 2 | 3 | <%= render 'layouts/servicepanel' %> 4 | <%= render 'layouts/rightbar' %> 5 | 6 |
    7 |

    New Weather

    8 |
    9 | <%= render 'shared/error_messages' %> 10 | <%= render 'weatherform' %> 11 |
    12 |
    13 | 14 | -------------------------------------------------------------------------------- /app/views/layouts/_footer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/views/layouts/_header.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= full_title(yield(:title)) %> 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> 12 | <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> 13 | <%= csrf_meta_tags %> 14 | 15 | -------------------------------------------------------------------------------- /app/views/layouts/_navbar.html.erb: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/views/layouts/_rightbar.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | 3 | <% unless @weathers.nil? || @weathers.empty? %> 4 |

    <%= link_to 'Weather', configuration_path, class: 'h2_index_link' %>

    5 | <%= render @weathers %> 6 | <% end %> 7 |
    -------------------------------------------------------------------------------- /app/views/layouts/_servicepanel.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | <% unless @services.nil? %> 5 | <% unless @services.empty? && @services.blank? %> 6 |

    <%= link_to 'Services', all_services_path, class: 'h2_index_link' %>

    7 | <% end %> 8 | 9 |
    10 | <%= render @services %> 11 |
    12 | <% end %> 13 |
    14 |
    15 |
    -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= render 'layouts/header' %> 5 | 6 | 7 | 8 | <%= render 'layouts/navbar' %> 9 | 10 | 11 |
    12 |
    13 |
    14 | 15 | <%= flash_messages %> 16 | 17 | <%= full_title(yield(:title)) %> 18 |
    19 |
    20 | <%= yield %> 21 |
    22 |
    23 | 24 | <%= render 'layouts/footer' %> 25 | 26 | 27 |
    28 |
    29 |
    30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/views/plex_recently_addeds/_plex_recently_added.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Recently Added

    4 |
    5 | <%= image_tag @plex_recently_added.plex_object.get_img %> 6 |

    <%= @plex_recently_added.plex_object.media_title %>

    7 |

    <%= @plex_recently_added.plex_object.get_description %>

    8 |

    Added to <%= link_to @plex_recently_added.plex_service.service.name, 9 | {controller: @plex_recently_added.plex_service.service.service_flavor_type.tableize, 10 | action: 'show', id: @plex_recently_added.plex_service.service.service_flavor_id}, 11 | class: 'h2_index_link'%> on <%= @plex_recently_added.get_added_date %>

    12 |
    13 |
    14 |
    15 | -------------------------------------------------------------------------------- /app/views/plex_recently_addeds/_plex_recently_added.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! @plex_recently_added, :id 2 | json.plex_object do 3 | json.partial! 'shared/plex_object', locals: {plex_object: @plex_recently_added.plex_object} 4 | end 5 | json.(@plex_recently_added, :created_at, :updated_at) 6 | json.self_uri url_for(action: :recently_added, controller: 'plex_services', only_path: false) -------------------------------------------------------------------------------- /app/views/plex_services/_now_playing.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Now Playing

    4 |
    5 | 6 | <%= image_tag plex_session.plex_object.get_img, skip_pipeline: true %> 7 | 8 |
    9 |
    13 |
    14 |
    15 |

    <%= plex_session.plex_object.media_title %>

    16 | 17 |

    <%= plex_session.plex_object.get_description %>

    18 |
    19 |
    20 |
    21 | 22 | -------------------------------------------------------------------------------- /app/views/plex_services/_now_playing_navbar.html.erb: -------------------------------------------------------------------------------- 1 | <% html_class ||= nil %> 2 | <%= link_to generate_stream_link(plex_service), plex_service, class: html_class ? html_class : 'h2_index_link', id: "streams_for_plex_service_#{plex_service.id}" %> -------------------------------------------------------------------------------- /app/views/plex_services/_plex_session.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Now Playing

    4 |
    5 | 6 | <%= image_tag plex_session.plex_object.get_img %> 7 | 8 |
    9 |
    13 |
    14 |
    15 |

    <%= plex_session.plex_object.media_title %>

    16 | 17 |

    <%= plex_session.plex_object.get_description %>

    18 |
    19 |
    20 |
    21 | 22 | -------------------------------------------------------------------------------- /app/views/plex_services/_plex_session.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! plex_session, :id, :plex_user_name, :session_key 2 | json.percent_done plex_session.get_percent_done 3 | json.plex_object do 4 | json.partial! 'shared/plex_object', locals: {plex_object: plex_session.plex_object} 5 | end 6 | json.(plex_session, :created_at, :updated_at) 7 | json.self_uri url_for(action: :now_playing, controller: 'plex_services', only_path: false) -------------------------------------------------------------------------------- /app/views/plex_services/_plexserviceform.html.erb: -------------------------------------------------------------------------------- 1 | <%= simple_form_for @plex_service do |i| %> 2 | 3 | <%= i.simple_fields_for :service do |f| %> 4 |
    5 | 6 |
    7 | <%= f.input :name, placeholder: 'New service name', required: false %> 8 |
    9 |
    10 | <%= f.input :ip, label: 'IP Address', placeholder: '127.0.0.1', required: false %> 11 |
    12 |
    13 | <%= f.input :dns_name, label: 'DNS Name', placeholder: 'plex', required: false %> 14 |
    15 |
    16 | <%= f.input :port, placeholder: '32400', required: false %> 17 |
    18 |
    19 |
    20 |
    21 | <%= f.input :url, label: 'URL', placeholder: 'http://plex.example.com:32400/web', required: false %> 22 |
    23 |
    24 | <% end %> 25 | 26 |
    27 |
    28 | <%= i.input :username, placeholder: 'plex.tv username', required: false %> 29 |
    30 |
    31 | <%= i.input :password, placeholder: 'plex.tv password', required: false %> 32 |
    33 |
    34 | 35 |
    36 |
    37 | <% if request.original_url.include? 'edit' %> 38 | <% str = "Are you sure you want to delete: #{@plex_service.service.name}?" %> 39 | <%= link_to 'Delete Service', @plex_service, method: :delete, data: { confirm: str }, class: 'btn btn-md btn-danger' %> 40 | <%= i.button :submit, 'Update Plex Service', class: 'btn-success pull-right' %> 41 | <% else %> 42 | <%= i.button :submit, 'Create Plex Service', class: 'btn-success pull-right' %> 43 | <% end %> 44 | 45 |
    46 |
    47 | 48 | 49 | <% end %> 50 | 51 | -------------------------------------------------------------------------------- /app/views/plex_services/_recently_added.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Recently Added

    4 |
    5 | <%= image_tag plex_recently_added.plex_object.get_img, skip_pipeline: true %> 6 |

    <%= plex_recently_added.plex_object.media_title %>

    7 |

    <%= plex_recently_added.plex_object.get_description %>

    8 |

    Added to <%= link_to plex_recently_added.plex_service.service.name, 9 | {controller: plex_recently_added.plex_service.service.service_flavor_type.tableize, 10 | action: 'show', id: plex_recently_added.plex_service.service.service_flavor_id}, 11 | class: 'h2_index_link'%> on <%= plex_recently_added.get_added_date %>

    12 |
    13 |
    14 |
    15 | -------------------------------------------------------------------------------- /app/views/plex_services/all_plex_services.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'All Plex Services') %> 2 | 3 | <%= render 'layouts/servicepanel' %> 4 | <%= render 'layouts/rightbar' %> 5 | 6 |
    7 |

    All Services

    8 |
    9 |
    10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | <% @plex_services.each do |f| %> 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | <% end %> 31 |
    Service NameIP AddressDNS NamePortURLUsernameOptions
    <%= f.service.name %><%= f.service.ip %><%= f.service.dns_name %><%= f.service.port %><%= f.service.url %><%= f.username %> 27 | <%= link_to 'Edit', edit_plex_service_path(f), class: "btn btn-md btn-primary" %> 28 |
    32 | 33 |
    34 |
    35 | <%= link_to "Add New Service", choose_service_type_path, class: "btn btn-success" %> 36 |
    37 | 38 |
    39 | 40 |
    41 | 42 | <%= render 'layouts/rightbar' %> 43 | -------------------------------------------------------------------------------- /app/views/plex_services/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Edit #{@plex_service.service.name}") %> 2 | 3 | <%= render 'layouts/servicepanel' %> 4 | <%= render 'layouts/rightbar' %> 5 | 6 |
    7 |

    Editing Plex Service: <%= @plex_service.service.name %>

    8 |
    9 | <%= render 'shared/error_messages' %> 10 | <%= render 'plexserviceform' %> 11 |
    12 |
    13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/views/plex_services/new.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'New Plex Service') %> 2 | 3 | <%= render 'layouts/servicepanel' %> 4 | <%= render 'layouts/rightbar' %> 5 | 6 |
    7 |

    New Plex Service

    8 |
    9 | <%= render 'shared/error_messages' %> 10 | <%= render 'plexserviceform' %> 11 |
    12 |
    13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/views/plex_services/show.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, @plex_service.service.name) %> 2 | 3 | <%= render 'layouts/servicepanel' %> 4 | <%= render 'layouts/rightbar' %> 5 | 6 |
    7 |

    Plex Service: <%= @plex_service.service.name %>

    8 |
    9 | 10 | 11 | <% if @plex_service.service.ip.nil? || @plex_service.service.ip.empty? %> 12 | 13 | <% elsif @plex_service.service.dns_name.nil? || @plex_service.service.dns_name.empty? %> 14 | 15 | <% else %> 16 | 17 | 18 | <% end %> 19 | 20 | 21 | 22 | 23 | 24 | 25 | <% if @plex_service.service.ip.nil? || @plex_service.service.ip.empty? %> 26 | 27 | <% elsif @plex_service.service.dns_name.nil? || @plex_service.service.dns_name.empty? %> 28 | 29 | <% else %> 30 | 31 | 32 | <% end %> 33 | 34 | 35 | 36 | 37 |
    Plex Service NameDNS NameIP AddressDNS NameIP AddressPortURLToken
    <%= @plex_service.service.name %><%= @plex_service.service.dns_name %><%= @plex_service.service.ip %> <%= @plex_service.service.dns_name %><%= @plex_service.service.ip %> <%= @plex_service.service.port %> <%= @plex_service.service.url %> <%= @plex_service.token %>
    38 | 39 |
    40 |
    41 | <% str = "Are you sure you want to delete: #{@plex_service.service.name}?" %> 42 | <% if @plex_service.service.service_flavor_id %> 43 | <%= link_to 'Edit', {controller: @plex_service.service.service_flavor_type.tableize, action: 'edit', 44 | id: @plex_service.service.service_flavor_id}, class: 'btn btn-md btn-primary' %> 45 | <% else %> 46 | <%= link_to 'Edit', edit_service_path(@plex_service.service), class: 'btn btn-md btn-primary' %> 47 | <% end %> 48 | <%= link_to 'Delete', @plex_service, method: :delete, data: { confirm: str }, class: 'btn btn-md btn-danger' %> 49 | 50 |
    51 |
    52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/views/services/_service.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 | 5 | 6 | 20 | 27 | 28 | 29 |
    7 | <% if service.online_status %> 8 | <%= link_to service.url, class: "h2_index_link", :target => "_blank" do %> 9 | 11 | <% end %> 12 | <% else %> 13 | <%= link_to service.url, class: "h2_index_link", :target => "_blank" do %> 14 | 16 | <% end %> 17 | <% end %> 18 |
    19 |
    21 | <% if service.service_flavor_id %> 22 | <%= link_to service.name, {controller: service.service_flavor_type.tableize, action: 'show', id: service.service_flavor_id}, class: 'h2_index_link'%> 23 | <% else %> 24 | <%= link_to service.name, service, class: 'h2_index_link' %> 25 | <% end %> 26 |
    30 |
    31 |
    -------------------------------------------------------------------------------- /app/views/services/_service.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! service, :id, :online_status, :name, :url, :last_seen 2 | json.(service, :created_at, :updated_at) 3 | json.self_uri service_url(service, format: :json) -------------------------------------------------------------------------------- /app/views/services/_serviceform.html.erb: -------------------------------------------------------------------------------- 1 | <%= simple_form_for @service do |f| %> 2 |
    3 |
    4 | <%= f.input :name, placeholder: 'New service name', required: false %> 5 |
    6 |
    7 | <%= f.input :ip, label: 'IP Address', placeholder: '127.0.0.1', required: false %> 8 |
    9 |
    10 | <%= f.input :dns_name, label: 'DNS Name', placeholder: 'plex', required: false %> 11 |
    12 |
    13 | <%= f.input :port, placeholder: '80', required: false %> 14 |
    15 |
    16 |
    17 |
    18 | <%= f.input :url, label: 'URL', placeholder: 'http://plex.example.com:32400/web', required: false %> 19 |
    20 |
    21 | 22 |
    23 |
    24 | <% if request.original_url.include? 'edit' %> 25 | <% str = "Are you sure you want to delete: #{@service.name}?" %> 26 | <% if @service.service_flavor_id %> 27 | <%= link_to 'Delete Service', {controller: @service.service_flavor_type.tableize, 28 | method: :delete, data: { confirm: str }, anchor: @service}, 29 | class: 'btn btn-md btn-danger' %> 30 | <% else %> 31 | <%= link_to 'Delete Service', @service, method: :delete, data: { confirm: str }, class: 'btn btn-md btn-danger' %> 32 | <% end %> 33 | <% end %> 34 | <%= f.button :submit, class: 'btn-success pull-right' %> 35 |
    36 |
    37 | 38 | 39 | <% end %> 40 | 41 | -------------------------------------------------------------------------------- /app/views/services/all_services.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "All Services") %> 2 | 3 | <%= render 'layouts/servicepanel' %> 4 | <%= render 'layouts/rightbar' %> 5 | 6 |
    7 |

    All Services

    8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | <% @services.each do |f| %> 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 36 | 37 | <% end %> 38 | 39 |
    Service NameIP AddressDNS NamePortURLService TypeOptions
    <%= f.name %><%= f.ip %><%= f.dns_name %><%= f.port %><%= f.url %><%= f.service_flavor_type || 'Generic Service' %> 30 | <% if f.service_flavor_id %> 31 | <%= link_to 'Edit', {controller: f.service_flavor_type.tableize, action: 'edit', id: f.service_flavor_id}, class: 'btn btn-md btn-primary' %> 32 | <% else %> 33 | <%= link_to 'Edit', edit_service_path(f), class: 'btn btn-md btn-primary' %> 34 | <% end %> 35 |
    40 |
    41 |
    42 | <%= link_to "Add New Service", choose_service_type_path, class: "btn btn-success" %> 43 |
    44 |
    45 | 46 | 47 | -------------------------------------------------------------------------------- /app/views/services/choose_service_type.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Choose Service Type') %> 2 | 3 | <%= render 'layouts/servicepanel' %> 4 | <%= render 'layouts/rightbar' %> 5 | 6 |
    7 |
    8 |

    What type of service would you like?

    9 |
    10 | 20 |
    21 |
    22 |
    23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/views/services/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Edit #{@service.name}") %> 2 | 3 | <%= render 'layouts/servicepanel' %> 4 | <%= render 'layouts/rightbar' %> 5 | 6 |
    7 |

    Editing Service: <%= @service.name %>

    8 |
    9 | <%= render 'shared/error_messages' %> 10 | <%= render 'serviceform' %> 11 |
    12 |
    13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/views/services/index.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'layouts/servicepanel' %> 2 | <%= render 'layouts/rightbar' %> 3 | 4 | <% if @services.nil? || @services.empty? %> 5 |
    6 |
    7 |

    Welcome to Plex-Board!

    8 |

    It looks like you haven't added a service yet...

    9 |

    You should add one here:

    10 |
    11 | <%= link_to "Add Service!", choose_service_type_path, class: "btn btn-lg btn-success" %> 12 |
    13 |
    14 |
    15 | <% elsif !@plex_services.nil? && !@plex_services.empty? %> 16 |
    17 | 46 |
    47 | 48 | <% else %> 49 |
    50 |
    51 |

    It looks like you haven't added a Plex service yet...

    52 |

    You should add one here:

    53 |
    54 | <%= link_to 'Add Plex Service!', new_plex_service_path, class: 'btn btn-md btn-success' %> 55 |
    56 |
    57 |
    58 | 59 | <% end %> -------------------------------------------------------------------------------- /app/views/services/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.array!(@services) do |service| 2 | json.extract! service, :id 3 | json.url service_url(service, format: :json) 4 | end 5 | -------------------------------------------------------------------------------- /app/views/services/new.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'New Service') %> 2 | 3 | <%= render 'layouts/servicepanel' %> 4 | 5 |
    6 |
    7 |

    New Service

    8 | <%= render 'shared/error_messages' %> 9 | <%= render 'serviceform' %> 10 |
    11 |
    12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/views/services/show.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, @service.name) %> 2 | 3 | <%= render 'layouts/servicepanel' %> 4 | <%= render 'layouts/rightbar' %> 5 | 6 |
    7 |

    Service: <%= @service.name %>

    8 |
    9 | 10 | 11 | <% if @service.ip.nil? || @service.ip.empty? %> 12 | 13 | <% elsif @service.dns_name.nil? || @service.dns_name.empty? %> 14 | 15 | <% else %> 16 | 17 | 18 | <% end %> 19 | 20 | 21 | 22 | 23 | 24 | <% if @service.ip.nil? || @service.ip.empty? %> 25 | 26 | <% elsif @service.dns_name.nil? || @service.dns_name.empty? %> 27 | 28 | <% else %> 29 | 30 | 31 | <% end %> 32 | 33 | 34 | 35 |
    Service NameDNS NameIP AddressDNS NameIP AddressPortURL
    <%= @service.name %><%= @service.dns_name %><%= @service.ip %> <%= @service.dns_name %><%= @service.ip %> <%= @service.port %> <%= @service.url %>
    36 | 37 |
    38 |
    39 | <% str = "Are you sure you want to delete: #{@service.name}?" %> 40 | <% if @service.service_flavor_id %> 41 | <%= link_to 'Edit', {controller: @service.service_flavor_type.tableize, action: 'edit', 42 | id: @service.service_flavor_id}, class: 'btn btn-md btn-primary' %> 43 | <%= link_to 'Delete', {controller: @service.service_flavor_type.tableize, method: :delete, 44 | data: { confirm: str }, anchor: @service}, class: 'btn btn-md btn-danger' %> 45 | <% else %> 46 | <%= link_to 'Edit', edit_service_path(@service), class: 'btn btn-md btn-primary' %> 47 | <%= link_to 'Delete', @service, method: :delete, data: { confirm: str }, class: 'btn btn-md btn-danger' %> 48 | <% end %> 49 |
    50 |
    51 | 52 | 53 | -------------------------------------------------------------------------------- /app/views/services/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! @service, :id, :created_at, :updated_at 2 | -------------------------------------------------------------------------------- /app/views/shared/_error_messages.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <% if @service %> 3 | <% if @service.errors.any? %> 4 |
    5 | 10 |
    11 | <% end %> 12 | <% end %> 13 | 14 | <% if @plex_service %> 15 | <% if @plex_service.errors.any? %> 16 |
    17 | 22 |
    23 | <% end %> 24 | <% end %> 25 | -------------------------------------------------------------------------------- /app/views/shared/_plex_object.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! plex_object, :id, :media_title, :description 2 | json.plex_thumb_url plex_object.thumb_url 3 | json.image_uri image_url(plex_object.image) 4 | json.(plex_object, :created_at, :updated_at) -------------------------------------------------------------------------------- /app/views/weathers/_weather.html.erb: -------------------------------------------------------------------------------- 1 | <% forecast = weather.get_weather %> 2 |
    3 |
    4 |
    5 |
    6 | 7 |
    8 |
    9 |

    10 | <%= weather.get_city_and_state %> 11 |

    12 |

    13 | <%= forecast['currently']['temperature'].round %> ° 14 | 15 | <%= forecast['currently']['summary'] %> 16 | 17 |

    18 |
    19 | 20 |
    21 |
    22 |
    23 |

    Tomorrow:
    24 | <%= forecast['daily']['summary'] %> 25 |

    26 |
    27 |
    28 |
    29 | 30 | 35 | 36 |
    37 | -------------------------------------------------------------------------------- /app/views/weathers/_weather.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! @weather, :id 2 | json.weather JSON.parse(@weather.get_weather.to_json) 3 | json.(@weather, :created_at, :updated_at) 4 | json.self_uri weather_url(@weather, format: :json) -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../config/application', __dir__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # Install JavaScript dependencies if using Yarn 22 | # system('bin/yarn') 23 | 24 | 25 | # puts "\n== Copying sample files ==" 26 | # unless File.exist?('config/database.yml') 27 | # cp 'config/database.yml.sample', 'config/database.yml' 28 | # end 29 | 30 | puts "\n== Preparing database ==" 31 | system! 'bin/rails db:setup' 32 | 33 | puts "\n== Removing old logs and tempfiles ==" 34 | system! 'bin/rails log:clear tmp:clear' 35 | 36 | puts "\n== Restarting application server ==" 37 | system! 'bin/rails restart' 38 | end 39 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) 11 | spring = lockfile.specs.detect { |spec| spec.name == "spring" } 12 | if spring 13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 14 | gem 'spring', spring.version 15 | require 'spring/binstub' 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | VENDOR_PATH = File.expand_path('..', __dir__) 3 | Dir.chdir(VENDOR_PATH) do 4 | begin 5 | exec "yarnpkg #{ARGV.join(" ")}" 6 | rescue Errno::ENOENT 7 | $stderr.puts "Yarn executable was not detected in the system." 8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 9 | exit 1 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | # run Rails.application 5 | config_file = Rails.root.join 'server_config.yml' 6 | if !File.exist?(config_file) 7 | error_string = "Plex-Board was started without a #{config_file}. You should not run without it. RTFM." 8 | puts error_string 9 | Rails.logger.error error_string 10 | else 11 | relative_root = YAML.load_file(config_file)['relative_root'] 12 | end 13 | 14 | map relative_root ||= '/' do 15 | run Rails.application 16 | end -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module Workspace 10 | class Application < Rails::Application 11 | # Initialize configuration defaults for originally generated Rails version. 12 | config.load_defaults 5.1 13 | 14 | config.generators do |g| 15 | g.test_framework :test_unit, fixture_replacement: :fabrication 16 | g.fixture_replacement :fabrication, dir: 'test/fabricators' 17 | end 18 | 19 | config.action_dispatch.default_headers = { 20 | 'X-Frame-Options' => 'ALLOWALL' 21 | } 22 | 23 | config.assets.unknown_asset_fallback = true 24 | 25 | config.cache_store = :memory_store 26 | 27 | # Settings in config/environments/* take precedence over those specified here. 28 | # Application configuration should go into files in config/initializers 29 | # -- all .rb files in that directory are automatically loaded. 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | channel_prefix: workspace_production 11 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: 30 10 | timeout: 5000 11 | busy_timeout: 1000 12 | 13 | development: 14 | <<: *default 15 | database: db/development.sqlite3 16 | 17 | # Warning: The database defined as "test" will be erased and 18 | # re-generated from your development database when you run "rake". 19 | # Do not set this db to the same as development or production. 20 | test: 21 | <<: *default 22 | # database: db/test.sqlite3 23 | database: db/test<%= ENV['TEST_ENV_NUMBER'] %>.sqlite3 24 | 25 | 26 | production: 27 | <<: *default 28 | database: db/production.sqlite3 29 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = true 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = true 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | # if Rails.root.join('tmp/caching-dev.txt').exist? 17 | # config.action_controller.perform_caching = true 18 | # 19 | # config.cache_store = :memory_store 20 | # config.public_file_server.headers = { 21 | # 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}" 22 | # } 23 | # else 24 | config.action_controller.perform_caching = false 25 | 26 | # config.cache_store = :null_store 27 | # end 28 | 29 | # Don't care if the mailer can't send. 30 | config.action_mailer.raise_delivery_errors = false 31 | 32 | config.action_mailer.perform_caching = false 33 | 34 | # Print deprecation notices to the Rails logger. 35 | config.active_support.deprecation = :log 36 | 37 | # Raise an error on page load if there are pending migrations. 38 | config.active_record.migration_error = :page_load 39 | 40 | # Debug mode disables concatenation and preprocessing of assets. 41 | # This option may cause significant delays in view rendering with a large 42 | # number of complex assets. 43 | config.assets.debug = true 44 | 45 | # Suppress logger output for asset requests. 46 | config.assets.quiet = true 47 | 48 | # Raises error for missing translations 49 | # config.action_view.raise_on_missing_translations = true 50 | 51 | # Use an evented file watcher to asynchronously detect changes in source code, 52 | # routes, locales, etc. This feature depends on the listen gem. 53 | # config.file_watcher = ActiveSupport::EventedFileUpdateChecker 54 | end 55 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Attempt to read encrypted secrets from `config/secrets.yml.enc`. 18 | # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or 19 | # `config/secrets.yml.key`. 20 | config.read_encrypted_secrets = false 21 | 22 | # Disable serving static files from the `/public` folder by default since 23 | # Apache or NGINX already handles this. 24 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'] || true 25 | 26 | # Compress JavaScripts and CSS. 27 | config.assets.js_compressor = :uglifier 28 | # config.assets.css_compressor = :sass 29 | 30 | # Do not fallback to assets pipeline if a precompiled asset is missed. 31 | config.assets.compile = ENV['RAILS_SERVE_STATIC_FILES'] || true 32 | 33 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 34 | 35 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 36 | # config.action_controller.asset_host = 'http://assets.example.com' 37 | 38 | # Specifies the header that your server uses for sending files. 39 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 40 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 41 | 42 | # Mount Action Cable outside main process or domain 43 | # config.action_cable.mount_path = nil 44 | # config.action_cable.url = 'wss://example.com/cable' 45 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 46 | 47 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 48 | # config.force_ssl = true 49 | 50 | # Use the lowest log level to ensure availability of diagnostic information 51 | # when problems arise. 52 | config.log_level = :debug 53 | 54 | # Prepend all log lines with the following tags. 55 | config.log_tags = [ :request_id ] 56 | 57 | # Use a different cache store in production. 58 | # config.cache_store = :mem_cache_store 59 | 60 | # Use a real queuing backend for Active Job (and separate queues per environment) 61 | # config.active_job.queue_adapter = :resque 62 | # config.active_job.queue_name_prefix = "workspace_#{Rails.env}" 63 | config.action_mailer.perform_caching = false 64 | 65 | # Ignore bad email addresses and do not raise email delivery errors. 66 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 67 | # config.action_mailer.raise_delivery_errors = false 68 | 69 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 70 | # the I18n.default_locale when a translation cannot be found). 71 | config.i18n.fallbacks = true 72 | 73 | # Send deprecation notices to registered listeners. 74 | config.active_support.deprecation = :silence 75 | 76 | # Use default logging formatter so that PID and timestamp are not suppressed. 77 | config.log_formatter = ::Logger::Formatter.new 78 | 79 | # Use a different logger for distributed setups. 80 | # require 'syslog/logger' 81 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 82 | 83 | if ENV['LOG_TO_STDOUT'] 84 | logger = ActiveSupport::Logger.new(STDOUT) 85 | logger.formatter = config.log_formatter 86 | config.logger = ActiveSupport::TaggedLogging.new(logger) 87 | end 88 | 89 | # Do not dump schema after migrations. 90 | config.active_record.dump_schema_after_migration = false 91 | end 92 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}" 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | config.action_mailer.perform_caching = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # if ENV['LOG_TO_FILE'] == 'true' 41 | config.logger = Logger.new(Rails.root.join('log','test.log')) 42 | config.log_level = :DEBUG 43 | # else 44 | # config.logger = Logger.new(STDOUT) 45 | # config.log_level = :DEBUG 46 | # end 47 | 48 | config.active_support.test_order = :random 49 | 50 | # Raises error for missing translations 51 | # config.action_view.raise_on_missing_translations = true 52 | end 53 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | # Add Yarn node_modules folder to the asset load path. 9 | Rails.application.config.assets.paths << Rails.root.join('node_modules') 10 | 11 | # Precompile additional assets. 12 | # application.js, application.css, and all non-JS/CSS in the app/assets 13 | # folder are already added. 14 | Rails.application.config.assets.precompile += %w( Allan-Regular.ttf ) 15 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | Rails.backtrace_cleaner.add_silencer { |line| line =~ /rvm/ } 9 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /config/initializers/geocoder.rb: -------------------------------------------------------------------------------- 1 | Geocoder.configure( 2 | # Geocoding options 3 | timeout: 3, # geocoding service timeout (secs) 4 | lookup: :google, # name of geocoding service (symbol) 5 | language: :en, # ISO-639 language code 6 | use_https: true, # use HTTPS for lookup requests? (if supported) 7 | # http_proxy: nil, # HTTP proxy server (user:pass@host:port) 8 | # https_proxy: nil, # HTTPS proxy server (user:pass@host:port) 9 | # api_key: nil, # API key for geocoding service 10 | # cache: nil, # cache object (must respond to #[], #[]=, and #keys) 11 | # cache_prefix: 'geocoder:', # prefix (string) to use for all cache keys 12 | 13 | # Exceptions that should not be rescued by default 14 | # (if you want to implement custom error handling); 15 | # supports SocketError and Timeout::Error 16 | # always_raise: [], 17 | 18 | # Calculation options 19 | # units: :mi, # :km for kilometers or :mi for miles 20 | # distances: :linear # :spherical or :linear 21 | ) 22 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/lograge.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | 3 | unless Rails.env == 'test' 4 | ActiveRecord::Base.logger.level = Logger::INFO 5 | end 6 | 7 | config.lograge.enabled = true 8 | config.lograge.custom_options = lambda do |event| 9 | exceptions = %w(controller action format id) 10 | { 11 | params: event.payload[:params].except(*exceptions) 12 | } 13 | end 14 | end -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/new_framework_defaults_5_1.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains migration options to ease your Rails 5.1 upgrade. 4 | # 5 | # Once upgraded flip defaults one by one to migrate to the new default. 6 | # 7 | # Read the Guide for Upgrading Ruby on Rails for more info on each option. 8 | 9 | # Make `form_with` generate non-remote forms. 10 | Rails.application.config.action_view.form_with_generates_remote_forms = false 11 | 12 | # Unknown asset fallback will return the path passed in when the given 13 | # asset is not present in the asset pipeline. 14 | # Rails.application.config.assets.unknown_asset_fallback = false 15 | -------------------------------------------------------------------------------- /config/initializers/server_config.rb: -------------------------------------------------------------------------------- 1 | config_file = Rails.root.join('server_config.yml') 2 | 3 | if config_file.exist? 4 | relative_root = YAML.load_file(config_file)['relative_root'] 5 | 6 | unless relative_root.blank? 7 | puts "Setting relative_root to: #{relative_root}" 8 | Rails.application.config.relative_url_root = relative_root 9 | Rails.application.config.action_controller.relative_url_root = relative_root 10 | ENV['RAILS_RELATIVE_URL_ROOT'] = relative_root 11 | ENV['ROOT_URL'] = relative_root 12 | end 13 | else 14 | raise StandardError('The config file: server_config.yml was not found. RTFM.') 15 | end -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_workspace_session' 4 | -------------------------------------------------------------------------------- /config/initializers/simple_form_bootstrap.rb: -------------------------------------------------------------------------------- 1 | # Use this setup block to configure all options available in SimpleForm. 2 | SimpleForm.setup do |config| 3 | config.error_notification_class = 'alert alert-danger' 4 | config.button_class = 'btn btn-default' 5 | config.boolean_label_class = nil 6 | 7 | config.wrappers :vertical_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| 8 | b.use :html5 9 | b.use :placeholder 10 | b.optional :maxlength 11 | b.optional :pattern 12 | b.optional :min_max 13 | b.optional :readonly 14 | b.use :label, class: 'control-label' 15 | 16 | b.use :input, class: 'form-control' 17 | b.use :error, wrap_with: { tag: 'span', class: 'help-block' } 18 | b.use :hint, wrap_with: { tag: 'p', class: 'help-block' } 19 | end 20 | 21 | config.wrappers :vertical_file_input, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| 22 | b.use :html5 23 | b.use :placeholder 24 | b.optional :maxlength 25 | b.optional :readonly 26 | b.use :label, class: 'control-label' 27 | 28 | b.use :input 29 | b.use :error, wrap_with: { tag: 'span', class: 'help-block' } 30 | b.use :hint, wrap_with: { tag: 'p', class: 'help-block' } 31 | end 32 | 33 | config.wrappers :vertical_boolean, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| 34 | b.use :html5 35 | b.optional :readonly 36 | 37 | b.wrapper tag: 'div', class: 'checkbox' do |ba| 38 | ba.use :label_input 39 | end 40 | 41 | b.use :error, wrap_with: { tag: 'span', class: 'help-block' } 42 | b.use :hint, wrap_with: { tag: 'p', class: 'help-block' } 43 | end 44 | 45 | config.wrappers :vertical_radio_and_checkboxes, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| 46 | b.use :html5 47 | b.optional :readonly 48 | b.use :label, class: 'control-label' 49 | b.use :input 50 | b.use :error, wrap_with: { tag: 'span', class: 'help-block' } 51 | b.use :hint, wrap_with: { tag: 'p', class: 'help-block' } 52 | end 53 | 54 | config.wrappers :horizontal_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| 55 | b.use :html5 56 | b.use :placeholder 57 | b.optional :maxlength 58 | b.optional :pattern 59 | b.optional :min_max 60 | b.optional :readonly 61 | b.use :label, class: 'col-sm-3 control-label' 62 | 63 | b.wrapper tag: 'div', class: 'col-sm-9' do |ba| 64 | ba.use :input, class: 'form-control' 65 | ba.use :error, wrap_with: { tag: 'span', class: 'help-block' } 66 | ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' } 67 | end 68 | end 69 | 70 | config.wrappers :horizontal_file_input, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| 71 | b.use :html5 72 | b.use :placeholder 73 | b.optional :maxlength 74 | b.optional :readonly 75 | b.use :label, class: 'col-sm-3 control-label' 76 | 77 | b.wrapper tag: 'div', class: 'col-sm-9' do |ba| 78 | ba.use :input 79 | ba.use :error, wrap_with: { tag: 'span', class: 'help-block' } 80 | ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' } 81 | end 82 | end 83 | 84 | config.wrappers :horizontal_boolean, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| 85 | b.use :html5 86 | b.optional :readonly 87 | 88 | b.wrapper tag: 'div', class: 'col-sm-offset-3 col-sm-9' do |wr| 89 | wr.wrapper tag: 'div', class: 'checkbox' do |ba| 90 | ba.use :label_input 91 | end 92 | 93 | wr.use :error, wrap_with: { tag: 'span', class: 'help-block' } 94 | wr.use :hint, wrap_with: { tag: 'p', class: 'help-block' } 95 | end 96 | end 97 | 98 | config.wrappers :horizontal_radio_and_checkboxes, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| 99 | b.use :html5 100 | b.optional :readonly 101 | 102 | b.use :label, class: 'col-sm-3 control-label' 103 | 104 | b.wrapper tag: 'div', class: 'col-sm-9' do |ba| 105 | ba.use :input 106 | ba.use :error, wrap_with: { tag: 'span', class: 'help-block' } 107 | ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' } 108 | end 109 | end 110 | 111 | config.wrappers :inline_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| 112 | b.use :html5 113 | b.use :placeholder 114 | b.optional :maxlength 115 | b.optional :pattern 116 | b.optional :min_max 117 | b.optional :readonly 118 | b.use :label, class: 'sr-only' 119 | 120 | b.use :input, class: 'form-control' 121 | b.use :error, wrap_with: { tag: 'span', class: 'help-block' } 122 | b.use :hint, wrap_with: { tag: 'p', class: 'help-block' } 123 | end 124 | 125 | config.wrappers :multi_select, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| 126 | b.use :html5 127 | b.optional :readonly 128 | b.use :label, class: 'control-label' 129 | b.wrapper tag: 'div', class: 'form-inline' do |ba| 130 | ba.use :input, class: 'form-control' 131 | ba.use :error, wrap_with: { tag: 'span', class: 'help-block' } 132 | ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' } 133 | end 134 | end 135 | # Wrappers for forms and inputs using the Bootstrap toolkit. 136 | # Check the Bootstrap docs (http://getbootstrap.com) 137 | # to learn about the different styles for forms and inputs, 138 | # buttons and other elements. 139 | config.default_wrapper = :vertical_form 140 | config.wrapper_mappings = { 141 | check_boxes: :vertical_radio_and_checkboxes, 142 | radio_buttons: :vertical_radio_and_checkboxes, 143 | file: :vertical_file_input, 144 | boolean: :vertical_boolean, 145 | datetime: :multi_select, 146 | date: :multi_select, 147 | time: :multi_select 148 | } 149 | end 150 | -------------------------------------------------------------------------------- /config/initializers/version.rb: -------------------------------------------------------------------------------- 1 | module PlexBoard 2 | VERSION='0.14.0' 3 | end 4 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at http://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /config/locales/simple_form.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | simple_form: 3 | "yes": 'Yes' 4 | "no": 'No' 5 | required: 6 | text: 'required' 7 | mark: '*' 8 | # You can uncomment the line below if you need to overwrite the whole required html. 9 | # When using html, text and mark won't be used. 10 | # html: '*' 11 | error_notification: 12 | default_message: "Please review the problems below:" 13 | # Examples 14 | # labels: 15 | # defaults: 16 | # password: 'Password' 17 | # user: 18 | # new: 19 | # email: 'E-mail to sign in.' 20 | # edit: 21 | # email: 'E-mail.' 22 | # hints: 23 | # defaults: 24 | # username: 'User name to sign in.' 25 | # password: 'No special characters, please.' 26 | # include_blanks: 27 | # defaults: 28 | # age: 'Rather not say' 29 | # prompts: 30 | # defaults: 31 | # age: 'Select your age' 32 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env puma 2 | 3 | require 'yaml' 4 | threads 0, 32 5 | 6 | if ENV['DOCKER'] 7 | puts 'Detected DOCKER environment.' 8 | daemonize false 9 | bind 'tcp://0.0.0.0:3000' 10 | elsif Gem.win_platform? 11 | puts "Looks like we're running on Windows. Not Daemonizing." 12 | daemonize false 13 | puts 'Redirecting log output! If you run into any issues, check the error logs in: log/production.stderr.log, log/production.stdout.log, and log/production.log' 14 | stdout_redirect 'log/production.stdout.log', 'log/production.stderr.log', true 15 | 16 | web_host = YAML.load_file('server_config.yml')['web_host'] 17 | port = YAML.load_file('server_config.yml')['port'] 18 | 19 | web_host_binding = "tcp://#{web_host}:#{port}" 20 | puts "Binding puma to: #{web_host_binding}" 21 | bind web_host_binding 22 | else 23 | if YAML.load_file('server_config.yml')['use_reverse_proxy'] 24 | puts 'Binding to unix socket because use_reverse_proxy was set to "true"' 25 | bind "unix://#{Dir.pwd}/tmp/sockets/puma.sock" 26 | pidfile "#{Dir.pwd}/tmp/pids/puma.pid" 27 | state_path "#{Dir.pwd}/tmp/pids/puma.state" 28 | activate_control_app 29 | else 30 | web_host = YAML.load_file('server_config.yml')['web_host'] 31 | port = YAML.load_file('server_config.yml')['port'] 32 | 33 | web_host_binding = "tcp://#{web_host}:#{port}" 34 | puts "Binding puma to: #{web_host_binding}" 35 | bind web_host_binding 36 | end 37 | 38 | if ENV['NO_DAEMONIZE'] || ENV['RAILS_ENV'] == 'development' || ENV['RAILS_ENV'] == 'test' 39 | puts 'Detected NO_DAEMONIZE. Not daemonizing.' 40 | daemonize false 41 | else 42 | puts 'Redirecting log output! If you run into any issues, check the error logs in: log/production.stderr.log, log/production.stdout.log, and log/production.log' 43 | puts 'If you need to stop the service run ./stopServer.sh' 44 | stdout_redirect 'log/production.stdout.log', 'log/production.stderr.log', true 45 | pidfile "#{Dir.pwd}/tmp/pids/puma.pid" 46 | state_path "#{Dir.pwd}/tmp/pids/puma.state" 47 | daemonize true 48 | end 49 | end 50 | 51 | # Allow puma to be restarted by `rails restart` command. 52 | plugin :tmp_restart 53 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | # require 'browserlog' 2 | Rails.application.routes.draw do 3 | 4 | root 'services#index' 5 | get 'all_services' => 'services#all_services' 6 | get 'choose_service_type' => 'services#choose_service_type' 7 | get 'about' => 'info#about' 8 | get 'configuration' => 'info#configuration' 9 | get 'plex_services/all_plex_services' 10 | get 'notifications' => 'notifications#notifications' 11 | get 'recently_added' => 'plex_services#recently_added' 12 | 13 | resources :services do 14 | get :online_status 15 | end 16 | 17 | resources :plex_services do 18 | get :now_playing 19 | end 20 | resources :weather 21 | 22 | 23 | # mount Browserlog::Engine => '/logs' 24 | 25 | end 26 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | %w( 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ).each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /db/migrate/20151123041100_create_services.rb: -------------------------------------------------------------------------------- 1 | class CreateServices < ActiveRecord::Migration[4.2] 2 | def change 3 | create_table :services do |t| 4 | t.string :name 5 | t.string :dns_name 6 | t.string :ip 7 | t.string :url 8 | t.boolean :online_status 9 | t.integer :port 10 | t.datetime :last_seen 11 | t.integer :service_flavor_id 12 | t.string :service_flavor_type 13 | 14 | t.timestamps null: false 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20151123041932_create_plex_services.rb: -------------------------------------------------------------------------------- 1 | class CreatePlexServices < ActiveRecord::Migration[4.2] 2 | def change 3 | create_table :plex_services do |t| 4 | t.string :username 5 | t.string :password 6 | t.string :token 7 | 8 | t.timestamps null: false 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20151123042532_create_plex_objects.rb: -------------------------------------------------------------------------------- 1 | class CreatePlexObjects < ActiveRecord::Migration[4.2] 2 | def change 3 | create_table :plex_objects do |t| 4 | t.string :image 5 | t.string :thumb_url 6 | t.text :description 7 | t.string :plex_user_name 8 | t.integer :total_duration 9 | t.integer :progress 10 | 11 | t.timestamps null: false 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20151123051045_create_plex_sessions.rb: -------------------------------------------------------------------------------- 1 | class CreatePlexSessions < ActiveRecord::Migration[4.2] 2 | def change 3 | create_table :plex_sessions do |t| 4 | t.integer :progress 5 | t.integer :total_duration 6 | t.string :plex_user_name 7 | 8 | t.timestamps null: false 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20151124030511_add_plex_service_reference_to_plex_objects.rb: -------------------------------------------------------------------------------- 1 | class AddPlexServiceReferenceToPlexObjects < ActiveRecord::Migration[4.2] 2 | def change 3 | add_reference :plex_objects, :plex_service, index: true, foreign_key: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151124030640_add_index_to_service_flavors_for_services.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToServiceFlavorsForServices < ActiveRecord::Migration[4.2] 2 | def change 3 | add_index :services, :service_flavor_id 4 | add_index :services, :service_flavor_type 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20151124041208_add_polymorphic_columns_to_plex_objects.rb: -------------------------------------------------------------------------------- 1 | class AddPolymorphicColumnsToPlexObjects < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :plex_objects, :plex_object_flavor_id, :integer 4 | add_column :plex_objects, :plex_object_flavor_type, :string 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20151125020147_add_media_title_to_plex_object.rb: -------------------------------------------------------------------------------- 1 | class AddMediaTitleToPlexObject < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :plex_objects, :media_title, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151125020347_add_session_key_to_plex_session.rb: -------------------------------------------------------------------------------- 1 | class AddSessionKeyToPlexSession < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :plex_sessions, :session_key, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151125020455_remove_plex_user_name_from_plex_objects.rb: -------------------------------------------------------------------------------- 1 | class RemovePlexUserNameFromPlexObjects < ActiveRecord::Migration[4.2] 2 | def change 3 | remove_column :plex_objects, :plex_user_name 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151125020909_remove_columns_from_plex_objects.rb: -------------------------------------------------------------------------------- 1 | class RemoveColumnsFromPlexObjects < ActiveRecord::Migration[4.2] 2 | def change 3 | remove_column :plex_objects, :total_duration, :string 4 | remove_column :plex_objects, :progress, :string 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20151125021341_add_index_to_plex_objects.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToPlexObjects < ActiveRecord::Migration[4.2] 2 | def change 3 | add_index :plex_objects, :plex_object_flavor_id 4 | add_index :plex_objects, :plex_object_flavor_type 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20151125021356_add_index_to_plex_sessions.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToPlexSessions < ActiveRecord::Migration[4.2] 2 | def change 3 | add_index :plex_sessions, :session_key 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151205191615_add_added_date_to_plex_recently_added.rb: -------------------------------------------------------------------------------- 1 | class AddAddedDateToPlexRecentlyAdded < ActiveRecord::Migration[4.2] 2 | def change 3 | create_table :plex_recently_addeds do |t| 4 | t.datetime :added_date 5 | 6 | t.timestamps null:false 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20151206220720_add_plex_object_to_plex_service.rb: -------------------------------------------------------------------------------- 1 | class AddPlexObjectToPlexService < ActiveRecord::Migration[4.2] 2 | def change 3 | add_reference :plex_services, :plex_object, index: true, foreign_key: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151208024529_remove_plex_service_from_plex_object.rb: -------------------------------------------------------------------------------- 1 | class RemovePlexServiceFromPlexObject < ActiveRecord::Migration[4.2] 2 | def change 3 | remove_reference :plex_objects, :plex_service, index: true, foreign_key: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151208024559_remove_plex_object_from_plex_service.rb: -------------------------------------------------------------------------------- 1 | class RemovePlexObjectFromPlexService < ActiveRecord::Migration[4.2] 2 | def change 3 | remove_reference :plex_services, :plex_object, index: true, foreign_key: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151208025103_add_plex_service_to_plex_recently_added.rb: -------------------------------------------------------------------------------- 1 | class AddPlexServiceToPlexRecentlyAdded < ActiveRecord::Migration[4.2] 2 | def change 3 | add_reference :plex_recently_addeds, :plex_service, index: true, foreign_key: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151208025136_add_plex_service_to_plex_session.rb: -------------------------------------------------------------------------------- 1 | class AddPlexServiceToPlexSession < ActiveRecord::Migration[4.2] 2 | def change 3 | add_reference :plex_sessions, :plex_service, index: true, foreign_key: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151227040353_add_uuid_to_plex_recently_added.rb: -------------------------------------------------------------------------------- 1 | class AddUuidToPlexRecentlyAdded < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :plex_recently_addeds, :uuid, :string 4 | add_index :plex_recently_addeds, :uuid 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20151230044756_add_auth_successful_to_plex_service.rb: -------------------------------------------------------------------------------- 1 | class AddAuthSuccessfulToPlexService < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :plex_services, :auth_successful, :boolean 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160121032317_rename_auth_successful_to_api_error.rb: -------------------------------------------------------------------------------- 1 | class RenameAuthSuccessfulToApiError < ActiveRecord::Migration[4.2] 2 | def change 3 | rename_column :plex_services, :auth_successful, :api_error 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160124024211_remove_username_from_plex_service.rb: -------------------------------------------------------------------------------- 1 | class RemoveUsernameFromPlexService < ActiveRecord::Migration[4.2] 2 | def change 3 | remove_column :plex_services, :username 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160124024307_remove_password_from_plex_service.rb: -------------------------------------------------------------------------------- 1 | class RemovePasswordFromPlexService < ActiveRecord::Migration[4.2] 2 | def change 3 | remove_column :plex_services, :password 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160124033003_remove_api_error_from_plex_service.rb: -------------------------------------------------------------------------------- 1 | class RemoveApiErrorFromPlexService < ActiveRecord::Migration[4.2] 2 | def change 3 | remove_column :plex_services, :api_error 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160318030058_create_server_loads.rb: -------------------------------------------------------------------------------- 1 | class CreateServerLoads < ActiveRecord::Migration[4.2] 2 | def change 3 | create_table :server_loads do |t| 4 | t.string :name 5 | t.integer :order 6 | 7 | t.timestamps null: false 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20160507224352_create_weathers.rb: -------------------------------------------------------------------------------- 1 | class CreateWeathers < ActiveRecord::Migration[4.2] 2 | def change 3 | create_table :weathers do |t| 4 | t.string :api_key 5 | t.float :latitude 6 | t.float :longitude 7 | 8 | t.timestamps null: false 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20160507224801_add_address_to_weather.rb: -------------------------------------------------------------------------------- 1 | class AddAddressToWeather < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :weathers, :address, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160510020651_add_units_to_weather.rb: -------------------------------------------------------------------------------- 1 | class AddUnitsToWeather < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :weathers, :units, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160513022103_change_weather_units_from_string_to_text.rb: -------------------------------------------------------------------------------- 1 | class ChangeWeatherUnitsFromStringToText < ActiveRecord::Migration[4.2] 2 | def change 3 | change_column :weathers, :units, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160707043046_add_city_to_weather.rb: -------------------------------------------------------------------------------- 1 | class AddCityToWeather < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :weathers, :city, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160707043058_add_state_to_weather.rb: -------------------------------------------------------------------------------- 1 | class AddStateToWeather < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :weathers, :state, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170325204752_remove_online_status_from_service.rb: -------------------------------------------------------------------------------- 1 | class RemoveOnlineStatusFromService < ActiveRecord::Migration[4.2] 2 | def change 3 | remove_column :services, :online_status 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170329020117_add_stream_type_to_plex_session.rb: -------------------------------------------------------------------------------- 1 | class AddStreamTypeToPlexSession < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :plex_sessions, :stream_type, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170404205426_remove_last_seen_from_services.rb: -------------------------------------------------------------------------------- 1 | class RemoveLastSeenFromServices < ActiveRecord::Migration[4.2] 2 | def change 3 | remove_column :services, :last_seen 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170428012623_add_null_false_to_database_fields.rb: -------------------------------------------------------------------------------- 1 | class AddNullFalseToDatabaseFields < ActiveRecord::Migration[5.0] 2 | def up 3 | PlexSession.destroy_all 4 | PlexRecentlyAdded.destroy_all 5 | change_column :plex_objects, :media_title, :string, null: false 6 | change_column :plex_objects, :thumb_url, :string, null: false 7 | change_column :plex_recently_addeds, :uuid, :string, null: false 8 | change_column :plex_recently_addeds, :added_date, :datetime, null: false 9 | change_column :plex_sessions, :progress, :integer, null: false 10 | change_column :plex_sessions, :total_duration, :integer, null: false 11 | change_column :plex_sessions, :session_key, :string, null: false 12 | change_column :plex_sessions, :stream_type, :string, null: false 13 | change_column :services, :name, :string, null: false 14 | change_column :services, :url, :string, null: false 15 | change_column :services, :port, :integer, null: false 16 | end 17 | 18 | def down 19 | change_column :plex_objects, :media_title, :string 20 | change_column :plex_objects, :thumb_url, :string 21 | change_column :plex_recently_addeds, :uuid, :string 22 | change_column :plex_recently_addeds, :added_date, :datetime 23 | change_column :plex_sessions, :progress, :integer 24 | change_column :plex_sessions, :total_duration, :integer 25 | change_column :plex_sessions, :session_key, :string 26 | change_column :plex_sessions, :stream_type, :string 27 | change_column :services, :name, :string 28 | change_column :services, :url, :string 29 | change_column :services, :port, :integer 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 20170428012623) do 14 | 15 | create_table "plex_objects", force: :cascade do |t| 16 | t.string "image" 17 | t.string "thumb_url", null: false 18 | t.text "description" 19 | t.datetime "created_at", null: false 20 | t.datetime "updated_at", null: false 21 | t.integer "plex_object_flavor_id" 22 | t.string "plex_object_flavor_type" 23 | t.string "media_title", null: false 24 | t.index ["plex_object_flavor_id"], name: "index_plex_objects_on_plex_object_flavor_id" 25 | t.index ["plex_object_flavor_type"], name: "index_plex_objects_on_plex_object_flavor_type" 26 | end 27 | 28 | create_table "plex_recently_addeds", force: :cascade do |t| 29 | t.datetime "added_date", null: false 30 | t.datetime "created_at", null: false 31 | t.datetime "updated_at", null: false 32 | t.integer "plex_service_id" 33 | t.string "uuid", null: false 34 | t.index ["plex_service_id"], name: "index_plex_recently_addeds_on_plex_service_id" 35 | t.index ["uuid"], name: "index_plex_recently_addeds_on_uuid" 36 | end 37 | 38 | create_table "plex_services", force: :cascade do |t| 39 | t.string "token" 40 | t.datetime "created_at", null: false 41 | t.datetime "updated_at", null: false 42 | end 43 | 44 | create_table "plex_sessions", force: :cascade do |t| 45 | t.integer "progress", null: false 46 | t.integer "total_duration", null: false 47 | t.string "plex_user_name" 48 | t.datetime "created_at", null: false 49 | t.datetime "updated_at", null: false 50 | t.string "session_key", null: false 51 | t.integer "plex_service_id" 52 | t.string "stream_type", null: false 53 | t.index ["plex_service_id"], name: "index_plex_sessions_on_plex_service_id" 54 | t.index ["session_key"], name: "index_plex_sessions_on_session_key" 55 | end 56 | 57 | create_table "server_loads", force: :cascade do |t| 58 | t.string "name" 59 | t.integer "order" 60 | t.datetime "created_at", null: false 61 | t.datetime "updated_at", null: false 62 | end 63 | 64 | create_table "services", force: :cascade do |t| 65 | t.string "name", null: false 66 | t.string "dns_name" 67 | t.string "ip" 68 | t.string "url", null: false 69 | t.integer "port", null: false 70 | t.integer "service_flavor_id" 71 | t.string "service_flavor_type" 72 | t.datetime "created_at", null: false 73 | t.datetime "updated_at", null: false 74 | t.index ["service_flavor_id"], name: "index_services_on_service_flavor_id" 75 | t.index ["service_flavor_type"], name: "index_services_on_service_flavor_type" 76 | end 77 | 78 | create_table "weathers", force: :cascade do |t| 79 | t.string "api_key" 80 | t.float "latitude" 81 | t.float "longitude" 82 | t.datetime "created_at", null: false 83 | t.datetime "updated_at", null: false 84 | t.string "address" 85 | t.text "units" 86 | t.string "city" 87 | t.string "state" 88 | end 89 | 90 | end 91 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | -------------------------------------------------------------------------------- /example.server_config.yml: -------------------------------------------------------------------------------- 1 | # The settings in this file will change how the Rails app behaves. Most users shouldn't have to change these settings. 2 | 3 | 4 | # This should be set to whatever IP address your host will respond to requests from. 5 | # Most *nix users can leave this at 0.0.0.0, but if you have issues then you'll need to bind to a specific interface. 6 | # run: ifconfig to find out what IP addresses your system has configured. 7 | # Also 0.0.0.0 may be invalid on some systems like Windows and some Raspberry Pi distros (or when IPv6 is enabled sometimes) 8 | # so if you have issues then set it to something else 9 | # Example: 192.168.0.10 10 | # Default: 0.0.0.0 11 | web_host: 0.0.0.0 12 | 13 | 14 | # This is the port that Rails will bind to. If you want to run the app on default http port 80 then you'll probably need 15 | # to run it as root. 16 | # Example: 80 17 | # Default: 3000 18 | port: 3000 19 | 20 | 21 | # Most users won't run the app behind a reverse proxy, if you don't know that that is then leave this alone. 22 | # Example: true 23 | # Default: false 24 | use_reverse_proxy: false 25 | 26 | 27 | # relative_root should be set to the path that the app is under. So "/plexdashboard" (omit the trailing "/") would 28 | # mount the app under (using 127.0.0.1 as an example web_host) http://127.0.0.1/plexdashboard 29 | 30 | # !!!!!!!!!!!! If you change relative_root you will need to regenerate your assets by running serverSetup.sh !!!!!!!!!!!! 31 | 32 | # Example: /plexdashboard 33 | # Default: 34 | relative_root: -------------------------------------------------------------------------------- /example.windows.server_config.yml: -------------------------------------------------------------------------------- 1 | # The settings in this file will change how the Rails app behaves. Most users shouldn't have to change these settings. 2 | 3 | 4 | # This should be set to whatever IP address your host will respond to requests from. 5 | # Most *nix users can leave this at 0.0.0.0, but if you have issues then you'll need to bind to a specific interface. 6 | # run: ifconfig to find out what IP addresses your system has configured. 7 | # Also 0.0.0.0 may be invalid on some systems like Windows and some Raspberry Pi distros (or when IPv6 is enabled sometimes) 8 | # so if you have issues then set it to something else 9 | # Example: 192.168.0.10 10 | # Default: 127.0.0.1 11 | web_host: 127.0.0.1 12 | 13 | 14 | # This is the port that Rails will bind to. If you want to run the app on default http port 80 then you'll probably need 15 | # to run it as root. 16 | # Example: 80 17 | # Default: 3000 18 | port: 3000 19 | 20 | 21 | # Most users won't run the app behind a reverse proxy, if you don't know that that is then leave this alone. 22 | # Example: true 23 | # Default: false 24 | use_reverse_proxy: false 25 | 26 | 27 | # relative_root should be set to the path that the app is under. So "/plexdashboard" (omit the trailing "/") would 28 | # mount the app under (using 127.0.0.1 as an example web_host) http://127.0.0.1/plexdashboard 29 | 30 | # !!!!!!!!!!!! If you change relative_root you will need to regenerate your assets by running serverSetup.sh !!!!!!!!!!!! 31 | 32 | # Example: /plexdashboard 33 | # Default: 34 | relative_root: -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/lib/assets/.keep -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/lib/tasks/.keep -------------------------------------------------------------------------------- /lib/tasks/version.rake: -------------------------------------------------------------------------------- 1 | desc 'update the version numbers of the readme file. Use like this: rails "bump_version[0.13.2]" Yes the quotes do matter' 2 | task :bump_version, [:version_number] => :environment do |_, args| 3 | VERSION_REGEX = /\d+\.\d+\.\d+/ 4 | files=%w(config/initializers/version.rb) 5 | version_number = args[:version_number] 6 | puts "New version number is: #{version_number}" 7 | if VERSION_REGEX.match(version_number) 8 | files.each do |file_name| 9 | file = File.read(file_name) 10 | puts "\nRead in the contents:\n#{file}" 11 | new_contents = file.gsub(VERSION_REGEX, version_number) 12 | File.open(file_name, 'w') do |f| 13 | puts "\nWriting out:\n#{new_contents}" 14 | f.puts(new_contents) 15 | end 16 | end 17 | else 18 | puts "The version that was input: #{version_number} did not match the expected pattern defined by the regex: #{VERSION_REGEX}" 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/templates/erb/scaffold/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%%= simple_form_for(@<%= singular_table_name %>) do |f| %> 2 | <%%= f.error_notification %> 3 | 4 |
    5 | <%- attributes.each do |attribute| -%> 6 | <%%= f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %> %> 7 | <%- end -%> 8 |
    9 | 10 |
    11 | <%%= f.button :submit %> 12 |
    13 | <%% end %> 14 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The page you were looking for doesn't exist.

    62 |

    You may have mistyped the address or the page may have moved.

    63 |
    64 |

    If you are the application owner check the logs for more information.

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The change you wanted was rejected.

    62 |

    Maybe you tried to change something you didn't have access to.

    63 |
    64 |

    If you are the application owner check the logs for more information.

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    We're sorry, but something went wrong.

    62 |
    63 |

    If you are the application owner check the logs for more information.

    64 |
    65 | 66 | 67 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/public/favicon.ico -------------------------------------------------------------------------------- /public/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/public/placeholder.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /serverSetup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | case "$(uname -s)" in 5 | 6 | Darwin) 7 | OS="Mac OSX" 8 | ;; 9 | 10 | Linux) 11 | OS="Linux" 12 | ;; 13 | 14 | CYGWIN*|MINGW32*|MSYS*) 15 | OS="Windows" 16 | ;; 17 | 18 | *) 19 | OS="Unknown" 20 | ;; 21 | esac 22 | 23 | printf "\nThe current operating system was detected as: ${OS}." 24 | 25 | cd "$(dirname "$0")" 26 | if [[ "$OS" != "Windows" ]]; then 27 | printf "\nStopping server if it's running.\n" 28 | source stopServer.sh 29 | fi 30 | 31 | printf "\nDestroying tmp folder to clear caches and leftover pidfiles.\n" 32 | rm -rf tmp 33 | 34 | printf "\nDestroying public/images to clear leftover images.\n" 35 | rm -rf public/images 36 | 37 | HOME_RVM=$HOME/.rvm/scripts/rvm 38 | ROOT_RVM="/usr/local/rvm/scripts/rvm" 39 | UBUNTU_RVM="/usr/share/rvm/bin/rvm" 40 | 41 | RUBY_VERSION="2.4.1" 42 | 43 | # Load RVM into a shell session *as a function* 44 | if [[ -s ${HOME_RVM} ]] ; then 45 | # First try to load from a user install 46 | source ${HOME_RVM} \ 47 | && printf "\nRVM successfully loaded from $HOME_RVM\n" \ 48 | && printf "\nInstalling ruby-$RUBY_VERSION if it isn't already installed. This could take a while...\n" \ 49 | && rvm install ruby-${RUBY_VERSION} \ 50 | && rvm use gemset ruby-${RUBY_VERSION}@plexdashboard 51 | 52 | elif [[ -s ${UBUNTU_RVM} ]] ; then 53 | # Second try to use RVM in an Ubuntu install 54 | printf "\nRVM should already be loaded from $UBUNTU_RVM\n" \ 55 | && printf "\nInstalling ruby-$RUBY_VERSION if it isn't already installed. This could take a while...\n" \ 56 | && rvm install ruby-${RUBY_VERSION} \ 57 | && rvm use gemset ruby-${RUBY_VERSION}@plexdashboard 58 | 59 | elif [[ -s ${ROOT_RVM} ]] ; then 60 | # Then try to load from a root install 61 | source ${ROOT_RVM} \ 62 | && printf "\nRVM successfully loaded from $ROOT_RVM\n" \ 63 | && printf "\nInstalling ruby-$RUBY_VERSION if it isn't already installed. This could take a while...\n" \ 64 | && rvm install ruby-${RUBY_VERSION} \ 65 | && rvm use gemset ruby-${RUBY_VERSION}@plexdashboard 66 | 67 | else 68 | if [[ "$OS" == "Windows" ]]; then 69 | printf "\nUsing system Ruby because we're on Windows...\n" 70 | else 71 | printf "\nWARNING: A RVM installation was not found. Did you follow the instructions correctly? Attempting to use system Ruby...\n" 72 | fi 73 | fi 74 | 75 | INSTALLED_RUBY_VERSION="$(ruby -v)" 76 | 77 | if ! [[ ${INSTALLED_RUBY_VERSION} =~ 2\.[3-4]\.[0-9]+ ]]; then 78 | printf "\nERROR: The required version of Ruby was not installed. This application will not work with any Ruby < 2.3.x" 79 | printf "\nFound: ${INSTALLED_RUBY_VERSION}\n" 80 | exit 1 81 | fi 82 | 83 | gem install bundler rake --no-ri --no-rdoc 84 | 85 | printf "\nInstalling Gems. This could take a while depending on how powerful your CPU is...\n" 86 | bundle install --without development test 87 | 88 | EXAMPLE_WINDOWS_CONFIG_FILE="example.windows.server_config.yml" 89 | EXAMPLE_CONFIG_FILE="example.server_config.yml" 90 | SERVER_CONFIG_FILE="server_config.yml" 91 | SECRETS="config/secrets.yml" 92 | SECRET="$(rake secret)" 93 | BOGUS="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 94 | 95 | printf "\nRemoving old secrets.yml file if it exists. Creating a fresh one with new secrets.\n" 96 | if [ -f ${SECRETS} ] ; then 97 | rm ${SECRETS} 98 | fi 99 | 100 | echo -e "development:\n secret_key_base:" ${BOGUS} "\n" >> ${SECRETS} 101 | echo -e "test:\n secret_key_base:" ${BOGUS} "\n" >> ${SECRETS} 102 | echo -e "production:\n secret_key_base:" ${SECRET} >> ${SECRETS} 103 | 104 | if [ ! -f ${SERVER_CONFIG_FILE} ]; then 105 | if [[ "$OS" == "Windows" ]]; then 106 | printf "\nSince windows is being used we'll use 127.0.0.1:3000 as the default host." 107 | printf "\nYou'll need to configure server_config.yml in order to accept traffic from external hosts.\n" 108 | EXAMPLE_CONFIG_FILE=${EXAMPLE_WINDOWS_CONFIG_FILE} 109 | fi 110 | 111 | if cp ${EXAMPLE_CONFIG_FILE} ${SERVER_CONFIG_FILE} 2>&1; then 112 | printf "\nCreated server_config.yml since it didn't exist." 113 | fi 114 | else 115 | printf "\nserver_config.yml exists, not creating." 116 | fi 117 | 118 | printf "\nCreating and setting up the database for production.\n" 119 | bundle exec rake db:create RAILS_ENV=production 120 | bundle exec rake db:migrate RAILS_ENV=production 121 | 122 | printf "\nCompiling assets. This could take a while depending on CPU power...\n" 123 | bundle exec rake assets:clobber assets:precompile RAILS_ENV=production 124 | 125 | printf "\nCreating images directory.\n" 126 | mkdir -p public/images 127 | 128 | printf "\nFinished. Run ./startServer.sh to start the server!\n\n" 129 | -------------------------------------------------------------------------------- /startServer.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | cd "$(dirname "$0")" 5 | 6 | case "$(uname -s)" in 7 | 8 | Darwin) 9 | OS="Mac OSX" 10 | ;; 11 | 12 | Linux) 13 | OS="Linux" 14 | ;; 15 | 16 | CYGWIN*|MINGW32*|MSYS*) 17 | OS="Windows" 18 | ;; 19 | 20 | *) 21 | OS="Unknown" 22 | ;; 23 | esac 24 | 25 | if [[ "$OS" == "Windows" ]]; then 26 | printf "\nNot creating pid files etc. since we're on Windows. Starting puma.\n" 27 | bundle exec puma -e production -C config/puma.rb config.ru 28 | else 29 | HOME_RVM=$HOME/.rvm/scripts/rvm 30 | ROOT_RVM="/usr/local/rvm/scripts/rvm" 31 | UBUNTU_RVM="/usr/share/rvm/bin/rvm" 32 | 33 | # Load RVM into a shell session *as a function* 34 | if [[ -s ${HOME_RVM} ]] ; then 35 | # First try to load from a user install 36 | source ${HOME_RVM} \ 37 | && printf "\nRVM successfully loaded from $HOME_RVM\n" 38 | 39 | elif [[ -s ${UBUNTU_RVM} ]] ; then 40 | # Second try to load from an Ubuntu install 41 | source ${UBUNTU_RVM} \ 42 | && printf "\nRVM successfully loaded from $UBUNTU_RVM\n" 43 | 44 | elif [[ -s ${ROOT_RVM} ]] ; then 45 | # Then try to load from a root install 46 | source ${ROOT_RVM} \ 47 | && printf "\nRVM successfully loaded from $ROOT_RVM\n" 48 | 49 | else 50 | printf "\nWARNING: An RVM installation was not found. Did you follow the instructions correctly? Attempting to use system Ruby...\n" 51 | fi 52 | 53 | printf "\nIf you run into any issues, check the error logs in: log/production.stderr.log, log/production.stdout.log, and log/production.log\n\n" 54 | 55 | mkdir -p tmp/pids \ 56 | && mkdir -p tmp/sockets \ 57 | && touch tmp/pids/puma.pid \ 58 | && touch tmp/pids/puma.state \ 59 | && touch tmp/sockets/puma.socket \ 60 | && exec bundle exec puma -e production -C config/puma.rb config.ru 61 | 62 | fi -------------------------------------------------------------------------------- /stopServer.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | case "$(uname -s)" in 5 | 6 | Darwin) 7 | OS="Mac OSX" 8 | ;; 9 | 10 | Linux) 11 | OS="Linux" 12 | ;; 13 | 14 | CYGWIN*|MINGW32*|MSYS*) 15 | OS="Windows" 16 | ;; 17 | 18 | *) 19 | OS="Unknown" 20 | ;; 21 | esac 22 | 23 | if [[ "$OS" == "Windows" ]]; then 24 | printf "\nNo puma daemon to kill since we're on Windows.\n" 25 | else 26 | cd "$(dirname "$0")" 27 | PIDFILE="tmp/pids/puma.pid" 28 | STATEFILE="tmp/pids/puma.state" 29 | 30 | if [ -s ${PIDFILE} ] 31 | then 32 | PUMA_PID="$(cat ${PIDFILE})" 33 | 34 | printf "Attempting to kill puma with PID: $PUMA_PID" \ 35 | && kill -9 ${PUMA_PID} \ 36 | && printf "\nServer stopped.\n" \ 37 | && rm ${PIDFILE} \ 38 | && rm ${STATEFILE} 39 | else 40 | printf "It seems Puma isn't running right now. Nothing to kill.\n" 41 | fi 42 | fi 43 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/test/controllers/.keep -------------------------------------------------------------------------------- /test/controllers/info_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class InfoControllerTest < ActionController::TestCase 4 | 5 | test "should get configuration" do 6 | get :configuration 7 | assert_response :success 8 | end 9 | 10 | test "should get about" do 11 | get :about 12 | assert_response :success 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /test/controllers/notifications_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'timeout' 3 | 4 | class NotificationsControllerTest < ActionController::TestCase 5 | # test 'Notifications returns 200 OK' do 6 | # begin 7 | # Timeout.timeout(1) do 8 | # get :notifications 9 | # end 10 | # rescue Timeout::Error 11 | # assert_response :success 12 | # end 13 | # end 14 | end 15 | -------------------------------------------------------------------------------- /test/controllers/plex_services_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PlexServicesControllerTest < ActionController::TestCase 4 | 5 | test 'should get edit' do 6 | get :edit, params: {id: @plex_service_one.id} 7 | assert_response :success 8 | assert_select 'title', "Plex-Board | Edit #{@plex_service_one.service.name}" 9 | end 10 | 11 | test 'should get show' do 12 | get :show, params: {id: @plex_service_one.id} 13 | assert_response :success 14 | assert_select 'title', "Plex-Board | #{@plex_service_one.service.name}" 15 | end 16 | 17 | test 'should get all_plex_services' do 18 | get :all_plex_services 19 | assert_response :success 20 | assert_select 'title', 'Plex-Board | All Plex Services' 21 | end 22 | 23 | test 'should get new' do 24 | get :new 25 | assert_response :success 26 | assert_select 'title', 'Plex-Board | New Plex Service' 27 | end 28 | 29 | test 'should update PlexService' do 30 | patch :update, params: {id: @plex_service_one.id, plex_service: {username: 'user', password: 'newpass'}} 31 | assert_redirected_to plex_service_path(assigns(:plex_service)) 32 | end 33 | 34 | test 'should create plex_service' do 35 | assert_difference('PlexService.count') do 36 | post :create, params: {plex_service: {username: 'user', password: 'pass', token: 'garbage'}} 37 | end 38 | 39 | assert_redirected_to plex_service_path(assigns(:plex_service)) 40 | end 41 | 42 | test 'should destroy plex_service' do 43 | assert_difference('PlexService.count', -1) do 44 | assert delete :destroy, params: {id: @plex_service_one.id} 45 | end 46 | assert_redirected_to root_url 47 | end 48 | 49 | end 50 | -------------------------------------------------------------------------------- /test/controllers/services_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | class ServicesControllerTest < ActionController::TestCase 3 | 4 | test 'should get index' do 5 | get :index 6 | assert_response :success 7 | assert_not_nil assigns(:services) 8 | assert_select 'title', 'Plex-Board' 9 | end 10 | 11 | test 'should get all_services' do 12 | get :all_services 13 | assert_response :success 14 | assert_not_nil assigns(:services) 15 | assert_select 'title', 'Plex-Board | All Services' 16 | end 17 | 18 | test 'should get choose_service_type' do 19 | get :choose_service_type 20 | assert_response :success 21 | assert_select 'title', 'Plex-Board | Choose Service Type' 22 | end 23 | 24 | test 'should get new' do 25 | get :new 26 | assert_response :success 27 | assert_select 'title', 'Plex-Board | New Service' 28 | end 29 | 30 | test 'should create service' do 31 | assert_difference('Service.count') do 32 | post :create, params: {service: { name: 'test_create', ip: '172.111.3.1', port: 80, dns_name: 'test_create', url: 'test_create'}} 33 | end 34 | 35 | assert_redirected_to service_path(assigns(:service)) 36 | end 37 | 38 | test 'should show service' do 39 | get :show, params: {id: @generic_service_one.id} 40 | assert_response :success 41 | assert_select 'title', "Plex-Board | #{@generic_service_one.name}" 42 | end 43 | 44 | test 'should get edit' do 45 | get :edit, params:{id: @generic_service_one.id} 46 | assert_response :success 47 | assert_select 'title', "Plex-Board | Edit #{@generic_service_one.name}" 48 | end 49 | 50 | test 'should update service' do 51 | patch :update, params: {id: @generic_service_one.id, service: {name: 'test2', ip: '172.123.1.1', dns_name: 'test', url: 'test' }} 52 | assert_redirected_to service_path(assigns(:service)) 53 | end 54 | 55 | test 'should destroy service' do 56 | assert_difference('Service.count', -1) do 57 | assert delete :destroy, params: {id: @generic_service_one.id} 58 | end 59 | assert_redirected_to root_url 60 | end 61 | 62 | # test 'Bad plex service port wont break page load' do 63 | # get :index 64 | # assert_response :success 65 | # assert_requested(:get, 'https://plex5:32400/status/sessions') 66 | # @plex_service_one.service.update(port: 32401) 67 | # get :index 68 | # assert_response :success 69 | # assert_requested(:get, 'https://plex5:32401/status/sessions') 70 | # end 71 | 72 | # This might take some tinkering 73 | # test 'should show service as online' do 74 | 75 | # end 76 | 77 | 78 | end -------------------------------------------------------------------------------- /test/controllers/weather_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class WeatherControllerTest < ActionController::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/fabricators/fabrication.rb: -------------------------------------------------------------------------------- 1 | Fabrication.configure do |config| 2 | config.fabricator_path = 'test/fabricators' 3 | config.path_prefix = Rails.root 4 | config.sequence_start = 0 5 | end 6 | -------------------------------------------------------------------------------- /test/fabricators/plex_object_fabricator.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: plex_objects 4 | # 5 | # id :integer not null, primary key 6 | # image :string 7 | # thumb_url :string not null 8 | # description :text 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # plex_object_flavor_id :integer 12 | # plex_object_flavor_type :string 13 | # media_title :string not null 14 | # 15 | 16 | require 'test_helper' 17 | 18 | Fabricator(:plex_object) do 19 | media_title { Faker::Book.title } #Book title is the closest thing I found 20 | thumb_url '/test_images/placeholder.png' 21 | end 22 | -------------------------------------------------------------------------------- /test/fabricators/plex_recently_added_fabricator.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: plex_recently_addeds 4 | # 5 | # id :integer not null, primary key 6 | # added_date :datetime not null 7 | # created_at :datetime not null 8 | # updated_at :datetime not null 9 | # plex_service_id :integer 10 | # uuid :string not null 11 | # 12 | 13 | require 'test_helper' 14 | 15 | Fabricator(:plex_recently_added) do 16 | added_date { Faker::Date.backward(2) } 17 | uuid { SecureRandom.uuid } 18 | end 19 | -------------------------------------------------------------------------------- /test/fabricators/plex_service_fabricator.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: plex_services 4 | # 5 | # id :integer not null, primary key 6 | # token :string 7 | # created_at :datetime not null 8 | # updated_at :datetime not null 9 | # 10 | 11 | require 'test_helper' 12 | 13 | Fabricator(:plex_service) do 14 | transient :ps_token, sessions: 0, pras: 0 15 | token { |attrs| attrs[:ps_token] ? attrs[:ps_token] : Proc.new { rand(36**20).to_s(36) }.call } #generates random 20 character token 16 | service 17 | plex_sessions(count: 1) 18 | plex_recently_addeds(count: 1) 19 | end 20 | -------------------------------------------------------------------------------- /test/fabricators/plex_session_fabricator.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: plex_sessions 4 | # 5 | # id :integer not null, primary key 6 | # progress :integer not null 7 | # total_duration :integer not null 8 | # plex_user_name :string 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # session_key :string not null 12 | # plex_service_id :integer 13 | # stream_type :string not null 14 | # 15 | 16 | require 'test_helper' 17 | 18 | Fabricator(:plex_session) do 19 | plex_user_name { Faker::Internet::user_name } 20 | session_key { Faker::Number } 21 | stream_type { %w(Stream Transcode '').sample } 22 | plex_object 23 | end 24 | -------------------------------------------------------------------------------- /test/fabricators/server_load_fabricator.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: server_loads 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # created_at :datetime not null 8 | # updated_at :datetime not null 9 | # service_host_id :integer 10 | # 11 | 12 | require 'test_helper' 13 | Fabricator(:server_load) do 14 | name "MyString" 15 | order 1 16 | end 17 | -------------------------------------------------------------------------------- /test/fabricators/service_fabricator.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: services 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # dns_name :string 8 | # ip :string 9 | # url :string not null 10 | # port :integer not null 11 | # service_flavor_id :integer 12 | # service_flavor_type :string 13 | # created_at :datetime not null 14 | # updated_at :datetime not null 15 | # 16 | 17 | require 'test_helper' 18 | 19 | Fabricator(:service) do 20 | transient :s_name, :s_url, :s_dns_name, :s_ip, :s_port 21 | name { |attrs| attrs[:s_name] ? attrs[:s_name] : Faker::App.name + rand(10000).to_s } 22 | url { |attrs| attrs[:s_url] ? attrs[:s_url] : Faker::Internet.url } 23 | dns_name { |attrs| attrs[:s_dns_name] ? attrs[:s_dns_name] : Faker::Internet.domain_name + rand(10000).to_s } 24 | ip { |attrs| attrs[:s_ip] ? attrs[:s_ip] : Faker::Internet.ip_v4_address } 25 | port { |attrs| attrs[:s_port] ? attrs[:s_port] : Faker::Number.between(1, 65535) } 26 | end 27 | -------------------------------------------------------------------------------- /test/fabricators/weather_fabricator.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: weathers 4 | # 5 | # id :integer not null, primary key 6 | # api_key :string 7 | # latitude :float 8 | # longitude :float 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # address :string 12 | # units :text 13 | # city :string 14 | # state :string 15 | # 16 | 17 | require 'test_helper' 18 | 19 | Fabricator(:weather) do 20 | transient :units 21 | api_key { Proc.new { rand(36**32).to_s(36) }.call } 22 | latitude { Proc.new { rand(-90.0..90.0).to_s }.call } 23 | longitude { Proc.new { rand(-180.0..180.0).to_s }.call } 24 | units { |attrs| attrs[:units] ? attrs[:units] : {'US Customary Units': 'us'} } 25 | # transient :address, :latitude, :longitude, :api_key 26 | # api_key { |attrs| attrs[:api_key] ? attrs[:api_key] : Proc.new { rand(36**32).to_s(36) }.call } 27 | # latitude { |attrs| attrs[:latitude] ? attrs[:latitude] : Proc.new { rand(-90.0..90.0).to_s }.call } 28 | # longitude { |attrs| attrs[:longitude] ? attrs[:longitude] : Proc.new { rand(-180.0..180.0).to_s }.call } 29 | end 30 | 31 | 32 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/test/fixtures/.keep -------------------------------------------------------------------------------- /test/fixtures/JSON/google_location_api.json: -------------------------------------------------------------------------------- 1 | { 2 | "results": [ 3 | { 4 | "address_components": [ 5 | { 6 | "long_name": "2300", 7 | "short_name": "2300", 8 | "types": [ 9 | "street_number" 10 | ] 11 | }, 12 | { 13 | "long_name": "Traverwood Drive", 14 | "short_name": "Traverwood Dr", 15 | "types": [ 16 | "route" 17 | ] 18 | }, 19 | { 20 | "long_name": "Logan", 21 | "short_name": "Logan", 22 | "types": [ 23 | "neighborhood", 24 | "political" 25 | ] 26 | }, 27 | { 28 | "long_name": "Ann Arbor", 29 | "short_name": "Ann Arbor", 30 | "types": [ 31 | "locality", 32 | "political" 33 | ] 34 | }, 35 | { 36 | "long_name": "Washtenaw County", 37 | "short_name": "Washtenaw County", 38 | "types": [ 39 | "administrative_area_level_2", 40 | "political" 41 | ] 42 | }, 43 | { 44 | "long_name": "Michigan", 45 | "short_name": "MI", 46 | "types": [ 47 | "administrative_area_level_1", 48 | "political" 49 | ] 50 | }, 51 | { 52 | "long_name": "United States", 53 | "short_name": "US", 54 | "types": [ 55 | "country", 56 | "political" 57 | ] 58 | }, 59 | { 60 | "long_name": "48105", 61 | "short_name": "48105", 62 | "types": [ 63 | "postal_code" 64 | ] 65 | } 66 | ], 67 | "formatted_address": "2300 Traverwood Dr, Ann Arbor, MI 48105, USA", 68 | "geometry": { 69 | "location": { 70 | "lat": 42.306642, 71 | "lng": -83.71466199999999 72 | }, 73 | "location_type": "ROOFTOP", 74 | "viewport": { 75 | "northeast": { 76 | "lat": 42.3079909802915, 77 | "lng": -83.71331301970849 78 | }, 79 | "southwest": { 80 | "lat": 42.3052930197085, 81 | "lng": -83.7160109802915 82 | } 83 | } 84 | }, 85 | "place_id": "ChIJaUsRICysPIgRtu8h-gpZ_pY", 86 | "types": [ 87 | "street_address" 88 | ] 89 | } 90 | ], 91 | "status": "OK" 92 | } -------------------------------------------------------------------------------- /test/fixtures/JSON/plex_recently_added_movie.json: -------------------------------------------------------------------------------- 1 | { 2 | "MediaContainer": { 3 | "size": 50, 4 | "allowSync": false, 5 | "identifier": "com.plexapp.plugins.library", 6 | "mediaTagPrefix": "/system/bundle/media/flags/", 7 | "mediaTagVersion": 1481837967, 8 | "mixedParents": true, 9 | "Metadata": [ 10 | { 11 | "allowSync": true, 12 | "librarySectionID": 1, 13 | "librarySectionTitle": "Movies", 14 | "librarySectionUUID": "d370c316-f79b-4810-b0cc-fad49bef2af7", 15 | "ratingKey": "49724", 16 | "key": "/library/metadata/49724", 17 | "studio": "Fox 2000 Pictures", 18 | "type": "movie", 19 | "title": "Hidden Figures", 20 | "contentRating": "PG", 21 | "summary": "The incredible untold story of Katherine G. Johnson, Dorothy Vaughan and Mary Jackson - brilliant African-American women working at NASA, who served as the brains behind one of the greatest operations in history: the launch of astronaut John Glenn into orbit, a stunning achievement that restored the nation's confidence, turned around the Space Race, and galvanized the world. The visionary trio crossed all gender and race lines to inspire generations to dream big.", 22 | "rating": 9.3, 23 | "audienceRating": 9.4, 24 | "year": 2017, 25 | "tagline": "Meet the women you don't know, behind the mission you do.", 26 | "thumb": "/library/metadata/49724/thumb/1490327440", 27 | "art": "/library/metadata/49724/art/1490327440", 28 | "duration": 7603062, 29 | "originallyAvailableAt": "2017-01-06", 30 | "addedAt": 1490327405, 31 | "updatedAt": 1490327440, 32 | "audienceRatingImage": "rottentomatoes://image.rating.upright", 33 | "chapterSource": "media", 34 | "primaryExtraKey": "/library/metadata/49725", 35 | "ratingImage": "rottentomatoes://image.rating.certified", 36 | "Media": [ 37 | { 38 | "videoResolution": "1080", 39 | "id": 135653, 40 | "duration": 7603062, 41 | "bitrate": 11111, 42 | "width": 1920, 43 | "height": 808, 44 | "aspectRatio": 2.35, 45 | "audioChannels": 6, 46 | "audioCodec": "dca", 47 | "videoCodec": "h264", 48 | "container": "mkv", 49 | "videoFrameRate": "24p", 50 | "audioProfile": "dts", 51 | "videoProfile": "high", 52 | "Part": [ 53 | { 54 | "id": 136430, 55 | "key": "/library/parts/136430/1490335125/file.mkv", 56 | "duration": 7603062, 57 | "file": "/mnt/PLEX-MEDIA/Movies/Hidden Figures (2016)/Hidden Figures.mkv", 58 | "size": 10559697658, 59 | "audioProfile": "dts", 60 | "container": "mkv", 61 | "deepAnalysisVersion": "2", 62 | "indexes": "sd", 63 | "requiredBandwidths": "42252,29243,22270,16875,12600,12600,12600,12600", 64 | "videoProfile": "high" 65 | } 66 | ] 67 | } 68 | ], 69 | "Genre": [ 70 | { 71 | "tag": "Biography" 72 | }, 73 | { 74 | "tag": "History" 75 | }, 76 | { 77 | "tag": "Drama" 78 | } 79 | ], 80 | "Director": [ 81 | { 82 | "tag": "Theodore Melfi" 83 | } 84 | ], 85 | "Writer": [ 86 | { 87 | "tag": "Allison Schroeder" 88 | }, 89 | { 90 | "tag": "Theodore Melfi" 91 | } 92 | ], 93 | "Country": [ 94 | { 95 | "tag": "USA" 96 | } 97 | ], 98 | "Role": [ 99 | { 100 | "tag": "Taraji P. Henson" 101 | }, 102 | { 103 | "tag": "Octavia Spencer" 104 | }, 105 | { 106 | "tag": "Janelle Monae" 107 | } 108 | ] 109 | } 110 | ] 111 | } 112 | } -------------------------------------------------------------------------------- /test/fixtures/JSON/plex_recently_added_tv_show.json: -------------------------------------------------------------------------------- 1 | { 2 | "MediaContainer": { 3 | "size": 1, 4 | "allowSync": false, 5 | "identifier": "com.plexapp.plugins.library", 6 | "mediaTagPrefix": "/system/bundle/media/flags/", 7 | "mediaTagVersion": 1481837967, 8 | "mixedParents": true, 9 | "Metadata": [ 10 | { 11 | "allowSync": true, 12 | "librarySectionID": 1, 13 | "librarySectionTitle": "TV Shows", 14 | "librarySectionUUID": "b58dc19b-7a20-475b-adca-a4e26cb340bd", 15 | "ratingKey": "48181", 16 | "key": "/library/metadata/48181/children", 17 | "parentRatingKey": "40506", 18 | "type": "season", 19 | "title": "Season 2", 20 | "parentKey": "/library/metadata/40506", 21 | "parentTitle": "The Expanse", 22 | "parentSummary": "Two hundred years in the future, in a fully colonized solar system, police detective Josephus Miller is given the assignment to find a missing young woman, Julie Mao. Joining his task is James Holden, former first officer of an ice freighter who is involved in an incident caused by the tense relationship between Earth, Mars and the Belt. Far from their struggles in space, United Nations executive Chrisjen Avasarala works to prevent war between Earth and Mars. Soon the three find out that the missing woman is connected to a vast conspiracy that threatens all humanity.", 23 | "summary": "", 24 | "index": 2, 25 | "parentIndex": 1, 26 | "thumb": "/library/metadata/48181/thumb/1490253646", 27 | "art": "/library/metadata/40506/art/1490253646", 28 | "parentThumb": "/library/metadata/40506/thumb/1490253646", 29 | "parentTheme": "/library/metadata/40506/theme/1490253646", 30 | "leafCount": 8, 31 | "viewedLeafCount": 0, 32 | "addedAt": 1490253614, 33 | "updatedAt": 1490253646 34 | } 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/fixtures/JSON/sign_in.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": { 3 | "email": "user@example.com", 4 | "joined_at": "2013-02-10T02:34:58Z", 5 | "username": "Generic_User", 6 | "title": "Generic_User", 7 | "authentication_token": "zV75NzEnTA1migSb21ze", 8 | "subscription": { 9 | "active": true, 10 | "status": "Active", 11 | "plan": "lifetime", 12 | "features": [ 13 | "pass", 14 | "sync", 15 | "cloudsync", 16 | "home" 17 | ] 18 | }, 19 | "roles": { 20 | "roles": [ 21 | "plexpass" 22 | ] 23 | }, 24 | "entitlements": [ 25 | "all", 26 | "roku", 27 | "android", 28 | "xbox_one", 29 | "xbox_360", 30 | "windows", 31 | "windows_phone", 32 | "ios" 33 | ], 34 | "confirmed_at": "2013-02-10T15:15:14Z", 35 | "forum_id": 0 36 | } 37 | } -------------------------------------------------------------------------------- /test/fixtures/images/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/test/fixtures/images/placeholder.png -------------------------------------------------------------------------------- /test/fixtures/plex404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | The page you were looking for doesn't exist (404) 8 | 9 | 10 | 11 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
    29 | 30 |
    31 | 32 |

    Oops; a 404 error...

    33 | 34 |

    That's "page not found" in human language.

    35 | 36 |

    You may have mistyped the address or the page may have moved. 37 |
    38 | 39 | Let us take you to the main page and we can start all over again, shall we? 40 | 41 |

    42 | 43 |
    44 | 45 |
    46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /test/fixtures/plex_objects.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: plex_objects 4 | # 5 | # id :integer not null, primary key 6 | # image :string 7 | # thumb_url :string not null 8 | # description :text 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # plex_object_flavor_id :integer 12 | # plex_object_flavor_type :string 13 | # media_title :string not null 14 | # 15 | 16 | plex_object_session_1: 17 | image: placeholder.png 18 | media_title: Movie1 19 | description: . 20 | thumb_url: /library/metadata/38501/thumb/1444964730 21 | plex_object_flavor: plex_service_w1sess_session_1 (PlexSession) 22 | 23 | plex_object_session_2: 24 | image: placeholder.png 25 | media_title: Movie1 26 | description: . 27 | thumb_url: /library/metadata/38501/thumb/1444964730 28 | plex_object_flavor: plex_service_w2sess_session_1 (PlexSession) 29 | 30 | plex_object_session_3: 31 | image: placeholder.png 32 | media_title: Movie1 33 | description: . 34 | thumb_url: /library/metadata/38501/thumb/1444964730 35 | plex_object_flavor: plex_service_w2sess_session_2 (PlexSession) 36 | 37 | plex_object_recently_added_1: 38 | image: placeholder.png 39 | media_title: Movie1 40 | description: . 41 | thumb_url: /library/metadata/38501/thumb/1444964730 42 | plex_object_flavor: plex_service_w1ra_pra_1 (PlexRecentlyAdded) 43 | -------------------------------------------------------------------------------- /test/fixtures/plex_recently_addeds.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: plex_recently_addeds 4 | # 5 | # id :integer not null, primary key 6 | # added_date :datetime not null 7 | # created_at :datetime not null 8 | # updated_at :datetime not null 9 | # plex_service_id :integer 10 | # uuid :string not null 11 | # 12 | 13 | plex_service_w1ra_pra_1: 14 | plex_service: plex_service_with_one_recently_added 15 | added_date: <%= 5.day.ago.to_s(:db) %> 16 | uuid: c58dc19b-7a20-475b-adca-a4e26cb340bd 17 | -------------------------------------------------------------------------------- /test/fixtures/plex_services.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: plex_services 4 | # 5 | # id :integer not null, primary key 6 | # token :string 7 | # created_at :datetime not null 8 | # updated_at :datetime not null 9 | # 10 | 11 | plex_service_one: 12 | token: k3qRS5pJuWFz8U9tJp1d 13 | 14 | plex_service_with_no_token: 15 | token: 16 | 17 | plex_service_with_one_session: 18 | token: k3qRS5pJuWFz8U9tJp1d 19 | 20 | plex_service_with_two_sessions: 21 | token: k3qRS5pJuWFz8U9tJp1d 22 | 23 | plex_service_with_one_recently_added: 24 | token: k3qRS5pJuWFz8U9tJp1d 25 | -------------------------------------------------------------------------------- /test/fixtures/plex_sessions.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: plex_sessions 4 | # 5 | # id :integer not null, primary key 6 | # progress :integer not null 7 | # total_duration :integer not null 8 | # plex_user_name :string 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # session_key :string not null 12 | # plex_service_id :integer 13 | # stream_type :string not null 14 | # 15 | 16 | plex_service_w1sess_session_1: 17 | progress: 1 18 | total_duration: 100 19 | session_key: 1 20 | stream_type: Transcode 21 | plex_user_name: Generic_User 22 | plex_service: plex_service_with_one_session 23 | 24 | plex_service_w2sess_session_1: 25 | progress: 1 26 | total_duration: 100 27 | session_key: 1 28 | stream_type: Transcode 29 | plex_user_name: Generic_User 30 | plex_service: plex_service_with_two_sessions 31 | 32 | plex_service_w2sess_session_2: 33 | progress: 1 34 | total_duration: 100 35 | session_key: 2 36 | stream_type: Stream 37 | plex_user_name: Generic_User 38 | plex_service: plex_service_with_two_sessions 39 | 40 | -------------------------------------------------------------------------------- /test/fixtures/services.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: services 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # dns_name :string 8 | # ip :string 9 | # url :string not null 10 | # port :integer not null 11 | # service_flavor_id :integer 12 | # service_flavor_type :string 13 | # created_at :datetime not null 14 | # updated_at :datetime not null 15 | # 16 | 17 | generic_service_one: 18 | name: Test Service One 19 | ip: 127.0.0.1 20 | dns_name: localhost1 21 | port: 80 22 | url: http://localhost1 23 | 24 | generic_service_two: 25 | name: Test Service Two 26 | ip: 127.0.0.2 27 | dns_name: localhost2 28 | port: 80 29 | url: http://localhost2 30 | 31 | plex_1: 32 | name: plex_service_one 33 | ip: 127.0.0.3 34 | dns_name: plex3 35 | port: 32400 36 | url: http://127.0.0.3 37 | service_flavor: plex_service_one (PlexService) 38 | 39 | plex_2: 40 | name: plex_service_no_token 41 | ip: 127.0.0.4 42 | dns_name: plex4 43 | port: 32400 44 | url: http://127.0.0.4 45 | service_flavor: plex_service_with_no_token (PlexService) 46 | 47 | plex_3: 48 | name: plex_service_with_one_session 49 | ip: 127.0.0.5 50 | dns_name: plex5 51 | port: 32400 52 | url: http://127.0.0.5 53 | service_flavor: plex_service_with_one_session (PlexService) 54 | 55 | plex_4: 56 | name: plex_service_with_two_sessions 57 | ip: 127.0.0.6 58 | dns_name: plex6 59 | port: 32400 60 | url: http://127.0.0.6 61 | service_flavor: plex_service_with_two_sessions (PlexService) 62 | 63 | plex_5: 64 | name: plex_service_with_one_recently_added 65 | ip: 127.0.0.7 66 | dns_name: plex7 67 | port: 32400 68 | url: http://127.0.0.7 69 | service_flavor: plex_service_with_one_recently_added (PlexService) 70 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/test/helpers/.keep -------------------------------------------------------------------------------- /test/helpers/webmock_helper.rb: -------------------------------------------------------------------------------- 1 | module WebmockHelper 2 | require 'webmock' 3 | 4 | HEADERS = {"Cache-Control" => "no-cache", "Connection" => "Keep-Alive", 5 | "Content-Type" => "application/json", "Keep-Alive" => "timeout=20", 6 | "X-Plex-Protocol" => "1.0"} 7 | 8 | AUTH_HEADERS = { "Content-Type" => "application/json; charset=utf-8", "Access-Control-Max-Age" => 86400 } 9 | 10 | TOKEN = 'zV75NzEnTA1migSb21ze' 11 | 12 | WebMock.disable_net_connect!(:allow_localhost => true) 13 | 14 | WebMock.stub_request(:post, "https://user:pass@my.plexapp.com/users/sign_in.json"). 15 | with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate', 'User-Agent'=>'Ruby', 'X-Plex-Client-Identifier'=>'Plex-Board'}). 16 | to_return(:status => 201, :body => File.open(Rails.root.join 'test/fixtures/JSON/', "sign_in.json").read, :headers => AUTH_HEADERS) 17 | 18 | WebMock.stub_request(:get, "https://plex5:32400/status/sessions"). 19 | with(:headers => {'Accept'=>'application/json', 'Accept-Encoding'=>'gzip, deflate', 'User-Agent'=>'Ruby', 'X-Plex-Token'=>TOKEN}). 20 | to_return(:status => 200, :body => File.open(Rails.root.join 'test/fixtures/JSON/', "plex_one_session.json").read, :headers => HEADERS) 21 | 22 | WebMock.stub_request(:get, "https://plex5updated:32400/status/sessions"). 23 | with(:headers => {'Accept'=>'application/json', 'Accept-Encoding'=>'gzip, deflate', 'User-Agent'=>'Ruby', 'X-Plex-Token'=>TOKEN}). 24 | to_return(:status => 200, :body => File.open(Rails.root.join 'test/fixtures/JSON/', "plex_one_session_updated_viewOffset.json").read, :headers => HEADERS) 25 | 26 | WebMock.stub_request(:get, "https://plexnosessions:32400/status/sessions"). 27 | with(:headers => {'Accept'=>'application/json', 'Accept-Encoding'=>'gzip, deflate', 'User-Agent'=>'Ruby', 'X-Plex-Token'=>TOKEN}). 28 | to_return(:status => 200, :body => "{\"_elementType\": \"MediaContainer\",\"_children\": []}", :headers => HEADERS) 29 | 30 | WebMock.stub_request(:get, "https://plex6:32400/status/sessions"). 31 | with(:headers => {'Accept'=>'application/json', 'Accept-Encoding'=>'gzip, deflate', 'User-Agent'=>'Ruby', 'X-Plex-Token'=>TOKEN}). 32 | to_return(:status => 200, :body => File.open(Rails.root.join 'test/fixtures/JSON/', "plex_two_sessions.json").read, :headers => HEADERS) 33 | 34 | WebMock.stub_request(:get, /https:\/\/plex(.*?):32400\/library\/metadata\/(\d*)\/thumb\/(\d*$)/). 35 | with(:headers => {'Accept'=>'image/jpeg', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby', 'X-Plex-Token'=>TOKEN}). 36 | to_return(:status => 200, :body => File.open(Rails.root.join 'test/fixtures/images/', 'placeholder.png').read, :headers => {"Content-Type" => "image/jpeg"}) 37 | 38 | end -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/test/integration/.keep -------------------------------------------------------------------------------- /test/integration/behavior_test.rb: -------------------------------------------------------------------------------- 1 | # require 'test_helper' 2 | # 3 | # class BehaviorTest < ActionDispatch::IntegrationTest 4 | # # These tests will ensure that the UI functions as designed. 5 | # 6 | # def teardown 7 | # super 8 | # Capybara.reset_sessions! 9 | # Capybara.use_default_driver 10 | # end 11 | # 12 | # # # Setup: At least one service in the online state. 13 | # # # Steps: 14 | # # # Action 1: The service will be taken offline 15 | # # # Result 1: The UI button will be updated by js to reflect the change 16 | # # test 'A service taken offline will have its button updated to reflect the change' do 17 | # # skip "Unfinished test" 18 | # # visit '/' 19 | # # end 20 | # # 21 | # # # Setup: At least one service in the offline state. 22 | # # # Steps: 23 | # # # Action 1: The service will be put back online 24 | # # # Result 1: The UI button will be updated by js to reflect the change 25 | # # test 'A newly onlined service will have its button updated to reflect the change' do 26 | # # skip "Unfinished test" 27 | # # visit '/' 28 | # # end 29 | # # 30 | # # # Setup: A Plex service in the online state. 31 | # # # Steps: 32 | # # # Action 1: A new session will started 33 | # # # Result 1: The UI will have the new session added to the carousel 34 | # # test 'A newly started Plex Session will show in the carousel' do 35 | # # skip "Unfinished test" 36 | # # visit '/' 37 | # # end 38 | # # 39 | # # # Setup: A Plex service in the online state. A session is in progress. 40 | # # # Steps: 41 | # # # Action 1: The session will be stopped 42 | # # # Result 1: The UI will have the session removed from the carousel 43 | # # test 'A stoppped Plex Session will be removed from the carousel' do 44 | # # skip "Unfinished test" 45 | # # visit '/' 46 | # # end 47 | # # 48 | # # # Setup: A Plex service in the online state. 49 | # # # Steps: 50 | # # # Action 1: Add content to the Plex server 51 | # # # Result 1: The UI will have the newly added content added to the carousel 52 | # # test 'A newly added Plex Media Item will show in the carousel' do 53 | # # skip "Unfinished test" 54 | # # visit '/' 55 | # # end 56 | # 57 | # 58 | # # # Setup: A Plex service in the online state. PS has a bad token. 59 | # # # Steps: 60 | # # # Action 1: update_plex_data from Plex server 61 | # # # Result 1: The server returns a 401. The UI displays an error message. 62 | # # test 'An expired token will raise an error in the UI' do 63 | # # 64 | # # end 65 | # 66 | # 67 | # end 68 | -------------------------------------------------------------------------------- /test/integration/site_layout_test.rb: -------------------------------------------------------------------------------- 1 | # require 'test_helper' 2 | # 3 | # 4 | # class SiteLayoutTest < ActionDispatch::IntegrationTest 5 | # keywords = %w(Plex-Board Services Config Status About) 6 | # 7 | # def teardown 8 | # super 9 | # Capybara.reset_sessions! 10 | # Capybara.use_default_driver 11 | # end 12 | # 13 | # 14 | # # Capybara::Screenshot.screenshot_and_save_page 15 | # 16 | # 17 | # # test 'check index for all expected keywords' do 18 | # # skip 'This test ruins everything.' 19 | # # # begin 20 | # # VCR.insert_cassette 'check_index_for_all_expected_keywords' 21 | # # # skip 'Need to figure out fixtures first, and get vcr captures...' 22 | # # visit '/' 23 | # # keywords.each do |word| 24 | # # assert page.has_content?(word), "#{word} was missing from \"/\"" 25 | # # end 26 | # # assert page.has_title?('Plex-Board'), '"Plex-Board" was not the title for "/"' 27 | # # # ensure 28 | # # Capybara::Screenshot.screenshot_and_save_page 29 | # # VCR.eject_cassette 30 | # # # end 31 | # # end 32 | # 33 | # # test 'Check carousel for carousel items' do 34 | # # visit '/' 35 | # # assert page.has_selector?('#carousel-inner'), '#Carousel-inner was missing from page' 36 | # # end 37 | # end 38 | -------------------------------------------------------------------------------- /test/integration_test_config_files/example_service_test_config.yml: -------------------------------------------------------------------------------- 1 | # This file is used to describe your real-life local plex server so it can be used for testing. 2 | # The actual service_test_config.yml file is not intended to be used after cassettes recordings of 3 | # interactions with a Plex Server are added to the test/fixtures/cassettes/ folder 4 | 5 | plex_server_1: 6 | ip: 127.0.0.1 7 | port: 32400 8 | url: https://127.0.0.1:32400/web 9 | username: bob 10 | password: 1234 11 | 12 | # Future usages could look like this: 13 | couchpotato_server_1: 14 | ip: 127.0.0.1 15 | port: 5050 16 | url: https://127.0.0.1:5050 17 | -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/test/mailers/.keep -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/test/models/.keep -------------------------------------------------------------------------------- /test/models/plex_object_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: plex_objects 4 | # 5 | # id :integer not null, primary key 6 | # image :string 7 | # thumb_url :string not null 8 | # description :text 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # plex_object_flavor_id :integer 12 | # plex_object_flavor_type :string 13 | # media_title :string not null 14 | # 15 | 16 | require 'test_helper' 17 | 18 | class PlexObjectTest < ActiveSupport::TestCase 19 | 20 | test 'plex_objects should be valid' do 21 | assert @plex_object_session_2.valid? 22 | assert @plex_object_session_1.valid? 23 | assert @plex_object_session_3.valid? 24 | assert @plex_object_recently_added_1.valid? 25 | end 26 | 27 | # test 'plex_object with no plex_session should not be valid' do 28 | # skip('This test needs more thought') 29 | # @plex_object_session_1.plex_object_flavor = nil 30 | # assert_not @plex_object_session_1.valid?, 'Plex object should not be valid if there is no parent plex session' 31 | # end 32 | 33 | test 'plex_object media_title must not be blank' do 34 | @plex_object_session_1.media_title = nil 35 | assert_not @plex_object_session_1.valid?, 'media_title should not be allowed to be nil' 36 | @plex_object_session_1.media_title = '' 37 | assert_not @plex_object_session_1.valid?, 'media_title should not be allowed to be an empty string' 38 | end 39 | 40 | test 'user_name should not be allowed to be whitespace only' do 41 | @plex_object_session_1.media_title = ' ' 42 | assert_not @plex_object_session_1.valid?, 'media_title should not be allowed to be a whitespace string' 43 | end 44 | 45 | test 'plex_object should successfully retrieve image' do 46 | # Temporary until I figure out why this directory doesn't get created in proper time. 47 | unless File.directory? Rails.root.join 'test/test_images' 48 | skip 'The test/test_images folder does not exist. This test will probably fail.' 49 | end 50 | assert @plex_object_session_1.delete_thumbnail, 'Deleting thumbnail failed' 51 | assert_not File.file?(Rails.root.join 'test/test_images', (@plex_object_session_1.id.to_s + '.jpeg')), 52 | 'Image file should not be present' 53 | assert_not_nil @plex_object_session_1.get_img, 'Image file was not retrieved' 54 | assert File.file?(Rails.root.join 'test/test_images', (@plex_object_session_1.id.to_s + '.jpeg')), 55 | 'Image file was not found' 56 | end 57 | 58 | test 'destroying a plex_object will delete the associated image' do 59 | # Temporary until I figure out why this directory doesn't get created in proper time. 60 | unless File.directory? Rails.root.join 'test/test_images' 61 | skip 'The test/test_images folder does not exist. This test will probably fail.' 62 | end 63 | assert_not_nil @plex_object_session_1.get_img, 'Image file was not retrieved' 64 | assert File.file?(Rails.root.join 'test/test_images', (@plex_object_session_1.id.to_s + '.jpeg')), 65 | 'Image file was not found' 66 | assert @plex_object_session_1.destroy, 'Destroying the session failed' 67 | assert_not File.file?(Rails.root.join 'test/test_images', (@plex_object_session_1.id.to_s + '.jpeg')), 68 | 'The image file was not deleted' 69 | end 70 | 71 | # test 'destroying a plex_object with a nil image will be successful' do 72 | # ps = Fabricate(:plex_service) 73 | # # ps.plex_recently_added.plex_object.image = nil 74 | # 75 | # # assert(ps.plex_recently_added.destroy, 'The PlexObject was not destroyed') 76 | # end 77 | end 78 | -------------------------------------------------------------------------------- /test/models/plex_recently_added_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: plex_recently_addeds 4 | # 5 | # id :integer not null, primary key 6 | # added_date :datetime not null 7 | # created_at :datetime not null 8 | # updated_at :datetime not null 9 | # plex_service_id :integer 10 | # uuid :string not null 11 | # 12 | 13 | require 'test_helper' 14 | 15 | class PlexRecentlyAddedTest < ActiveSupport::TestCase 16 | test 'plex_recently_addeds should be valid' do 17 | assert @plex_service_w1ra_pra_1.valid?, 'plex_service_w1ra_pra_1 was not valid' 18 | end 19 | 20 | test 'added_date should be present' do 21 | @plex_service_w1ra_pra_1.added_date = '' 22 | @plex_service_w1ra_pra_1.reload 23 | assert @plex_service_w1ra_pra_1.valid?, 'PRA was valid with blank added_date' 24 | @plex_service_w1ra_pra_1.added_date = nil 25 | @plex_service_w1ra_pra_1.reload 26 | assert @plex_service_w1ra_pra_1.valid?, 'PRA was valid with nil added_date' 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/models/plex_session_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: plex_sessions 4 | # 5 | # id :integer not null, primary key 6 | # progress :integer not null 7 | # total_duration :integer not null 8 | # plex_user_name :string 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # session_key :string not null 12 | # plex_service_id :integer 13 | # stream_type :string not null 14 | # 15 | 16 | require 'test_helper' 17 | 18 | class PlexSessionTest < ActiveSupport::TestCase 19 | 20 | test 'sessions should be valid' do 21 | assert @plex_service_w1sess_session_1.valid?, 'Session_one was invalid' 22 | assert @plex_service_w2sess_session_1.valid?, 'Session_two was invalid' 23 | assert @plex_service_w2sess_session_2.valid?, 'Session_three was invalid' 24 | end 25 | 26 | test 'plex_session should have progress' do 27 | plex_session = @plex_service_w1sess_session_1 28 | plex_session.progress = nil 29 | assert_not plex_session.valid? 30 | end 31 | 32 | test 'plex_session should have total_duration' do 33 | plex_session = @plex_service_w1sess_session_1 34 | plex_session.total_duration = nil 35 | assert_not plex_session.valid? 36 | end 37 | 38 | test 'plex_user_name should be present' do 39 | @plex_service_w1sess_session_1.plex_user_name = nil 40 | assert_not @plex_service_w1sess_session_1.valid?, 'plex_user_name should not be nil' 41 | @plex_service_w1sess_session_1.plex_user_name = '' 42 | assert_not @plex_service_w1sess_session_1.valid?, 'plex_user_name should not be empty string' 43 | end 44 | 45 | test 'user_name should not be allowed to be whitespace only' do 46 | @plex_service_w1sess_session_1.plex_user_name = ' ' 47 | assert_not @plex_service_w1sess_session_1.valid?, 'plex_user_name should not be valid with whitespace string' 48 | end 49 | 50 | test 'session should be unique' do 51 | duplicate_session = @plex_service_w1sess_session_1.dup 52 | @plex_service_w1sess_session_1.save 53 | assert_not duplicate_session.valid?, 'Duplicate session should not be valid' 54 | end 55 | 56 | test 'session will correctly identify stream_type Stream' do 57 | assert_equal('Stream', PlexSession.determine_stream_type('copy'), 58 | 'copy videoDecision was not correctly translated to Stream') 59 | assert_equal('Stream', PlexSession.determine_stream_type('COPY'), 60 | 'COPY videoDecision was not correctly translated to Stream') 61 | end 62 | 63 | test 'session will correctly identify stream_type Transcode' do 64 | assert_equal('Transcode', PlexSession.determine_stream_type('transcode'), 65 | 'transcode videoDecision was not correctly translated to Transcode') 66 | assert_equal('Transcode', PlexSession.determine_stream_type('TRANSCODE'), 67 | 'TRANSCODE videoDecision was not correctly translated to Transcode') 68 | end 69 | 70 | test 'session will correctly default stream_type nil or empty to Stream' do 71 | assert_equal('Stream', PlexSession.determine_stream_type(''), 72 | 'empty string videoDecision was not correctly translated to Stream') 73 | assert_equal('Stream', PlexSession.determine_stream_type(nil), 74 | 'nil videoDecision was not correctly translated to Stream') 75 | end 76 | 77 | #Tests for Plex integration 78 | 79 | 80 | end 81 | -------------------------------------------------------------------------------- /test/models/server_load_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: server_loads 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # created_at :datetime not null 8 | # updated_at :datetime not null 9 | # service_host_id :integer 10 | # 11 | 12 | require 'test_helper' 13 | 14 | class ServerLoadTest < ActiveSupport::TestCase 15 | # test "the truth" do 16 | # assert true 17 | # end 18 | end 19 | -------------------------------------------------------------------------------- /test/models/weather_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: weathers 4 | # 5 | # id :integer not null, primary key 6 | # api_key :string 7 | # latitude :float 8 | # longitude :float 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # address :string 12 | # units :text 13 | # city :string 14 | # state :string 15 | # 16 | 17 | require 'test_helper' 18 | 19 | class WeatherTest < ActiveSupport::TestCase 20 | test 'weather with lat and long is valid' do 21 | weather = Fabricate(:weather) 22 | assert weather.valid?, "Weather with lat: #{weather.latitude} and long: #{weather.longitude} was not valid." 23 | end 24 | 25 | test 'a provided address will be converted to lat and long' do 26 | weather = Fabricate.build(:weather) 27 | weather.latitude = nil 28 | weather.longitude = nil 29 | weather.address = '2300 Traverwood Dr, Ann Arbor, MI 48105' 30 | weather.save! 31 | assert_equal 42.306642, weather.latitude 32 | assert_equal -83.71466199999999, weather.longitude 33 | assert weather.valid?, "Weather with address: \"#{weather.address}\" should have been valid" 34 | end 35 | 36 | test 'weather with only latitude is not valid' do 37 | weather = Fabricate.build(:weather) 38 | weather.longitude = nil 39 | weather.latitude = 35.2341 40 | assert_not weather.valid?, 'Weather should not be valid if only supplied latitude.' 41 | end 42 | 43 | test 'weather with only longitude is not valid' do 44 | weather = Fabricate.build(:weather) 45 | weather.longitude = 88.0515 46 | weather.latitude = nil 47 | assert_not weather.valid?, 'Weather should not be valid if only supplied longitude.' 48 | end 49 | 50 | test 'weather with no api key is not valid' do 51 | weather = Fabricate.build(:weather) 52 | weather.api_key = nil 53 | assert_not weather.valid?, 'Weather should not be valid if there is no api key.' 54 | end 55 | 56 | test 'an invalid address will throw a RecordInvalid exception' do 57 | expected_message = 'Fetching the precise location failed. Please check that the address is valid.' 58 | weather = Fabricate.build(:weather) 59 | weather.longitude = nil 60 | weather.latitude = nil 61 | weather.address = 'This is not a valid address' 62 | 63 | exception = assert_raises(ActiveRecord::RecordInvalid) { weather.save! } 64 | assert exception.message.include?(expected_message), 65 | "The exception didn't mention the address error message as expected. \nGot: '#{exception.message}'\nwhen expecting: '#{expected_message}'." 66 | end 67 | 68 | test 'an nil address will throw a RecordInvalid exception' do 69 | expected_message = 'Fetching the precise location failed. Please check that the address is valid.' 70 | weather = Fabricate.build(:weather) 71 | weather.longitude = nil 72 | weather.latitude = nil 73 | weather.address = nil 74 | 75 | exception = assert_raises(ActiveRecord::RecordInvalid) { weather.save! } 76 | assert exception.message.include?(expected_message), 77 | "The exception didn't mention the address error message as expected. \nGot: '#{exception.message}'\nwhen expecting: '#{expected_message}'." 78 | end 79 | 80 | test 'weather can be retrieved with valid parameters' do 81 | weather = Fabricate(:weather) 82 | weather.latitude = 42.306642 83 | weather.longitude = -83.71466199999999 84 | weather.api_key = '0ca1d9fc73742b2dca0dc2643d89994d' 85 | assert_not_nil weather.get_weather, 'weather.get_weather should not return nil for a valid weather object' 86 | assert_equal 42.306642, weather.get_weather['latitude'] 87 | assert_equal -83.71466199999999, weather.get_weather['longitude'] 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /travisSetup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mv example.server_config.yml server_config.yml 3 | SECRETS="config/secrets.yml" 4 | SECRET="$(bundle exec rake secret)" 5 | BOGUS="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 6 | # rm $SECRETS 7 | echo -e "development:\n secret_key_base: " ${BOGUS} "\n" >> ${SECRETS} 8 | echo -e "test:\n secret_key_base: " ${BOGUS} "\n" >> ${SECRETS} 9 | echo -e "production:\n secret_key_base: " ${SECRET} >> ${SECRETS} 10 | # rm db/*sqlite3 11 | mkdir -p log/ 12 | touch log/test.log 13 | bundle exec rake db:create 14 | bundle exec rake db:migrate 15 | mkdir -p public/images 16 | mkdir -p test/test_images 17 | 18 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | case "$(uname -s)" in 5 | 6 | Darwin) 7 | OS="Mac OSX" 8 | ;; 9 | 10 | Linux) 11 | OS="Linux" 12 | ;; 13 | 14 | CYGWIN*|MINGW32*|MSYS*) 15 | OS="Windows" 16 | ;; 17 | 18 | *) 19 | OS="Unknown" 20 | ;; 21 | esac 22 | 23 | cd "$(dirname "$0")" 24 | if [[ "$OS" == "Windows" ]]; then 25 | printf "\nSince we're on Windows the local Gemfile.lock will be deleted prevent cross-platform conflicts.\n" 26 | rm Gemfile.lock 27 | fi 28 | 29 | printf "\nPulling down the latest for the current branch.\n" 30 | git pull 31 | printf "\nExecuting the serverSetup.sh script to recompile assets and update dependencies.\n" 32 | source serverSetup.sh 33 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/vendor/assets/javascripts/.keep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scytherswings/Plex-Board/c83fd13223d33b6565fd30fd1c237549800708c9/vendor/assets/stylesheets/.keep --------------------------------------------------------------------------------