diff --git a/Capfile b/Capfile
new file mode 100644
index 000000000..d04de115b
--- /dev/null
+++ b/Capfile
@@ -0,0 +1,4 @@
+load 'deploy' if respond_to?(:namespace) # cap2 differentiator
+Dir['vendor/gems/*/recipes/*.rb','vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
+
+load 'config/deploy' # remove this line to skip loading any of the default tasks
\ No newline at end of file
diff --git a/Gemfile b/Gemfile
index ce006c104..3db971454 100644
--- a/Gemfile
+++ b/Gemfile
@@ -9,10 +9,12 @@ group :test do
end
group :assets do
-
+ gem "uglifier"
+ gem "uglifier-rails"
+ gem "therubyracer"
end
-
-gem "rails", "3.1.0.rc5"
+
+gem "rails", "3.1.0.rc8"
gem "pg"
gem "memcache-client", :require => "memcache"
gem "imagesize", :require => "image_size"
@@ -24,4 +26,4 @@ gem "mechanize"
gem "nokogiri"
gem "meta_search", :git => "git://github.com/ernie/meta_search.git"
gem "silent-postgres"
-gem "whenever", :require => false
\ No newline at end of file
+gem "whenever", :require => false
diff --git a/Gemfile.lock b/Gemfile.lock
index 352b3e780..867dd2dfc 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -17,48 +17,51 @@ GEM
remote: http://gemcutter.org/
specs:
aaronh-chronic (0.3.9)
- actionmailer (3.1.0.rc5)
- actionpack (= 3.1.0.rc5)
+ actionmailer (3.1.0.rc8)
+ actionpack (= 3.1.0.rc8)
mail (~> 2.3.0)
- actionpack (3.1.0.rc5)
- activemodel (= 3.1.0.rc5)
- activesupport (= 3.1.0.rc5)
+ actionpack (3.1.0.rc8)
+ activemodel (= 3.1.0.rc8)
+ activesupport (= 3.1.0.rc8)
builder (~> 3.0.0)
erubis (~> 2.7.0)
i18n (~> 0.6)
- rack (~> 1.3.1)
- rack-cache (~> 1.0.2)
- rack-mount (~> 0.8.1)
- rack-test (~> 0.6.0)
- sprockets (~> 2.0.0.beta.12)
- activemodel (3.1.0.rc5)
- activesupport (= 3.1.0.rc5)
- bcrypt-ruby (~> 2.1.4)
+ rack (~> 1.3.2)
+ rack-cache (~> 1.0.3)
+ rack-mount (~> 0.8.2)
+ rack-test (~> 0.6.1)
+ sprockets (~> 2.0.0.beta.15)
+ activemodel (3.1.0.rc8)
+ activesupport (= 3.1.0.rc8)
+ bcrypt-ruby (~> 3.0.0)
builder (~> 3.0.0)
i18n (~> 0.6)
- activerecord (3.1.0.rc5)
- activemodel (= 3.1.0.rc5)
- activesupport (= 3.1.0.rc5)
- arel (~> 2.1.4)
+ activerecord (3.1.0.rc8)
+ activemodel (= 3.1.0.rc8)
+ activesupport (= 3.1.0.rc8)
+ arel (~> 2.2.1)
tzinfo (~> 0.3.29)
- activeresource (3.1.0.rc5)
- activemodel (= 3.1.0.rc5)
- activesupport (= 3.1.0.rc5)
- activesupport (3.1.0.rc5)
+ activeresource (3.1.0.rc8)
+ activemodel (= 3.1.0.rc8)
+ activesupport (= 3.1.0.rc8)
+ activesupport (3.1.0.rc8)
multi_json (~> 1.0)
- arel (2.1.4)
- bcrypt-ruby (2.1.4)
+ arel (2.2.1)
+ bcrypt-ruby (3.0.0)
builder (3.0.0)
daemons (1.1.4)
delayed_job (2.1.4)
activesupport (~> 3.0)
daemons
erubis (2.7.0)
- factory_girl (2.0.3)
+ execjs (1.2.4)
+ multi_json (~> 1.0)
+ factory_girl (2.0.5)
haml (3.1.2)
- hike (1.2.0)
+ hike (1.2.1)
i18n (0.6.0)
imagesize (0.1.1)
+ libv8 (3.3.10.2)
mail (2.3.0)
i18n (>= 0.4.0)
mime-types (~> 1.16)
@@ -73,58 +76,66 @@ GEM
mocha (0.9.12)
multi_json (1.0.3)
net-http-digest_auth (1.1.1)
- net-http-persistent (1.8)
+ net-http-persistent (1.9)
nokogiri (1.5.0)
pg (0.11.0)
polyglot (0.3.2)
rack (1.3.2)
- rack-cache (1.0.2)
+ rack-cache (1.0.3)
rack (>= 0.4)
- rack-mount (0.8.1)
+ rack-mount (0.8.2)
rack (>= 1.0.0)
rack-ssl (1.3.2)
rack
rack-test (0.6.1)
rack (>= 1.0)
- rails (3.1.0.rc5)
- actionmailer (= 3.1.0.rc5)
- actionpack (= 3.1.0.rc5)
- activerecord (= 3.1.0.rc5)
- activeresource (= 3.1.0.rc5)
- activesupport (= 3.1.0.rc5)
+ rails (3.1.0.rc8)
+ actionmailer (= 3.1.0.rc8)
+ actionpack (= 3.1.0.rc8)
+ activerecord (= 3.1.0.rc8)
+ activeresource (= 3.1.0.rc8)
+ activesupport (= 3.1.0.rc8)
bundler (~> 1.0)
- railties (= 3.1.0.rc5)
- railties (3.1.0.rc5)
- actionpack (= 3.1.0.rc5)
- activesupport (= 3.1.0.rc5)
+ railties (= 3.1.0.rc8)
+ railties (3.1.0.rc8)
+ actionpack (= 3.1.0.rc8)
+ activesupport (= 3.1.0.rc8)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (~> 0.14.6)
rake (0.9.2)
- rdoc (3.9.1)
+ rdoc (3.9.4)
shoulda (2.11.3)
- silent-postgres (0.0.8)
+ silent-postgres (0.1.1)
simple_form (1.4.2)
actionpack (~> 3.0)
activemodel (~> 3.0)
simplecov (0.4.2)
simplecov-html (~> 0.4.4)
simplecov-html (0.4.5)
- sprockets (2.0.0.beta.13)
+ sprockets (2.0.0)
hike (~> 1.2)
rack (~> 1.0)
- tilt (!= 1.3.0, ~> 1.1)
+ tilt (~> 1.1, != 1.3.0)
super_exception_notifier (3.0.13)
actionmailer
rake
+ therubyracer (0.9.4)
+ libv8 (~> 3.3.10)
thor (0.14.6)
- tilt (1.3.2)
+ tilt (1.3.3)
treetop (1.4.10)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.29)
- webrobots (0.0.10)
+ uglifier (1.0.2)
+ execjs (>= 0.3.0)
+ multi_json (>= 1.0.2)
+ uglifier-rails (3.1.0.rc.2)
+ railties (~> 3.1.0.rc1)
+ uglifier (>= 1.0.0)
+ webrobots (0.0.11)
nokogiri (>= 1.4.4)
whenever (0.6.8)
aaronh-chronic (>= 0.3.9)
@@ -145,10 +156,13 @@ DEPENDENCIES
mocha
nokogiri
pg
- rails (= 3.1.0.rc5)
+ rails (= 3.1.0.rc8)
shoulda
silent-postgres
simple_form
simplecov
super_exception_notifier
+ therubyracer
+ uglifier
+ uglifier-rails
whenever
diff --git a/INSTALL.debian b/INSTALL.debian
index 6bf1c8cd5..4fb2ac5f3 100644
--- a/INSTALL.debian
+++ b/INSTALL.debian
@@ -1,9 +1,8 @@
#!/bin/bash
-echo "This script is out of date; please read the INSTALL document"
-exit 1
+# Run: curl -s https://raw.github.com/r888888888/danbooru/master/INSTALL.debian > install.sh ; chmod +x install.sh ; ./install.sh
-if [ $USER != root ] ; then
+if [[ "$(whoami)" != "root" ]] ; then
echo "You must run this script as root"
exit 1
fi
@@ -13,120 +12,131 @@ echo "This script will install Ruby, Rails, PostgreSQL, and Nginx. By the end,"
echo "you should be able to connect to the server and create an account."
echo
echo "It will create a new user called danbooru which will run the Danbooru"
-echo "processes. It will download the latest trunk copy and install it in"
-echo "/var/www/danbooru. It will run three Mongrel processes, starting on port"
-echo "8050."
+echo "processes. It will download the latest copy and install it in"
+echo "/var/www/danbooru."
+echo
+echo "It is mostly automated but you will receive some prompts when installing"
+echo "Passenger towards the end of the script."
echo
echo -n "Enter the hostname for this server (ex: danbooru.donmai.us): "
-read hostname
+read HOSTNAME
-if [ -z $hostname ] ; then
+if [[ -z "$HOSTNAME" ]] ; then
echo "Must enter a hostname"
exit 1
fi
echo -n "Enter a name for the site (default: Danbooru): "
-read sitename
+read SITENAME
-if [ -z $sitename ] ; then
- sitename=Danbooru
+if [[ -z "$SITENAME" ]] ; then
+ SITENAME=Danbooru
fi
# Install packages
-apt-get -y install sudo gcc g++ make libreadline5-dev zlib1g-dev flex bison libgd2-noxpm libgd2-noxpm-dev bzip2 postgresql-8.3 postgresql-contrib-8.3 libpq-dev ruby ruby1.8-dev ri irb rdoc rubygems ragel memcached libmemcache-dev subversion nginx rake libopenssl-ruby mongrel
+echo "Installing packages..."
+apt-get -y install build-essential automake openssl libssl-dev libyaml-dev libxml2-dev libxslt-dev autoconf ncurses-dev sudo gcc g++ libreadline-dev zlib1g-dev flex bison libgd2-noxpm libgd2-noxpm-dev bzip2 ragel memcached libmemcache-dev git curl libcurl4-openssl-dev emacs-nox
-# Install Ruby gems
-for i in postgres diff-lcs html5 mongrel mongrel_cluster memcache-client aws-s3 json ; do gem install $i ; done
-gem install rails --version=2.1.0
-gem install acts_as_versioned
+# Install PostgreSQL 9.1
+apt-get -y install python-software-properties
+add-apt-repository ppa:pitti/postgresql
+apt-get update
+apt-get install -y postgresql-9.1 postgresql-contrib-9.1 libpq-dev
+
+if [ $? -ne 0 ]; then
+ exit 1
+fi
+
+# Install RVM
+echo "Installing RVM..."
+bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)
+echo "source /usr/local/rvm/scripts/rvm" >> /etc/bash.bashrc
+echo "source /usr/local/rvm/scripts/rvm" >> /etc/profile
+source /usr/local/rvm/scripts/rvm
+rvm install ruby-1.9.2-p290 --with-openssl-dir=/usr/local
+rvm 1.9.2 --default
+
+# Install gems
+gem install --no-ri --no-rdoc capistrano
+gem install --no-ri --no-rdoc capistrano-ext
+gem install --no-ri --no-rdoc bundler
+
+# Install Passenger
+gem install --no-ri --no-rdoc -v 3.0.8 passenger
+rvm exec passenger-install-nginx-module
+rm /opt/nginx/conf/*.default
+
+if [ $? -ne 0 ]; then
+ exit 1
+fi
# Create user account
useradd -m danbooru
-PG_HBA_FILE="/etc/postgresql/8.3/main/pg_hba.conf"
+chsh -s /bin/bash danbooru
+echo "source /usr/local/rvm/scripts/rvm" > /home/danbooru/.bashrc
+chmod g-wx,o-wx /home/danbooru/.bashrc
+chown danbooru:danbooru /home/danbooru/.bashrc
+echo "export rvm_path=/usr/local/rvm" > /etc/rvmrc
+addgroup wheel
+usermod -G root,wheel root
+usermod -G danbooru,wheel danbooru
+
+# Update PostgreSQL
+PG_HBA_FILE="/etc/postgresql/9.1/main/pg_hba.conf"
echo "local all postgres,danbooru trust" > $PG_HBA_FILE
echo "host all postgres,danbooru 127.0.0.1/32 trust" >> $PG_HBA_FILE
-/etc/init.d/postgresql-8.3 restart
-# Install Danbooru
-cd /var/www
-svn export svn://donmai.us/danbooru/trunk danbooru
-chown -R danbooru:danbooru danbooru
-cd danbooru
-mkdir -p public/data/sample
-cd config
-cp database.yml.example database.yml
-cp local_config.rb.example local_config.rb
-sed -i -e "s/DAN_HOSTNAME/$hostname/g" local_config.rb
-sed -i -e "s/DAN_SITENAME/$sitename/g" local_config.rb
-echo "--- " > mongrel_cluster.yml
-echo "cwd: /var/www/danbooru" >> mongrel_cluster.yml
-echo "port: \"8050\"" >> mongrel_cluster.yml
-echo "environment: production" >> mongrel_cluster.yml
-echo "address: 127.0.0.1" >> mongrel_cluster.yml
-echo "servers: 3" >> mongrel_cluster.yml
-echo "num_processors: 10" >> mongrel_cluster.yml
-cd ../lib/danbooru_image_resizer
-ruby extconf.rb
-make
-cd ../..
+/etc/init.d/postgresql restart
sudo -u postgres createuser -s danbooru
-sudo -u danbooru createdb danbooru
-sudo -u danbooru psql danbooru < db/postgres.sql
-sudo -u danbooru rake db:migrate RAILS_ENV=production
-script/donmai/upbooru
-# Set up nginx
-DANBOORU_CONF_FILE="/etc/nginx/sites-enabled/danbooru.conf"
-echo "upstream mongrel {" > $DANBOORU_CONF_FILE
-echo " server 127.0.0.1:8050;" >> $DANBOORU_CONF_FILE
-echo " server 127.0.0.1:8051;" >> $DANBOORU_CONF_FILE
-echo " server 127.0.0.1:8052;" >> $DANBOORU_CONF_FILE
-echo "}" >> $DANBOORU_CONF_FILE
-echo "server {" >> $DANBOORU_CONF_FILE
-echo " listen 80;" >> $DANBOORU_CONF_FILE
-echo " server_name $hostname;" >> $DANBOORU_CONF_FILE
-echo " root /var/www/danbooru/public;" >> $DANBOORU_CONF_FILE
-echo " index index.html;" >> $DANBOORU_CONF_FILE
-echo " access_log /var/www/danbooru/log/server.access.log;" >> $DANBOORU_CONF_FILE
-echo " error_log /var/www/danbooru/log/server.error.log;" >> $DANBOORU_CONF_FILE
-echo " client_max_body_size 30m;" >> $DANBOORU_CONF_FILE
-echo " location /stylesheets {" >> $DANBOORU_CONF_FILE
-echo " expires max;" >> $DANBOORU_CONF_FILE
-echo " break;" >> $DANBOORU_CONF_FILE
-echo " }" >> $DANBOORU_CONF_FILE
-echo " location /javascripts {" >> $DANBOORU_CONF_FILE
-echo " expires max;" >> $DANBOORU_CONF_FILE
-echo " break;" >> $DANBOORU_CONF_FILE
-echo " }" >> $DANBOORU_CONF_FILE
-echo " location /data {" >> $DANBOORU_CONF_FILE
-echo " valid_referers none $hostname;" >> $DANBOORU_CONF_FILE
-echo " if (\$invalid_referer) {" >> $DANBOORU_CONF_FILE
-echo " return 403;" >> $DANBOORU_CONF_FILE
-echo " }" >> $DANBOORU_CONF_FILE
-echo " expires max;" >> $DANBOORU_CONF_FILE
-echo " break;" >> $DANBOORU_CONF_FILE
-echo " }" >> $DANBOORU_CONF_FILE
-echo " location /maintenance.html {" >> $DANBOORU_CONF_FILE
-echo " expires 10;" >> $DANBOORU_CONF_FILE
-echo " }" >> $DANBOORU_CONF_FILE
-echo " if (-f \$document_root/maintenance.html) {" >> $DANBOORU_CONF_FILE
-echo " rewrite ^(.*)\$ /maintenance.html last;" >> $DANBOORU_CONF_FILE
-echo " break;" >> $DANBOORU_CONF_FILE
-echo " }" >> $DANBOORU_CONF_FILE
-echo " location / {" >> $DANBOORU_CONF_FILE
-echo " proxy_set_header X-Real-IP \$remote_addr;" >> $DANBOORU_CONF_FILE
-echo " proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;" >> $DANBOORU_CONF_FILE
-echo " proxy_set_header Host \$http_host;" >> $DANBOORU_CONF_FILE
-echo " proxy_redirect false;" >> $DANBOORU_CONF_FILE
-echo " if (!-f \$request_filename) {" >> $DANBOORU_CONF_FILE
-echo " proxy_pass http://mongrel;" >> $DANBOORU_CONF_FILE
-echo " }" >> $DANBOORU_CONF_FILE
-echo " }" >> $DANBOORU_CONF_FILE
-echo " error_page 404 /404.html;" >> $DANBOORU_CONF_FILE
-echo " error_page 500 502 503 504 /500.html;" >> $DANBOORU_CONF_FILE
-echo "}" >> $DANBOORU_CONF_FILE
-/etc/init.d/nginx restart
+mkdir -p /var/log/www
+
+# Setup nginx
+curl -s https://raw.github.com/r888888888/danbooru/master/script/install/init_d.templ > /etc/init.d/nginx
+chmod +x,g-w /etc/init.d/nginx
+update-rc.d -f nginx defaults
+mkdir -p /opt/nginx/conf/sites
+curl -s https://raw.github.com/r888888888/danbooru/master/script/install/nginx.conf > /opt/nginx/conf/nginx.conf
+curl -s https://raw.github.com/r888888888/danbooru/master/script/install/nginx.danbooru.conf > /opt/nginx/conf/sites/danbooru.conf
+sed -i -e 's/\$hostname/$HOSTNAME/g' /opt/nginx/conf/sites/danbooru.conf
+/etc/init.d/nginx start
+
+echo "%wheel ALL=(ALL) ALL" >> /etc/sudoers
+
+# Setup logrotate
+LOGROTATE_CONF_FILE="/etc/logrotate.conf"
+echo >> $LOGROTATE_CONF_FILE
+echo "/var/log/www/danbooru.access.log" >> $LOGROTATE_CONF_FILE
+echo "/var/log/www/danbooru.error.log">> $LOGROTATE_CONF_FILE
+echo "/var/www/danbooru/current/log/production.log {" >> $LOGROTATE_CONF_FILE
+echo " daily" >> $LOGROTATE_CONF_FILE
+echo " rotate 3" >> $LOGROTATE_CONF_FILE
+echo " copytruncate" >> $LOGROTATE_CONF_FILE
+echo "}" >> $LOGROTATE_CONF_FILE
+
+# Setup danbooru account
+echo "*************************************************"
+echo "* Enter a new password for the danbooru account *"
+echo "*************************************************"
+passwd danbooru
+
+sudo -u danbooru createdb danbooru2
+cd /home/danbooru
+sudo -u danbooru mkdir /home/danbooru/apps
+git clone git://github.com/r888888888/danbooru.git danbooru
+chown -R danbooru:danbooru danbooru
+cap deploy:setup
+cap deploy:update_code
+cap deploy:migrate
+chown danbooru:danbooru /var/www/danbooru/shared/log/development.log
echo
-echo "I'm done!"
-echo "You should probably set the password for the danbooru account (run passwd danbooru)."
+echo
+echo "**************************************************************"
+echo "* Danbooru has now been setup on this computer and should be *"
+echo "* accessible from the web. Go ahead and create an account. *"
+echo "* The first account will automatically be made an admin. You *"
+echo "* can edit the configuration files by editing *"
+echo "* /var/www/danbooru/shared/config/local_config and *"
+echo "* /var/www/danbooru/shared/config/database.yml *"
+echo "**************************************************************"
diff --git a/INSTALL.freebsd b/INSTALL.freebsd
deleted file mode 100644
index 44bb59039..000000000
--- a/INSTALL.freebsd
+++ /dev/null
@@ -1,138 +0,0 @@
-Danbooru Installation guide on FreeBSD (6.2)
-Provided by Shuugo
-Cleanup of dovac's guide: http://uruchai.com/2007/12/24/in-depth-guide-on-installing-danbooru-on-freebsd
-
-A minimal FreeBSD profile installation is encouraged. You can use VMWare to test how things work before trying on an actual machine.
-
-1)) Updating ports:
-You can find how fully work here: http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/ports-using.html but basically you will only need:
-
-1. portsnap fetch
-2. portsnap extract
-
-2)) Install the following things through ports
-
-1. cd /usr/ports/devel/mkmf/ && make install clean
-2. cd /usr/ports/ftp/wget/ && make install clean
-3. cd /usr/ports/editors/nano/ && make install clean
-4. cd /usr/ports/devel/subversion/ && make install clean (May ask for configuration: Choose "libiconv", "python25", enable "huge stack size", and disable "ipv6"
-5. cd /usr/ports/databases/memcached/ && make install clean
-6. cd /usr/ports/graphics/gd/ && make install clean (Make sure you enable iconv support)
-7. cd /usr/ports/databases/postgresql83-client/ && make install clean (config defaults are ok)
-8. cd /usr/ports/databases/postgresql83-server/ && make install clean (config defaults are ok)
-
-3)) Editing rc.conf for starting postgres and memcached at boot.
-
-1. nano /etc/rc.conf
-2. Add the following at the end of the file:
-
-postgresql_enable="YES"
-memcached_enable="yes"
-memacached_flags="-l 127.0.0.1 -d m 100"
-
-3. Press Control + X to save.
-
-4. We run "/usr/local/etc/rc.d/postgresql initdb" to get ready to initialize postgres for first time.
-
-5. Use "/usr/local/etc/rc.d/postgresql start", to start the postgresql server.
-
-4)) Ruby on rails installation
-
-1. cd /usr/ports/lang/ruby18/ && make install clean (Disable ipv6)
-2. rubygem-rails port is broken, download it from rubyforge
-2a wget http://rubyforge.org/frs/download.php/29548/rubygems-1.0.1.tgz
-2b tar xvf rubygems-1.0.1.tgz
-2c cd rubygems-1.0.1
-2d ruby setup.rb
-3. cd /usr/ports/www/rubygem-rails/ && make install clean (Be sure it's rubygem-rails-1.2.6. Rails 2.0 isn't compatible yet)
-4. cd /usr/ports/www/rubygem-redcloth/ && make install clean
-5. cd /usr/ports/www/rubygem-mongrel/ && make install clean
-
-Some gems are not available through ports
-
-6a wget http://rubyforge.org/frs/download.php/29624/postgres-0.7.9.2007.12.22.gem
-6b /usr/bin/gem18 install --no-ri --install-dir /usr/local/lib/ruby/gems/1.8 ./postgres-0.7.9.2007.12.22.gem (There's a chance that it's not gem18, but gem I'll assume it's the correct path, if not, replace it on the following steps too)
-
-7. /usr/bin/gem18 aws-s3 diff-lcs acts_as_versioned html5 (Accept all dependencies)
-
-5)) Danbooru installation
-
-1. Create the danbooru user, -> adduser (danbooru, and choose a password)
-2. cd /home/danbooru
-3. svn co svn://danbooru.donmai.us/danbooru/trunk (This will get you the latest trunk)
-4. cd trunk/
-5. mv * ..
-6. cd ..
-7. rm -r trunk/
-
-8. Database creation:
-8a su pgsql
-8b CREATE DATABASE danbooru;
-8c \q
-8d psql -d danbooru -f /home/danbooru/db/postgres.sql
-8e exit
-
-6)) Danbooru configuration
-
-1. cd /home/danbooru/config
-2. mv database.yml.example database.yml
-3. nano database.yml
-3a Change all users to pgsql (Control+X to save and exit)
-4. mv local_config.rb.example local_config.rb
-5. nano local_config.rb
-NOTE: You can check for additional configuration parameters and a brief explanation in default_config.rb
-5a. Change the parameters needed an add these parameters
-CONFIG["password_salt"] = “choujin-steinerr” (replace the salt with something else)
-CONFIG["image_store"] = :local_flat (replace with :local_hierarchy if you want scaled folders for example /01/23/fullhash.ext, leave :local_falt to leave all the images in one same folder)
-CONFIG["enable_caching"] = false (change to true to enable memcache, this will speed up things)
-CONFIG["memcache_servers"] = ["localhost:4000"] (change to "localhost:11211")
-5b Control + X to save and exit.
-
-6. Compiling the resizer
-6a You can rather choose between using "ruby extconf.rb" or "ruby extconf.rb --with-gd-dir=/usr/local/gd", if you are using this last option you can ignore the 6b 6c and 6d steps. Using the first method will give you always the same gd version while the second command, the gd version will be updated when you update it through ports.
-6b cp /usr/local/include/gd.h gd.h
-6c cp /usr/local/include/gd_io.h gd_io.h
-6d cp /usr/local/include/gdfx.h gdfx.h
-6e make
-6f make install
-
-7. cd /home/danbooru
-7b /usr/local/bin/rake db:migrate
-
-7)) Basic webserver
-
-- Now you can run "mongrel_rails start", this will make danbooru public on your hostname:3000. You will see debug information in the console
-
-- For running it with mongrel and serving it "normally" you can use "mongrel_rails start -p 80 -d" This will tell mongrel to run on port 80 and as a daemon (not displaying anything in the console)
-
-8)) Advanced webserver
-
-Is encouraged that you rather configure Apache or lighttpd to proxy (or vhosting) the rails app. However this last method requries advanced configuration files and server modules.
-
-If you plan only on running danbooru here's a good configuration for lighttpd usage (you can install it through ports)
-Mongrel running as a daemon (4 threads) You might want to add this to the rc.conf if you want to start it automatically
-
-1. cd /home/danbooru
-2a mongrel_rails start -d -p 8001 -e production -P log/mongrel-1.pid
-2b mongrel_rails start -d -p 8002 -e production -P log/mongrel-2.pid
-2c mongrel_rails start -d -p 8003 -e production -P log/mongrel-3.pid
-2d mongrel_rails start -d -p 8004 -e production -P log/mongrel-4.pid
-
-Then on lighttpd.conf be sure to have the following mods enabled (Remove # before its names if not):
--mod_rewrite
--mod_redirect
--mod_access
--mod_accesslog
--mod_compress
--mod_proxy
-
-3. Add this to your lighttpd.conf:
-
-proxy.balance = "fair"
-proxy.server = ( "/" =>
- ( ( "host" => "127.0.0.1", "port" => 8001 ),
- ( "host" => "127.0.0.1", "port" => 8002 ),
- ( "host" => "127.0.0.1", "port" => 8003 ),
- ( "host" => "127.0.0.1", "port" => 8004 ) ) )
-
-4. Restart lighttpd: "/usr/local/etc/rc.d/lighttpd restart"
\ No newline at end of file
diff --git a/app/assets/javascripts/cookie.js b/app/assets/javascripts/cookie.js
index 8613c1f85..952bf8647 100644
--- a/app/assets/javascripts/cookie.js
+++ b/app/assets/javascripts/cookie.js
@@ -50,17 +50,7 @@
location.href = domain + "/static/terms_of_service?url=" + location.href;
return;
}
-
- if (this.get("hide-news-ticker") == "1") {
- $("#news-ticker").hide();
- } else {
- $("#close-news-ticker-link").click(function(e) {
- $("#news-ticker").hide();
- Danbooru.Cookie.put("hide-news-ticker", "1", 1);
- return false;
- });
- }
-
+
if (this.get("hide-upgrade-account") != "1") {
$("#upgrade-account").show();
}
diff --git a/app/assets/javascripts/favorites.js b/app/assets/javascripts/favorites.js
index 3a88f0860..6441ff5cf 100644
--- a/app/assets/javascripts/favorites.js
+++ b/app/assets/javascripts/favorites.js
@@ -20,6 +20,36 @@
$("a#remove-from-favorites").hide();
}
}
+
+ Danbooru.Favorite.create = function(post_id) {
+ Danbooru.Post.notice_update("inc");
+
+ $.ajax({
+ type: "POST",
+ url: "/favorites",
+ data: {
+ post_id: post_id
+ },
+ complete: function() {
+ Danbooru.Post.notice_update("dec");
+ },
+ error: function(data, status, xhr) {
+ Danbooru.j_alert("Error: " + data.reason);
+ }
+ });
+ }
+
+ Danbooru.Favorite.destroy = function(post_id) {
+ Danbooru.Post.notice_update("inc");
+
+ $.ajax({
+ type: "DELETE",
+ url: "/favorites/" + post_id,
+ complete: function() {
+ Danbooru.Post.notice_update("dec");
+ }
+ });
+ }
})();
$(document).ready(function() {
diff --git a/app/assets/javascripts/forum_posts.js b/app/assets/javascripts/forum_posts.js
index 6caf4e4f2..0ec5b9a10 100644
--- a/app/assets/javascripts/forum_posts.js
+++ b/app/assets/javascripts/forum_posts.js
@@ -5,6 +5,19 @@
$("#c-forum-topics #preview").hide();
this.initialize_preview_link();
+ this.initialize_last_forum_read_at();
+ }
+
+ Danbooru.ForumPost.initialize_last_forum_read_at = function() {
+ var last_forum_read_at = Date.parse(Danbooru.meta("last-forum-read-at"));
+
+ $("#c-forum-topics #a-index time").each(function(i, x) {
+ var $x = $(x);
+ var $date = Date.parse($x.attr("datetime"));
+ if (Date.parse($x.attr("datetime")) > last_forum_read_at) {
+ $x.closest("tr").addClass("new-topic");
+ }
+ });
}
Danbooru.ForumPost.initialize_preview_link = function() {
diff --git a/app/assets/javascripts/news.js b/app/assets/javascripts/news.js
new file mode 100644
index 000000000..048928056
--- /dev/null
+++ b/app/assets/javascripts/news.js
@@ -0,0 +1,21 @@
+(function() {
+ Danbooru.News = {};
+
+ Danbooru.News.initialize = function() {
+ var key = $("#news-ticker").data("updated-at");
+
+ if (Danbooru.Cookie.get("news-ticker") === key) {
+ $("#news-ticker").hide();
+ } else {
+ $("#close-news-ticker-link").click(function(e) {
+ $("#news-ticker").hide();
+ Danbooru.Cookie.put("news-ticker", key);
+ return false;
+ });
+ }
+ }
+
+ $(function() {
+ Danbooru.News.initialize();
+ });
+})();
diff --git a/app/assets/javascripts/post_mode_menu.js b/app/assets/javascripts/post_mode_menu.js
new file mode 100644
index 000000000..e10b9037f
--- /dev/null
+++ b/app/assets/javascripts/post_mode_menu.js
@@ -0,0 +1,83 @@
+(function() {
+ Danbooru.PostModeMenu = {};
+
+ Danbooru.PostModeMenu.initialize = function() {
+ this.initialize_selector();
+ this.initialize_preview_link();
+ }
+
+ Danbooru.PostModeMenu.initialize_selector = function() {
+ if (Danbooru.Cookie.get("mode") === "") {
+ Danbooru.Cookie.put("mode", "view");
+ $("#mode-box select").val("view");
+ } else {
+ $("#mode-box select").val(Danbooru.Cookie.get("mode"));
+ }
+
+ $("#mode-box select").click(Danbooru.PostModeMenu.change);
+ }
+
+ Danbooru.PostModeMenu.initialize_preview_link = function() {
+ $(".post-preview a").click(Danbooru.PostModeMenu.click);
+ }
+
+ Danbooru.PostModeMenu.change = function() {
+ var s = $("#mode-box select").val();
+ var $body = $(document.body);
+ $body.removeClass();
+ $body.addClass("mode-" + s);
+ Danbooru.Cookie.put("mode", s, 7);
+
+ if (s === "edit-tag-script") {
+ var script = Danbooru.Cookie.get("tag-script");
+ script = prompt("Enter a tag script", script);
+
+ if (script) {
+ Cookie.put("tag-script", script);
+ $("#mode-box select").val("apply-tag-script");
+ } else {
+ $("#mode-box select").val("view");
+ }
+
+ this.change();
+ }
+ }
+
+ Danbooru.PostModeMenu.click = function(e) {
+ var s = $("#mode-box select").val();
+ var post_id = $(e.target).closest("article").data("id");
+
+ if (s === "add-fav") {
+ Danbooru.Favorite.create(post_id);
+ } else if (s === "remove-fav") {
+ Danbooru.Favorite.destroy(post_id);
+ } else if (s === "edit") {
+ // TODO
+ } else if (s === 'vote-down') {
+ Danbooru.Post.vote("down", post_id);
+ } else if (s === 'vote-up') {
+ Danbooru.Post.vote("up", post_id);
+ } else if (s === 'rating-q') {
+ Danbooru.Post.update(post_id, {"post[rating]": "q"});
+ } else if (s === 'rating-s') {
+ Danbooru.Post.update(post_id, {"post[rating]": "s"});
+ } else if (s === 'rating-e') {
+ Danbooru.Post.update(post_id, {"post[rating]": "e"});
+ } else if (s === 'lock-rating') {
+ Danbooru.Post.update(post_id, {"post[is_rating_locked]": "1"});
+ } else if (s === 'lock-note') {
+ Danbooru.Post.update(post_id, {"post[is_note_locked]": "1"});
+ } else if (s === "apply-tag-script") {
+ var tag_script = Danbooru.Cookie.get("tag-script");
+ Danbooru.TagScript.run(post_id, tag_script);
+ } else {
+ return;
+ }
+
+ e.preventDefault();
+ }
+})();
+
+$(function() {
+ Danbooru.PostModeMenu.initialize();
+});
diff --git a/app/assets/javascripts/posts.js b/app/assets/javascripts/posts.js
index a7235bafa..2f05527b5 100644
--- a/app/assets/javascripts/posts.js
+++ b/app/assets/javascripts/posts.js
@@ -1,170 +1,8 @@
-// PostModeMenu = {
-// init: function() {
-// this.original_background_color = $(document.body).css("background-color")
-//
-// if (Cookie.get("mode") == "") {
-// Cookie.put("mode", "view");
-// $("#mode-box select").val("view");
-// } else {
-// $("#mode-box select").val(Cookie.get("mode"));
-// }
-//
-// // this.change();
-// },
-//
-// change: function() {
-// var s = $("#mode-box select").val();
-// Cookie.put("mode", s, 7);
-//
-// if (s == "view") {
-// $(document.body).css({"background-color": this.original_background_color});
-// } else if (s == "edit") {
-// $(document.body).css({"background-color": "#3A3"});
-// } else if (s == "add-fav") {
-// $(document.body).css({"background-color": "#FFA"});
-// } else if (s == "remove-fav") {
-// $(document.body).css({"background-color": "#FFA"});
-// } else if (s == "rating-q") {
-// $(document.body).css({"background-color": "#AAA"});
-// } else if (s == "rating-s") {
-// $(document.body).css({"background-color": "#6F6"});
-// } else if (s == "rating-e") {
-// $(document.body).css({"background-color": "#F66"});
-// } else if (s == "vote-down") {
-// $(document.body).css({"background-color": "#FAA"});
-// } else if (s == "vote-up") {
-// $(document.body).css({"background-color": "#AFA"});
-// } else if (s == "lock-rating") {
-// $(document.body).css({"background-color": "#AA3"});
-// } else if (s == "lock-note") {
-// $(document.body).css({"background-color": "#3AA"});
-// } else if (s == "approve") {
-// $(document.body).css({"background-color": "#26A"});
-// } else if (s == "unapprove") {
-// $(document.body).css({"background-color": "#F66"});
-// } else if (s == "add-to-pool") {
-// $(document.body).css({"background-color": "#26A"});
-// } else if (s == "apply-tag-script") {
-// $(document.body).css({"background-color": "#A3A"});
-// } else if (s == "edit-tag-script") {
-// $(document.body).css({"background-color": "#FFF"});
-//
-// var script = Cookie.get("tag-script");
-// script = prompt("Enter a tag script", script);
-//
-// if (script) {
-// Cookie.put("tag-script", script);
-// $("#mode-box select").val("apply-tag-script");
-// } else {
-// $("#mode-box select").val("view");
-// }
-//
-// this.change();
-// } else {
-// $(document.body).css({"background-color": "#AFA"});
-// }
-// },
-//
-// click: function(post_id) {
-// var s = $("#mode-box select").val();
-//
-// if (s.value == "view") {
-// return true;
-// } else if (s.value == "add-fav") {
-// Favorite.create(post_id);
-// } else if (s.value == "remove-fav") {
-// Favorite.destroy(post_id);
-// } else if (s.value == "edit") {
-// // TODO
-// } else if (s.value == 'vote-down') {
-// PostVote.create("down", post_id);
-// } else if (s.value == 'vote-up') {
-// PostVote.create("up", post_id);
-// } else if (s.value == 'rating-q') {
-// Post.update(post_id, {"post[rating]": "questionable"});
-// } else if (s.value == 'rating-s') {
-// Post.update(post_id, {"post[rating]": "safe"});
-// } else if (s.value == 'rating-e') {
-// Post.update(post_id, {"post[rating]": "explicit"});
-// } else if (s.value == 'lock-rating') {
-// Post.update(post_id, {"post[is_rating_locked]": "1"});
-// } else if (s.value == 'lock-note') {
-// Post.update(post_id, {"post[is_note_locked]": "1"});
-// } else if (s.value == 'unapprove') {
-// Unapproval.create(post_id);
-// } else if (s.value == "approve") {
-// Post.update(post_id, {"post[is_pending]": "0"});
-// } else if (s.value == 'add-to-pool') {
-// Pool.add_post(post_id, 0);
-// } else if (s.value == "apply-tag-script") {
-// var tag_script = Cookie.get("tag-script");
-// TagScript.run(post_id, tag_script);
-// }
-//
-// return false;
-// }
-// }
-//
-// TagScript = {
-// parse: function(script) {
-// return script.match(/\[.+?\]|\S+/g);
-// },
-//
-// test: function(tags, predicate) {
-// var split_pred = predicate.match(/\S+/g);
-// var is_true = true;
-//
-// split_pred.each(function(x) {
-// if (x[0] == "-") {
-// if (tags.include(x.substr(1, 100))) {
-// is_true = false;
-// throw $break;
-// }
-// } else {
-// if (!tags.include(x)) {
-// is_true = false;
-// throw $break;
-// }
-// }
-// })
-//
-// return is_true
-// },
-//
-// process: function(tags, command) {
-// if (command.match(/^\[if/)) {
-// var match = command.match(/\[if\s+(.+?)\s*,\s*(.+?)\]/)
-// if (TagScript.test(tags, match[1])) {
-// return TagScript.process(tags, match[2]);
-// } else {
-// return tags;
-// }
-// } else if (command == "[reset]") {
-// return [];
-// } else if (command[0] == "-") {
-// return tags.reject(function(x) {return x == command.substr(1, 100)})
-// } else {
-// tags.push(command)
-// return tags;
-// }
-// },
-//
-// run: function(post_id, tag_script) {
-// var commands = TagScript.parse(tag_script);
-// var post = Post.posts.get(post_id);
-// var old_tags = post.tags.join(" ");
-//
-// commands.each(function(x) {
-// post.tags = TagScript.process(post.tags, x);
-// })
-//
-// Post.update(post_id, {"post[old_tags]": old_tags, "post[tags]": post.tags.join(" ")});
-// }
-// }
-
(function() {
Danbooru.Post = {};
+ Danbooru.Post.pending_update_count = 0;
+
Danbooru.Post.initialize_all = function() {
this.initialize_post_sections();
this.initialize_wiki_page_excerpt();
@@ -192,7 +30,7 @@
Danbooru.Cookie.put("hide-wiki-page-excerpt", "0");
});
}
-
+
Danbooru.Post.initialize_post_sections = function() {
$("#post-sections li a").click(function(e) {
$("#comments").hide();
@@ -209,11 +47,68 @@
$("#notes").hide();
$("#edit").hide();
}
+
+ Danbooru.Post.notice_update = function(x) {
+ if (x === "inc") {
+ Danbooru.Post.pending_update_count += 1;
+ Danbooru.notice("Updating posts (" + Danbooru.Post.pending_update_count + " pending)...");
+ } else {
+ Danbooru.Post.pending_update_count -= 1;
+
+ if (Danbooru.Post.pending_update_count < 1) {
+ Danbooru.notice("Posts updated");
+ } else {
+ Danbooru.notice("Updating posts (" + Post.pending_update_count + " pending)...");
+ }
+ }
+ }
+
+ Danbooru.Post.update_data = function(data) {
+ var $post = $("#post_" + data.id);
+ $post.data("tags", data.tags);
+ }
+
+ Danbooru.Post.vote = function(score, id) {
+ Danbooru.Post.notice_update("inc");
+
+ $.ajax({
+ type: "POST",
+ url: "/posts/" + id + "/votes",
+ data: {
+ score: score
+ },
+ complete: function() {
+ Danbooru.Post.notice_update("dec");
+ },
+ success: function(data, status, xhr) {
+ $("post-score-" + data.post_id).html(data.score);
+ },
+ error: function(data, status, xhr) {
+ Danbooru.notice("Error: " + data.reason);
+ }
+ });
+ }
+
+ Danbooru.Post.update = function(post_id, params) {
+ Danbooru.Post.notice_update("inc");
+
+ $.ajax({
+ type: "PUT",
+ url: "/posts/" + post_id + ".json",
+ data: params,
+ complete: function() {
+ Danbooru.Post.notice_update("dec");
+ },
+ success: function(data, status, xhr) {
+ Danbooru.Post.update_data(data);
+ },
+ error: function(data, status, xhr) {
+ Danbooru.j_alert("Error: " + data.reason);
+ }
+ });
+ }
})();
$(document).ready(function() {
- // $("#mode-box select").click(PostModeMenu.change);
- // PostModeMenu.init();
-
Danbooru.Post.initialize_all();
});
diff --git a/app/assets/javascripts/tag_script.js b/app/assets/javascripts/tag_script.js
new file mode 100644
index 000000000..7efe2be24
--- /dev/null
+++ b/app/assets/javascripts/tag_script.js
@@ -0,0 +1,56 @@
+(function() {
+ Danbooru.TagScript = {};
+
+ Danbooru.TagScript.parse = function(script) {
+ return script.match(/\[.+?\]|\S+/g);
+ }
+
+ Danbooru.TagScript.test = function(tags, predicate) {
+ var split_pred = predicate.match(/\S+/g);
+ var is_true = true;
+
+ $.each(split_pred, function(i, x) {
+ if (x[0] === "-") {
+ if (tags.include(x.substr(1, 100))) {
+ is_true = false;
+ }
+ } else {
+ if (!tags.include(x)) {
+ is_true = false;
+ }
+ }
+ });
+
+ return is_true;
+ }
+
+ Danbooru.TagScript.process = function(tags, command) {
+ if (command.match(/^\[if/)) {
+ var match = command.match(/\[if\s+(.+?)\s*,\s*(.+?)\]/)
+ if (this.test(tags, match[1])) {
+ return this.process(tags, match[2]);
+ } else {
+ return tags;
+ }
+ } else if (command === "[reset]") {
+ return [];
+ } else if (command[0] === "-") {
+ return Danbooru.reject(tags, function(x) {return x === command.substr(1, 100)});
+ } else {
+ tags.push(command)
+ return tags;
+ }
+ }
+
+ Danbooru.TagScript.run = function(post_id, tag_script) {
+ var commands = this.parse(tag_script);
+ var post = $("#p_" + post_id);
+ var old_tags = post.data("tags");
+
+ $.each(commands, function(i, x) {
+ post.data("tags", Danbooru.TagScript.process(post.data("tags"), x));
+ })
+
+ Danbooru.Post.update(post_id, {"post[old_tags]": old_tags, "post[tags]": post.data("tags")});
+ }
+})();
diff --git a/app/assets/javascripts/uploads.js b/app/assets/javascripts/uploads.js
index 4713e536d..2614604d1 100644
--- a/app/assets/javascripts/uploads.js
+++ b/app/assets/javascripts/uploads.js
@@ -1,4 +1,4 @@
-$(document).ready(function() {
+$(function() {
var img = $("#image-preview img");
if (img) {
var height = img.attr("height");
@@ -7,7 +7,7 @@ $(document).ready(function() {
var ratio = 400.0 / height;
img.attr("height", height * ratio);
img.attr("width", width * ratio);
- $("#scale").val("Scaled " + parseInt(100 * ratio) + "%");
+ $("#scale").html("Scaled " + parseInt(100 * ratio) + "%");
}
}
});
diff --git a/app/assets/javascripts/utility.js b/app/assets/javascripts/utility.js
index bae2db7cf..d93a858d0 100644
--- a/app/assets/javascripts/utility.js
+++ b/app/assets/javascripts/utility.js
@@ -3,6 +3,10 @@
return $("meta[name=" + key + "]").attr("content");
}
+ Danbooru.notice = function(msg) {
+ $('#notice').html(msg).show();
+ }
+
Danbooru.j_alert = function(title, msg) {
$('
').html(msg).dialog();
}
@@ -30,4 +34,14 @@
return all;
}
+
+ Danbooru.reject = function(array, f) {
+ var filtered = [];
+ $.each(array, function(i, x) {
+ if (!f(x)) {
+ filtered.push(x);
+ }
+ });
+ return filtered;
+ }
})();
diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss
index 098fea73d..229215959 100644
--- a/app/assets/stylesheets/application.css.scss
+++ b/app/assets/stylesheets/application.css.scss
@@ -896,6 +896,10 @@ div#c-forum-topics {
color: #AAA;
}
+ tr.new-topic {
+ font-weight: bold;
+ }
+
div#form-content {
float: left;
width: 450px;
@@ -1079,7 +1083,7 @@ footer#page-footer {
margin: 1em;
text-align: center;
padding-top: 1em;
- border-top: 2px solid #CCC;
+ border-top: 1px solid #EEE;
}
/*** news ticker ***/
@@ -1108,3 +1112,69 @@ div#news-ticker {
float: right;
}
}
+
+
+/*** post mode menus ***/
+body#mode-view {
+ background-color: "#FFF";
+}
+
+body#mode-edit {
+ background-color: "#3A3";
+}
+
+body#mode-add-fav {
+ background-color: "#FFA";
+}
+
+body#mode-remove-fav {
+ background-color: "#FFA";
+}
+
+body#mode-rating-q {
+ background-color: "#AAA";
+}
+
+body#mode-rating-s {
+ background-color: "#6F6";
+}
+
+body#mode-rating-e {
+ background-color: "#F66";
+}
+
+body#mode-vote-down {
+ background-color: "#FAA";
+}
+
+body#mode-vote-up {
+ background-color: "#AFA";
+}
+
+body#mode-lock-rating {
+ background-color: "#AA3";
+}
+
+body#mode-lock-note {
+ background-color: "#3AA";
+}
+
+body#mode-approve {
+ background-color: "#26A";
+}
+
+body#mode-unapprove {
+ background-color: "#F66";
+}
+
+body#mode-add-to-pool {
+ background-color: "#26A";
+}
+
+body#mode-apply-tag-script {
+ background-color: "#A3A";
+}
+
+body#mode-edit-tag-script {
+ background-color: "#FFF";
+}
diff --git a/app/controllers/artists_controller.rb b/app/controllers/artists_controller.rb
index 33f226937..2f21c92ac 100644
--- a/app/controllers/artists_controller.rb
+++ b/app/controllers/artists_controller.rb
@@ -12,6 +12,11 @@ class ArtistsController < ApplicationController
respond_with(@artist)
end
+ def banned
+ @artists = Artist.where("is_banned = ?", true).order("name")
+ respond_with(@artists)
+ end
+
def index
@search = Artist.search(params[:search])
@artists = @search.paginate(params[:page])
diff --git a/app/controllers/forum_topics_controller.rb b/app/controllers/forum_topics_controller.rb
index 5668dfdad..7f2f7b335 100644
--- a/app/controllers/forum_topics_controller.rb
+++ b/app/controllers/forum_topics_controller.rb
@@ -2,6 +2,7 @@ class ForumTopicsController < ApplicationController
respond_to :html, :xml, :json
before_filter :member_only, :except => [:index, :show]
before_filter :normalize_search, :only => :index
+ before_filter :update_last_forum_read_at, :only => [:index, :show]
rescue_from User::PrivilegeError, :with => "static/access_denied"
def new
@@ -56,6 +57,12 @@ private
forum_topic.is_sticky = params[:forum_topic][:is_sticky]
end
+ def update_last_forum_read_at
+ return if CurrentUser.last_forum_read_at.present? && CurrentUser.last_forum_read_at > 1.day.ago
+
+ CurrentUser.update_column(:last_forum_read_at, Time.now)
+ end
+
def normalize_search
if params[:title_matches]
params[:search] ||= {}
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index de0478d9d..310758350 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -6,6 +6,7 @@ class SessionsController < ApplicationController
def create
if User.authenticate(params[:name], params[:password])
@user = User.find_by_name(params[:name])
+ @user.update_column(:last_logged_in_at, Time.now)
session[:user_id] = @user.id
redirect_to(params[:url] || session[:previous_uri] || posts_path, :notice => "You are now logged in.")
else
diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb
index 59aded66b..072062ce4 100644
--- a/app/controllers/uploads_controller.rb
+++ b/app/controllers/uploads_controller.rb
@@ -23,6 +23,7 @@ class UploadsController < ApplicationController
def create
@upload = Upload.create(params[:upload])
+ @upload.delay.process!
respond_with(@upload)
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index c7a319709..18350bee4 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -22,6 +22,7 @@ class UsersController < ApplicationController
def show
@user = User.find(params[:id])
+ @presenter = UserPresenter.new(@user)
respond_with(@user)
end
diff --git a/app/helpers/advertisements_helper.rb b/app/helpers/advertisements_helper.rb
index 08b0218fa..211044b03 100644
--- a/app/helpers/advertisements_helper.rb
+++ b/app/helpers/advertisements_helper.rb
@@ -22,7 +22,7 @@ module AdvertisementsHelper
def render_rss_advertisement
if Danbooru.config.can_user_see_ads?(CurrentUser.user)
- render :partial => "static/jlist_rss_ads"
+ render "static/jlist_rss_ads"
end
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 7c3425f44..254503f97 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -23,13 +23,20 @@ module ApplicationHelper
end
end
+ def time_tag(content = nil, time)
+ zone = time.strftime("%z")
+ datetime = time.strftime("%Y-%m-%dT%H:%M" + zone[0, 3] + ":" + zone[3, 2])
+
+ content_tag(:time, content || datetime, :datetime => datetime)
+ end
+
def compact_time(time)
if time > Time.now.beginning_of_day
- time.strftime("%H:%M")
+ time_tag(time.strftime("%H:%M"), time)
elsif time > Time.now.beginning_of_year
- time.strftime("%b %e")
+ time_tag(time.strftime("%b %e"), time)
else
- time.strftime("%b %e, %Y")
+ time_tag(time.strftime("%b %e, %Y"), time)
end
end
@@ -62,7 +69,7 @@ protected
when "uploads"
/^\/post/
- when "post_versions"
+ when "post_versions", "explore/posts"
/^\/post/
when "pool_versions"
diff --git a/app/logical/anonymous_user.rb b/app/logical/anonymous_user.rb
index eb352dac4..1faf5b95e 100644
--- a/app/logical/anonymous_user.rb
+++ b/app/logical/anonymous_user.rb
@@ -112,6 +112,13 @@ class AnonymousUser
[]
end
+ def last_forum_read_at
+ Time.now
+ end
+
+ def update_column(*params)
+ end
+
%w(member banned privileged contributor janitor moderator admin).each do |name|
define_method("is_#{name}?") do
false
diff --git a/app/logical/remote_file_manager.rb b/app/logical/remote_file_manager.rb
new file mode 100644
index 000000000..beb2c7af5
--- /dev/null
+++ b/app/logical/remote_file_manager.rb
@@ -0,0 +1,23 @@
+class RemoteFileManager
+ attr_reader :path
+
+ def initialize(path)
+ @path = path
+ end
+
+ def distribute
+ Danbooru.config.other_server_hosts.each do |hostname|
+ Net::SFTP.start(hostname, Danbooru.config.remote_server_login) do |ftp|
+ ftp.upload!(path, path)
+ end
+ end
+ end
+
+ def delete
+ Danbooru.config.other_server_hosts.each do |hostname|
+ Net::SFTP.start(hostname, Danbooru.config.remote_server_login) do |ftp|
+ ftp.remove(path)
+ end
+ end
+ end
+end
diff --git a/app/models/amazon_backup.rb b/app/models/amazon_backup.rb
new file mode 100644
index 000000000..e29c2d373
--- /dev/null
+++ b/app/models/amazon_backup.rb
@@ -0,0 +1,44 @@
+require 'base64'
+require 'digest/md5'
+
+class AmazonBackup < ActiveRecord::Base
+ def self.last_id
+ first.last_id
+ end
+
+ def self.update_id(new_id)
+ first.update_column(:last_id, new_id)
+ end
+
+ def self.execute
+ last_id = AmazonBackup.last_id
+
+ Post.where("id > ?", last_id).limit(200).order("id").each do |post|
+ AWS::S3::Base.establish_connection!(
+ :access_key_id => Danbooru.config.amazon_s3_access_key_id,
+ :secret_access_key => Danbooru.config.amazon_s3_secret_access_key
+ )
+
+ if File.exists?(post.file_path)
+ base64_md5 = Base64.encode64(Digest::MD5.digest(File.read(post.file_path)))
+ AWS::S3::S3Object.store(post.file_name, open(post.file_path, "rb"), Danbooru.config.amazon_s3_bucket_name, "Content-MD5" => base64_md5)
+ end
+
+ if post.image? && File.exists?(post.preview_file_path)
+ AWS::S3::S3Object.store("preview/#{post.md5}.jpg", open(post.preview_file_path, "rb"), Danbooru.config.amazon_s3_bucket_name)
+ end
+
+ if File.exists?(post.medium_file_path)
+ AWS::S3::S3Object.store("medium/#{post.md5}.jpg", open(post.medium_file_path, "rb"), Danbooru.config.amazon_s3_bucket_name)
+ end
+
+ if File.exists?(post.large_file_path)
+ AWS::S3::S3Object.store("large/#{post.md5}.jpg", open(post.large_file_path, "rb"), Danbooru.config.amazon_s3_bucket_name)
+ end
+
+ AmazonBackup.update_id(last_id)
+ end
+ rescue Exception => x
+ # probably some network error, retry next time
+ end
+end
diff --git a/app/models/artist.rb b/app/models/artist.rb
index c405f18d7..0990a30fe 100644
--- a/app/models/artist.rb
+++ b/app/models/artist.rb
@@ -166,6 +166,10 @@ class Artist < ActiveRecord::Base
include NoteMethods
include TagMethods
+ def ban!
+
+ end
+
def initialize_creator
self.creator_id = CurrentUser.user.id
end
diff --git a/app/models/jobs/backup_to_s3.rb b/app/models/jobs/backup_to_s3.rb
deleted file mode 100644
index 3675ac649..000000000
--- a/app/models/jobs/backup_to_s3.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-module Jobs
- class BackupToS3 < Struct.new(:last_id)
- def perform
- Post.find(:all, :conditions => ["id > ?", last_id], :limit => 200, :order => "id").each do |post|
- AWS::S3::Base.establish_connection!(:access_key_id => CONFIG["amazon_s3_access_key_id"], :secret_access_key => CONFIG["amazon_s3_secret_access_key"])
- if File.exists?(post.file_path)
- base64_md5 = Base64.encode64(Digest::MD5.digest(File.read(post.file_path)))
- AWS::S3::S3Object.store(post.file_name, open(post.file_path, "rb"), CONFIG["amazon_s3_bucket_name"], "Content-MD5" => base64_md5)
- end
-
- if post.image? && File.exists?(post.preview_path)
- AWS::S3::S3Object.store("preview/#{post.md5}.jpg", open(post.preview_path, "rb"), CONFIG["amazon_s3_bucket_name"])
- end
-
- if File.exists?(post.sample_path)
- AWS::S3::S3Object.store("sample/" + CONFIG["sample_filename_prefix"] + "#{post.md5}.jpg", open(post.sample_path, "rb"), CONFIG["amazon_s3_bucket_name"])
- end
-
- self.last_id = post.id
- end
-
- Delayed::Job.enqueue(BackupToS3.new(last_id))
- rescue Exception => x
- # probably some network error, retry next time
- end
- end
-end
\ No newline at end of file
diff --git a/app/models/jobs/calculate_post_count.rb b/app/models/jobs/calculate_post_count.rb
deleted file mode 100644
index 24653b04a..000000000
--- a/app/models/jobs/calculate_post_count.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Jobs
- class CalculatePostCount < Struct.new(:tag_name)
- def perform
- Tag.recalculate_post_count(tag_name)
- end
- end
-end
diff --git a/app/models/jobs/calculate_related_tags.rb b/app/models/jobs/calculate_related_tags.rb
deleted file mode 100644
index 161faabb4..000000000
--- a/app/models/jobs/calculate_related_tags.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-module Jobs
- class CalculateRelatedTags < Struct.new(:tag_id)
- def perform
- tag = Tag.find_by_id(tag_id)
-
- if tag
- tag.update_related
- tag.save
- end
- end
- end
-end
diff --git a/app/models/jobs/calculate_uploaded_tags.rb b/app/models/jobs/calculate_uploaded_tags.rb
deleted file mode 100644
index a3f750df9..000000000
--- a/app/models/jobs/calculate_uploaded_tags.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-module Jobs
- class CalculateUploadedTags < Struct.new(:user_id)
- def perform
- tags = []
- user = User.find(user_id)
- CONFIG["tag_types"].values.uniq.each do |tag_type|
- tags += user.calculate_uploaded_tags(tag_type)
- end
- user.update_column(:uploaded_tags, tags.join("\n"))
- end
- end
-end
-
diff --git a/app/models/jobs/create_tag_alias.rb b/app/models/jobs/create_tag_alias.rb
deleted file mode 100644
index 1e1d4dfbc..000000000
--- a/app/models/jobs/create_tag_alias.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-module Jobs
- class CreateTagAlias < Struct.new(:antecedent_name, :consequent_name, :creator_id, :creator_ip_addr)
- def execute
- TagAlias.create(
- :antecedent_name => antecedent_name,
- :consequent_name => consequent_name,
- :creator_id => creator_id,
- :creator_ip_addr => creator_ip_addr
- )
- end
- end
-end
diff --git a/app/models/jobs/create_tag_implication.rb b/app/models/jobs/create_tag_implication.rb
deleted file mode 100644
index 860582c9d..000000000
--- a/app/models/jobs/create_tag_implication.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-module Jobs
- class CreateTagImplication < Struct.new(:antecedent_name, :consequent_name, :creator_id, :creator_ip_addr)
- def perform
- TagImplication.create(
- :antecedent_name => antecedent_name,
- :consequent_name => consequent_name,
- :creator_id => creator_id,
- :creator_ip_addr => creator_ip_addr
- )
- end
- end
-end
diff --git a/app/models/jobs/fix_pixiv_uploads.rb b/app/models/jobs/fix_pixiv_uploads.rb
deleted file mode 100644
index 3e9dd09dc..000000000
--- a/app/models/jobs/fix_pixiv_uploads.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-module Jobs
- class FixPixivUploads < Struct.new(:last_post_id)
- def perform
- post_id = nil
-
- Post.find_each(:conditions => ["GREATEST(width, height) IN (150, 600) AND source LIKE ? AND id > ?", "%pixiv%", last_post_id]) do |post|
- post_id = post.id
- end
-
- update_attributes(:data => {:last_post_id => post_id})
- end
- end
-end
diff --git a/app/models/jobs/mass_tag_edit.rb b/app/models/jobs/mass_tag_edit.rb
deleted file mode 100644
index c10102a7d..000000000
--- a/app/models/jobs/mass_tag_edit.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Jobs
- class MassTagEdit < Struct.new(:start_tags, :result_tags, :updater_id, :updater_ip_addr)
- def perform
- Tag.mass_edit(start_tags, result_tags, updater_id, updater_ip_addr)
- end
- end
-end
diff --git a/app/models/jobs/process_tag_subscriptions.rb b/app/models/jobs/process_tag_subscriptions.rb
deleted file mode 100644
index e6e21f265..000000000
--- a/app/models/jobs/process_tag_subscriptions.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-module Jobs
- class ProcessTagSubscriptions < Struct.new(:last_run)
- def perform
- if last_run.nil? || last_run < 20.minutes.ago
- TagSubscription.process_all
- Delayed::Job.enqueue(ProcessTagSubscriptions.new(Time.now))
- end
- end
- end
-end
diff --git a/app/models/jobs/process_uploads.rb b/app/models/jobs/process_uploads.rb
deleted file mode 100644
index c7ba63ae3..000000000
--- a/app/models/jobs/process_uploads.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module Jobs
- class ProcessUploads
- def perform
- Upload.find_each(:conditions => ["status = ?", "pending"]) do |upload|
- upload.process!
- end
- end
- end
-end
diff --git a/app/models/post.rb b/app/models/post.rb
index d9e82f357..5d50bf923 100644
--- a/app/models/post.rb
+++ b/app/models/post.rb
@@ -5,6 +5,7 @@ class Post < ActiveRecord::Base
attr_accessor :old_tag_string, :old_parent_id
after_destroy :delete_files
+ after_destroy :delete_remote_files
after_save :create_version
after_save :update_parent_on_save
before_save :merge_old_tags
@@ -61,11 +62,28 @@ class Post < ActiveRecord::Base
}
module FileMethods
+ def distribute_files
+ RemoteFileManager.new(file_path).distribute
+ RemoteFileManager.new(real_preview_file_path).distribute
+ RemoteFileManager.new(ssd_preview_file_path).distribute if Danbooru.config.ssd_path
+ RemoteFileManager.new(medium_file_path).distribute if has_medium?
+ RemoteFileManager.new(large_file_path).distribute if has_large?
+ end
+
+ def delete_remote_files
+ RemoteFileManager.new(file_path).delete
+ RemoteFileManager.new(real_preview_file_path).delete
+ RemoteFileManager.new(ssd_preview_file_path).delete if Danbooru.config.ssd_path
+ RemoteFileManager.new(medium_file_path).delete if has_medium?
+ RemoteFileManager.new(large_file_path).delete if has_large?
+ end
+
def delete_files
FileUtils.rm_f(file_path)
FileUtils.rm_f(medium_file_path)
FileUtils.rm_f(large_file_path)
- FileUtils.rm_f(preview_file_path)
+ FileUtils.rm_f(ssd_preview_file_path) if Danbooru.config.ssd_path
+ FileUtils.rm_f(real_preview_file_path)
end
def file_path_prefix
@@ -91,9 +109,21 @@ class Post < ActiveRecord::Base
file_path
end
end
+
+ def real_preview_file_path
+ "#{Rails.root}/public/data/preview/#{file_path_prefix}#{md5}.jpg"
+ end
+
+ def ssd_preview_file_path
+ "#{Danbooru.config.ssd_path}/public/data/preview/#{file_path_preview}#{md5}.jpg"
+ end
def preview_file_path
- "#{Rails.root}/public/data/preview/#{file_path_prefix}#{md5}.jpg"
+ if Danbooru.config.ssd_path
+ ssd_preview_file_path
+ else
+ real_preview_file_path
+ end
end
def file_url
@@ -117,7 +147,11 @@ class Post < ActiveRecord::Base
end
def preview_file_url
- "/data/preview/#{file_path_prefix}#{md5}.jpg"
+ if Danbooru.config.ssd_path
+ "/ssd/data/preview/#{file_path_prefix}#{md5}.jpg"
+ else
+ "/data/preview/#{file_path_prefix}#{md5}.jpg"
+ end
end
def file_url_for(user)
diff --git a/app/models/upload.rb b/app/models/upload.rb
index c22128b14..bf5e8004a 100644
--- a/app/models/upload.rb
+++ b/app/models/upload.rb
@@ -48,6 +48,8 @@ class Upload < ActiveRecord::Base
module ConversionMethods
def process!
+ return if status =~ /processing|completed|error/
+
CurrentUser.scoped(uploader, uploader_ip_addr) do
update_attribute(:status, "processing")
if is_downloadable?
@@ -64,6 +66,7 @@ class Upload < ActiveRecord::Base
generate_resizes(file_path)
move_file
post = convert_to_post
+ post.distribute_files
if post.save
update_attributes(:status => "completed", :post_id => post.id)
else
@@ -231,7 +234,7 @@ class Upload < ActiveRecord::Base
end
def temp_file_path
- @temp_file_path ||= File.join(Rails.root, "tmp", "upload_#{Time.now.to_f}.#{$PROCESS_ID}")
+ @temp_file_path ||= File.join(Rails.root, "tmp", "upload_#{Time.now.to_f}.#{Process.pid}")
end
end
@@ -258,7 +261,7 @@ class Upload < ActiveRecord::Base
self.file_path = temp_file_path
- if file.tempfile
+ if file.respond_to?(:tempfile) && file.tempfile
FileUtils.cp(file.tempfile.path, file_path)
else
File.open(file_path, 'wb') do |out|
@@ -279,6 +282,10 @@ class Upload < ActiveRecord::Base
status == "pending"
end
+ def is_processing?
+ status == "processing"
+ end
+
def is_completed?
status == "completed"
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 6d114b7ea..f496aaed8 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -30,7 +30,7 @@ class User < ActiveRecord::Base
has_many :feedback, :class_name => "UserFeedback", :dependent => :destroy
has_many :posts, :foreign_key => "uploader_id"
has_one :ban
- has_many :subscriptions, :class_name => "TagSubscription"
+ has_many :subscriptions, :class_name => "TagSubscription", :foreign_key => "creator_id"
has_many :note_versions, :foreign_key => "updater_id"
belongs_to :inviter, :class_name => "User"
scope :named, lambda {|name| where(["lower(name) = ?", name])}
diff --git a/app/presenters/forum_topic_presenter.rb b/app/presenters/forum_topic_presenter.rb
index 538a0a751..a10593cbc 100644
--- a/app/presenters/forum_topic_presenter.rb
+++ b/app/presenters/forum_topic_presenter.rb
@@ -5,8 +5,4 @@ class ForumTopicPresenter < Presenter
@forum_posts = forum_posts
@forum_topic = forum_topic
end
-
- def pagination_html(template)
- Paginators::ForumTopic.new(forum_topic, forum_posts).numbered_pagination_html(template)
- end
end
diff --git a/app/presenters/post_presenter.rb b/app/presenters/post_presenter.rb
index 97d93284c..ec587d9bb 100644
--- a/app/presenters/post_presenter.rb
+++ b/app/presenters/post_presenter.rb
@@ -26,9 +26,9 @@ class PostPresenter < Presenter
return template.content_tag("p", "You need a privileged account to see this image.") if !Danbooru.config.can_user_see_post?(CurrentUser.user, @post)
if @post.is_flash?
- template.render(:partial => "posts/partials/show/flash", :locals => {:post => @post})
+ template.render("posts/partials/show/flash", :post => @post)
elsif @post.is_image?
- template.render(:partial => "posts/partials/show/image", :locals => {:post => @post})
+ template.render("posts/partials/show/image", :post => @post)
end
end
diff --git a/app/presenters/post_set_presenter.rb b/app/presenters/post_set_presenter.rb
index 4e4db8f10..754be1c34 100644
--- a/app/presenters/post_set_presenter.rb
+++ b/app/presenters/post_set_presenter.rb
@@ -18,7 +18,7 @@ class PostSetPresenter < Presenter
html = ""
if posts.empty?
- return template.render(:partial => "post_sets/blank")
+ return template.render("post_sets/blank")
end
posts.each do |post|
diff --git a/app/presenters/post_set_presenters/base.rb b/app/presenters/post_set_presenters/base.rb
index b2b93dcf6..fb974cb11 100644
--- a/app/presenters/post_set_presenters/base.rb
+++ b/app/presenters/post_set_presenters/base.rb
@@ -8,7 +8,7 @@ module PostSetPresenters
html = ""
if posts.empty?
- return template.render(:partial => "post_sets/blank")
+ return template.render("post_sets/blank")
end
posts.each do |post|
diff --git a/app/presenters/post_set_presenters/popular.rb b/app/presenters/post_set_presenters/popular.rb
index 0d58f5e19..754b3fb3f 100644
--- a/app/presenters/post_set_presenters/popular.rb
+++ b/app/presenters/post_set_presenters/popular.rb
@@ -1,7 +1,7 @@
module PostSetPresenters
class Popular < Base
attr_accessor :post_set, :tag_set_presenter
- delegate :posts, :date, :to => :post_set
+ delegate :posts, :date, :min_date, :max_date, :to => :post_set
def initialize(post_set)
@post_set = post_set
@@ -30,5 +30,15 @@ module PostSetPresenters
def next_month
1.month.since(date)
end
+
+ def range_text
+ if min_date == max_date
+ date.strftime("%B %d, %Y")
+ elsif max_date - min_date == 6
+ min_date.strftime("Week of %B %d, %Y")
+ else
+ date.strftime("%B %Y")
+ end
+ end
end
end
diff --git a/app/presenters/user_presenter.rb b/app/presenters/user_presenter.rb
index 9b3fb0e64..8776e9e57 100644
--- a/app/presenters/user_presenter.rb
+++ b/app/presenters/user_presenter.rb
@@ -5,6 +5,10 @@ class UserPresenter
@user = user
end
+ def join_date
+ user.created_at.strftime("%Y-%m-%d")
+ end
+
def level
user.level_string
end
@@ -26,7 +30,7 @@ class UserPresenter
def upload_limit
deleted_count = Post.for_user(user.id).deleted.count
pending_count = Post.for_user(user.id).pending.count
- approved_count = Post.where("is_flagged = false and is_pending = false and user_id = ?", user.id).count
+ approved_count = Post.where("is_flagged = false and is_pending = false and uploader_id = ?", user.id).count
if user.base_upload_limit
limit = user.base_upload_limit - pending_count
@@ -89,7 +93,7 @@ class UserPresenter
if user.inviter_id
template.link_to(user.inviter.name, template.user_path(user.inviter_id))
else
- nil
+ "None"
end
end
@@ -102,6 +106,16 @@ class UserPresenter
neutral = UserFeedback.for_user(user.id).neutral.count
negative = UserFeedback.for_user(user.id).negative.count
- template.link_to("positive:#{positive} neutral:#{neutral} negative:#{negative}", user_feedbacks_path(:search => {:user_id_rq => user.id}))
+ template.link_to("positive:#{positive} neutral:#{neutral} negative:#{negative}", template.user_feedbacks_path(:search => {:user_id_rq => user.id}))
+ end
+
+ def subscriptions(template)
+ if user.subscriptions.any?
+ user.subscriptions.map do |subscription|
+ template.link_to(subscription.name, template.posts_path(:tags => "sub:#{user.name}:#{subscription.name}"))
+ end.join(", ").html_safe
+ else
+ "None"
+ end
end
end
diff --git a/app/views/advertisements/edit.html.erb b/app/views/advertisements/edit.html.erb
index ca4e7466b..61a745a01 100644
--- a/app/views/advertisements/edit.html.erb
+++ b/app/views/advertisements/edit.html.erb
@@ -1,3 +1,9 @@
-<%= image_tag(@advertisement.image_url) %>
-<%= render "form" %>
+
+
+
Edit Advertisement
+ <%= image_tag(@advertisement.image_url) %>
+ <%= render "form" %>
+
+
+
<%= render "secondary_links" %>
diff --git a/app/views/advertisements/index.html.erb b/app/views/advertisements/index.html.erb
index 17a5381db..3ee38a416 100644
--- a/app/views/advertisements/index.html.erb
+++ b/app/views/advertisements/index.html.erb
@@ -1,22 +1,26 @@
-
Advertisements
+
+
+
Advertisements
-
-
-
-
- Hits
-
-
-
-
- <% @advertisements.each do |advertisement| %>
-
- <%= image_tag(advertisement.image_url, :width => advertisement.preview_width, :height => advertisement.preview_height) %>
- <%= advertisement.hits.between(@start_date, @end_date).count %>
- <%= link_to "Edit", edit_advertisement_path(advertisement) %>
-
- <% end %>
-
-
+
+
+
+
+ Hits
+
+
+
+
+ <% @advertisements.each do |advertisement| %>
+
+ <%= image_tag(advertisement.image_url, :width => advertisement.preview_width, :height => advertisement.preview_height) %>
+ <%= advertisement.hits.between(@start_date, @end_date).count %>
+ <%= link_to "Edit", edit_advertisement_path(advertisement) %>
+
+ <% end %>
+
+
+
+
<%= render "secondary_links" %>
diff --git a/app/views/advertisements/new.html.erb b/app/views/advertisements/new.html.erb
index 319fd8451..0bf018729 100644
--- a/app/views/advertisements/new.html.erb
+++ b/app/views/advertisements/new.html.erb
@@ -1,3 +1,9 @@
-<%= error_messages_for :advertisement %>
-<%= render "form" %>
+
+
+
New Advertisement
+ <%= error_messages_for :advertisement %>
+ <%= render "form" %>
+
+
+
<%= render "secondary_links" %>
diff --git a/app/views/advertisements/show.html.erb b/app/views/advertisements/show.html.erb
index 302d08888..0248fc5ca 100644
--- a/app/views/advertisements/show.html.erb
+++ b/app/views/advertisements/show.html.erb
@@ -1,7 +1,14 @@
-
<%= image_tag(@advertisement.image_url, :width => @advertisement.preview_width, :height => @advertisement.preview_height) %>
+
+
+
Show Advertisement
+
+ <%= image_tag(@advertisement.image_url, :width => @advertisement.preview_width, :height => @advertisement.preview_height) %>
+
+
+ Hits: <%= @advertisement.hits.between(@start_date, @end_date).count %>
+
+
+
-
- Hits: <%= @advertisement.hits.between(@start_date, @end_date).count %>
-
<%= render "secondary_links" %>
diff --git a/app/views/artists/banned.html.erb b/app/views/artists/banned.html.erb
new file mode 100644
index 000000000..abf9ca387
--- /dev/null
+++ b/app/views/artists/banned.html.erb
@@ -0,0 +1,13 @@
+
+
+
Banned Artists
+
+
The following artists have requested they be removed from the site. Please do not upload any works from these artists. These artists all have implications pointing to the banned_artist tag.
+
+
+ <% @artists.each do |artist| %>
+ <%= link_to artist.name, artist_path(artist) %>
+ <% end %>
+
+
+
\ No newline at end of file
diff --git a/app/views/artists/index.html.erb b/app/views/artists/index.html.erb
index 9de28e6bc..003ef1a7d 100644
--- a/app/views/artists/index.html.erb
+++ b/app/views/artists/index.html.erb
@@ -1,5 +1,7 @@
+
Artists
+
diff --git a/app/views/bans/edit.html.erb b/app/views/bans/edit.html.erb
index 0e92665d9..3ec7b0115 100644
--- a/app/views/bans/edit.html.erb
+++ b/app/views/bans/edit.html.erb
@@ -1,7 +1,7 @@
Edit Ban
- <%= render :partial => "form", :locals => {:ban => @ban} %>
+ <%= render "form", :ban => @ban %>
diff --git a/app/views/bans/index.html.erb b/app/views/bans/index.html.erb
index 11f8196fa..6f15f1670 100644
--- a/app/views/bans/index.html.erb
+++ b/app/views/bans/index.html.erb
@@ -1,5 +1,7 @@
+
Bans
+
diff --git a/app/views/bans/new.html.erb b/app/views/bans/new.html.erb
index 8c5eaa8e9..32fa507e3 100644
--- a/app/views/bans/new.html.erb
+++ b/app/views/bans/new.html.erb
@@ -1,7 +1,7 @@
New Ban
- <%= render :partial => "form", :locals => {:ban => @ban} %>
+ <%= render "form", :ban => @ban %>
diff --git a/app/views/comments/create.js.erb b/app/views/comments/create.js.erb
new file mode 100644
index 000000000..57cd03d21
--- /dev/null
+++ b/app/views/comments/create.js.erb
@@ -0,0 +1 @@
+$("div.comments-for-post[data-post-id=<%= @comment.post_id %>] div.list-of-comments").append("<%= escape_javascript(render('comments/partials/show/comment', :comment => @comment)) %>");
\ No newline at end of file
diff --git a/app/views/comments/create.js.rjs b/app/views/comments/create.js.rjs
deleted file mode 100644
index ca608485d..000000000
--- a/app/views/comments/create.js.rjs
+++ /dev/null
@@ -1 +0,0 @@
-page.insert_html(:bottom, "div.comments-for-post[data-post-id=#{@comment.post_id}] div.list-of-comments", :partial => "comments/partials/show/comment", :locals => {:comment => @comment})
diff --git a/app/views/comments/index_by_comment.html.erb b/app/views/comments/index_by_comment.html.erb
index 1360609df..e578873aa 100644
--- a/app/views/comments/index_by_comment.html.erb
+++ b/app/views/comments/index_by_comment.html.erb
@@ -1,9 +1,11 @@
-<%= render "comments/secondary_links" %>
\ No newline at end of file
+<%= render "comments/secondary_links" %>
diff --git a/app/views/comments/partials/index/_list.html.erb b/app/views/comments/partials/index/_list.html.erb
index f80b48599..6cf5d0a19 100644
--- a/app/views/comments/partials/index/_list.html.erb
+++ b/app/views/comments/partials/index/_list.html.erb
@@ -1,6 +1,6 @@
diff --git a/app/views/posts/index.html.erb b/app/views/posts/index.html.erb
index dcfb18e8a..1fd067734 100644
--- a/app/views/posts/index.html.erb
+++ b/app/views/posts/index.html.erb
@@ -1,28 +1,28 @@
- <%= render :partial => "wiki_pages/excerpt", :locals => {:post_set => @post_set} %>
- <%= render :partial => "posts/partials/index/posts", :locals => {:post_set => @post_set} %>
+ <%= render "wiki_pages/excerpt", :post_set => @post_set %>
+ <%= render "posts/partials/index/posts", :post_set => @post_set %>
<% content_for(:page_title) do %>
/<%= @post_set.tag_string %>
<% end %>
- <%= render :partial => "posts/partials/common/secondary_links" %>
+ <%= render "posts/partials/common/secondary_links" %>
diff --git a/app/views/posts/partials/index/_mode_menu.html.erb b/app/views/posts/partials/index/_mode_menu.html.erb
index a0dcaa66d..ce2bfb157 100644
--- a/app/views/posts/partials/index/_mode_menu.html.erb
+++ b/app/views/posts/partials/index/_mode_menu.html.erb
@@ -16,9 +16,6 @@
Lock notes
Edit tag script
Apply tag script
- <% if CurrentUser.user.is_janitor? %>
- Approve
- <% end %>
diff --git a/app/views/posts/show.html.erb b/app/views/posts/show.html.erb
index 487bb8009..708c0f54f 100644
--- a/app/views/posts/show.html.erb
+++ b/app/views/posts/show.html.erb
@@ -1,12 +1,12 @@
- <%= render :partial => "posts/partials/show/notices", :locals => {:post => @post} %>
+ <%= render "posts/partials/show/notices", :post => @post %>
@@ -50,7 +50,7 @@
- <%= render :partial => "posts/partials/show/edit", :locals => {:post => @post} %>
+ <%= render "posts/partials/show/edit", :post => @post %>
@@ -93,4 +93,4 @@
<% end %>
-<%= render :partial => "posts/partials/common/secondary_links" %>
+<%= render "posts/partials/common/secondary_links" %>
diff --git a/app/views/report/common.html.erb b/app/views/report/common.html.erb
deleted file mode 100644
index fe7889d59..000000000
--- a/app/views/report/common.html.erb
+++ /dev/null
@@ -1,66 +0,0 @@
-
Report: <%= @report_title %>
-
-
-
- <% form_tag({:action => params[:action]}, :method => :get) do %>
-
-
-
- <%= submit_tag "Search" %>
-
-
-
-
- Start Date
- <%= text_field_tag "start_date", @start_date, :size => 10 %>
-
-
- End Date
- <%= text_field_tag "end_date", @end_date, :size => 10 %>
-
-
- Limit
- <%= text_field_tag "limit", @limit, :size => 5 %>
-
-
- Level
- <%= user_level_select_tag "level", :include_blank => true %>
-
-
-
- <% end %>
-
-
-
-
-
-
- User
- Changes
- Percentage
-
-
-
- Total
- <%= @users[0]["sum"].to_i %>
-
-
-
- <% @users.each do |user| %>
-
- <%= link_to_unless user["id"].nil?, h(user["name"]), :controller => "user", :action => "show", :id => user["id"] %>
- <%= link_to_unless user["id"].nil?, user["change_count"], @change_params.call(user["id"]) %>
- <%= number_to_percentage 100 * user["change_count"] / user["sum"], :precision => 1 %>
-
- <% end %>
-
-
-
-
-
-<% content_for("subnavbar") do %>
-
<%= link_to "Tags", :action => "tag_updates", :start_date => @start_date, :end_date => @end_date %>
-
<%= link_to "Notes", :action => "note_updates", :start_date => @start_date, :end_date => @end_date %>
-
<%= link_to "Wiki", :action => "wiki_updates", :start_date => @start_date, :end_date => @end_date %>
-
<%= link_to "Uploads", :action => "post_uploads", :start_date => @start_date, :end_date => @end_date %>
-<% end %>
diff --git a/app/views/report/tag_history.html.erb b/app/views/report/tag_history.html.erb
deleted file mode 100644
index 6fe89e644..000000000
--- a/app/views/report/tag_history.html.erb
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-<% form_tag({:action => "tag_history"}, :method => :get) do %>
-
-
-
- Tags
- <%= text_field_tag "tag", params[:tag], :size => 50 %>
-
-
- Start Date
- <%= text_field_tag "start_date", @start_date %>
-
-
- End Date
- <%= text_field_tag "end_date", @end_date %>
-
-
- <%= submit_tag "Search" %>
-
-
-
-<% end %>
-
-
-
-<% if @counts %>
-
-<% end %>
\ No newline at end of file
diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb
index 078a0a7e4..01dac73fe 100644
--- a/app/views/sessions/new.html.erb
+++ b/app/views/sessions/new.html.erb
@@ -1,5 +1,5 @@
-
-
+
+
Login
<%= form_tag(session_path, :class => "simple_form") do %>
diff --git a/app/views/tag_subscriptions/posts.html.erb b/app/views/tag_subscriptions/posts.html.erb
index f9bb2133b..83d2cc802 100644
--- a/app/views/tag_subscriptions/posts.html.erb
+++ b/app/views/tag_subscriptions/posts.html.erb
@@ -1,11 +1,11 @@
@@ -20,7 +20,7 @@
/fav:<%= CurrentUser.name %>
<% end %>
- <%= render :partial => "posts/partials/common/secondary_links" %>
+ <%= render "posts/partials/common/secondary_links" %>
diff --git a/app/views/uploads/index.html.erb b/app/views/uploads/index.html.erb
index 4e69ab83e..6d5f4b727 100644
--- a/app/views/uploads/index.html.erb
+++ b/app/views/uploads/index.html.erb
@@ -27,4 +27,4 @@
-<%= render :partial => "posts/partials/common/secondary_links" %>
\ No newline at end of file
+<%= render "posts/partials/common/secondary_links" %>
\ No newline at end of file
diff --git a/app/views/uploads/new.html.erb b/app/views/uploads/new.html.erb
index a0d898bac..a32419019 100644
--- a/app/views/uploads/new.html.erb
+++ b/app/views/uploads/new.html.erb
@@ -59,4 +59,4 @@
/ Upload
<% end %>
-<%= render :partial => "posts/partials/common/secondary_links" %>
+<%= render "posts/partials/common/secondary_links" %>
diff --git a/app/views/uploads/show.html.erb b/app/views/uploads/show.html.erb
index 97a0ee0ea..e761d92e2 100644
--- a/app/views/uploads/show.html.erb
+++ b/app/views/uploads/show.html.erb
@@ -10,6 +10,8 @@
This upload has finished processing. <%= link_to "View the post", post_path(@upload.post_id) %>.
<% elsif @upload.is_pending? %>
This upload is waiting to be processed. Please wait a few seconds.
+<% elsif @upload.is_processing? %>
+
This upload is being processed. Please wait a few seconds.
<% else %>
An error occurred: <%= @upload.status %>
<% end %>
@@ -21,4 +23,4 @@
<% end %>
-<%= render :partial => "posts/partials/common/secondary_links" %>
+<%= render "posts/partials/common/secondary_links" %>
diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb
index 3ed9c19ea..08902ef97 100644
--- a/app/views/users/show.html.erb
+++ b/app/views/users/show.html.erb
@@ -1,3 +1,54 @@
-
Namasté <%= CurrentUser.pretty_name %>!
+
+
+
Show User: <%= CurrentUser.pretty_name %>
-<%= render :partial => "secondary_links" %>
\ No newline at end of file
+
+ Join Date
+ <%= @presenter.join_date %>
+
+ Inviter
+ <%= @presenter.inviter(self) %>
+
+ Level
+ <%= @presenter.level %>
+
+ Upload Limit
+ <%= @presenter.upload_limit %>
+
+ Uploads
+ <%= @presenter.uploads(self) %>
+
+ Deleted Uploads
+ <%= @presenter.deleted_uploads(self) %>
+
+ Favorites
+ <%= @presenter.favorites(self) %>
+
+ Subscriptions
+ <%= @presenter.subscriptions(self) %>
+
+ Post Changes
+ <%= @presenter.post_versions(self) %>
+
+ Note Changes
+ <%= @presenter.note_versions(self) %>
+
+ Wiki Page Changes
+ <%= @presenter.wiki_page_versions(self) %>
+
+ Pool Changes
+ <%= @presenter.pool_versions(self) %>
+
+ Forum Posts
+ <%= @presenter.forum_posts(self) %>
+
+ Approvals
+ <%= @presenter.approvals(self) %>
+
+ Feedback
+ <%= @presenter.feedbacks(self) %>
+
+
+
+
+<%= render "secondary_links" %>
\ No newline at end of file
diff --git a/app/views/wiki_page_versions/index.html.erb b/app/views/wiki_page_versions/index.html.erb
index 13706c24d..633788e5f 100644
--- a/app/views/wiki_page_versions/index.html.erb
+++ b/app/views/wiki_page_versions/index.html.erb
@@ -1,6 +1,6 @@
- <%= render :partial => "wiki_pages/sidebar" %>
+ <%= render "wiki_pages/sidebar" %>
Wiki Pages
@@ -37,4 +37,4 @@
-<%= render :partial => "wiki_pages/secondary_links" %>
+<%= render "wiki_pages/secondary_links" %>
diff --git a/app/views/wiki_pages/index.html.erb b/app/views/wiki_pages/index.html.erb
index 5444615bc..6b003f441 100644
--- a/app/views/wiki_pages/index.html.erb
+++ b/app/views/wiki_pages/index.html.erb
@@ -1,6 +1,6 @@
- <%= render :partial => "sidebar" %>
+ <%= render "sidebar" %>
Wiki Pages
diff --git a/config/danbooru_default_config.rb b/config/danbooru_default_config.rb
index f4698b164..3e659bc9c 100644
--- a/config/danbooru_default_config.rb
+++ b/config/danbooru_default_config.rb
@@ -72,6 +72,11 @@ module Danbooru
300
end
+ # If a solid state drive is availble, cache the thumbnails on the SSD to reduce disk seek times.
+ def ssd_path
+ nil
+ end
+
# List of memcached servers
def memcached_servers
%w(localhost:11211)
diff --git a/config/danbooru_local_config.rb b/config/danbooru_local_config.rb
deleted file mode 100644
index 283e02225..000000000
--- a/config/danbooru_local_config.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-module Danbooru
- class CustomConfiguration < Configuration
- # Define your custom overloads here
- def app_name
- "Lorem"
- end
-
- def posts_per_page
- 3
- end
-
- def is_user_restricted?(user)
- !user.is_privileged? || user.name == "ppayne"
- end
-
- def is_post_restricted?(post)
- post.has_tag?("loli") || post.has_tag?("shota")
- end
-
- def custom_html_header_content
- %{
-
-
- }.html_safe
- end
-
- def is_user_advertiser?(user)
- user.is_admin? || user.name == "ppayne"
- end
- end
-end
diff --git a/config/deploy.rb b/config/deploy.rb
new file mode 100644
index 000000000..7dc205399
--- /dev/null
+++ b/config/deploy.rb
@@ -0,0 +1,118 @@
+$:.unshift(File.expand_path("./lib", ENV["rvm_path"]))
+set :rvm_ruby_string, "ruby-1.9.2"
+set :rvm_bin_path, "/usr/local/rvm/bin"
+require 'rvm/capistrano'
+
+set :stages, %w(production staging)
+set :default_stage, "staging"
+require 'capistrano/ext/multistage'
+
+require 'bundler/capistrano'
+
+set :whenever_command, "bundle exec whenever"
+set :whenever_environment, defer {stage}
+require 'whenever/capistrano'
+
+set :application, "danbooru"
+set :repository, "git://github.com/r888888888/danbooru.git"
+set :scm, :git
+set :user, "danbooru"
+set :deploy_to, "/var/www/#{application}"
+
+default_run_options[:pty] = true
+
+namespace :local_config do
+ desc "Create the shared config directory"
+ task :setup_shared_directory do
+ run "mkdir -p #{deploy_to}/shared/config"
+ end
+
+ desc "Initialize local config files"
+ task :setup_local_files do
+ run "curl -s https://raw.github.com/r888888888/danbooru/master/script/install/danbooru_local_config.rb.templ > #{deploy_to}/shared/config/danbooru_local_config.rb"
+ run "curl -s https://raw.github.com/r888888888/danbooru/master/script/install/database.yml.templ > #{deploy_to}/shared/config/database.yml"
+ end
+
+ desc "Link the local config files"
+ task :link_local_files do
+ run "ln -s #{deploy_to}/shared/config/danbooru_local_config.rb #{release_path}/config/danbooru_local_config.rb"
+ run "ln -s #{deploy_to}/shared/config/database.yml #{release_path}/config/database.yml"
+ end
+end
+
+namespace :data do
+ task :setup_directories do
+ run "mkdir -p #{deploy_to}/shared/data"
+ run "mkdir #{deploy_to}/shared/data/preview"
+ run "mkdir #{deploy_to}/shared/data/small"
+ run "mkdir #{deploy_to}/shared/data/large"
+ run "mkdir #{deploy_to}/shared/data/original"
+ end
+
+ task :link_directories do
+ run "rm -f #{release_path}/public/data"
+ run "ln -s #{deploy_to}/shared/data #{release_path}/public/data"
+ end
+end
+
+desc "Change ownership of common directory to user"
+task :reset_ownership_of_common_directory do
+ sudo "chown -R #{user}:#{user} /var/www/danbooru"
+end
+
+namespace :deploy do
+ namespace :web do
+ desc "Present a maintenance page to visitors."
+ task :disable do
+ maintenance_html_path = "#{current_path}/public/maintenance.html.bak"
+ run "if [ -e #{maintenance_html_path} ] ; then mv #{maintenance_html_path} #{current_path}/public/maintenance.html ; fi"
+ end
+
+ desc "Makes the application web-accessible again."
+ task :enable do
+ maintenance_html_path = "#{current_path}/public/maintenance.html"
+ run "if [ -e #{maintenance_html_path} ] ; then mv #{maintenance_html_path} #{current_path}/public/maintenance.html.bak ; fi"
+ end
+ end
+
+ desc "Restart the application"
+ task :restart do
+ run "touch #{current_path}/tmp/restart.txt"
+ end
+
+ desc "Compile the image resizer"
+ task :compile_image_resizer do
+ run "cd #{release_path}/lib/danbooru_image_resizer ; ruby extconf.rb ; make"
+ end
+end
+
+namespace :delayed_job do
+ desc "Start delayed_job process"
+ task :start, :roles => :app do
+ run "cd #{current_path}; RAILS_ENV=#{rails_env} script/delayed_job start"
+ end
+
+ desc "Stop delayed_job process"
+ task :stop, :roles => :app do
+ run "cd #{current_path}; RAILS_ENV=#{rails_env} script/delayed_job stop"
+ end
+
+ desc "Restart delayed_job process"
+ task :restart, :roles => :app do
+ run "cd #{current_path}; RAILS_ENV=#{rails_env} script/delayed_job restart"
+ end
+end
+
+after "deploy:setup", "reset_ownership_of_common_directory"
+after "deploy:setup", "local_config:setup_shared_directory"
+after "deploy:setup", "local_config:setup_local_files"
+after "deploy:setup", "data:setup_directories"
+after "deploy:symlink", "local_config:link_local_files"
+after "deploy:symlink", "data:link_directories"
+after "deploy:symlink", "deploy:compile_image_resizer"
+after "deploy:start", "delayed_job:start"
+after "deploy:stop", "delayed_job:stop"
+after "deploy:restart", "delayed_job:restart"
+before "deploy:update", "deploy:web:disable"
+after "deploy:restart", "deploy:web:enable"
+
diff --git a/config/deploy/production.rb b/config/deploy/production.rb
new file mode 100644
index 000000000..0a59a8671
--- /dev/null
+++ b/config/deploy/production.rb
@@ -0,0 +1,2 @@
+server "sonohara.donmai.us", :web, :app, :primary => true
+server "hijiribe.donmai.us", :web, :app
\ No newline at end of file
diff --git a/config/deploy/staging.rb b/config/deploy/staging.rb
new file mode 100644
index 000000000..1fa48f7ad
--- /dev/null
+++ b/config/deploy/staging.rb
@@ -0,0 +1 @@
+server "testbooru.donmai.us", :web, :app, :db, :primary => true
diff --git a/config/routes.rb b/config/routes.rb
index 868fdb2a2..547d46765 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -38,6 +38,7 @@ Danbooru::Application.routes.draw do
end
collection do
get :search
+ get :banned
end
end
resources :artist_versions, :only => [:index]
diff --git a/config/schedule.rb b/config/schedule.rb
index 53fdc0a39..6bf986368 100644
--- a/config/schedule.rb
+++ b/config/schedule.rb
@@ -1,5 +1,11 @@
set :output, "/var/log/whenever.log"
every 1.hour do
- TagSubscription.process_all
+ runner "TagSubscription.process_all"
+end
+
+if environment == "production"
+ every 1.hour do
+ runner "AmazonBackup.execute"
+ end
end
diff --git a/db/development_structure.sql b/db/development_structure.sql
index 3317975ee..8309e1645 100644
--- a/db/development_structure.sql
+++ b/db/development_structure.sql
@@ -463,6 +463,37 @@ CREATE SEQUENCE advertisements_id_seq
ALTER SEQUENCE advertisements_id_seq OWNED BY advertisements.id;
+--
+-- Name: amazon_backups; Type: TABLE; Schema: public; Owner: -; Tablespace:
+--
+
+CREATE TABLE amazon_backups (
+ id integer NOT NULL,
+ last_id integer,
+ created_at timestamp without time zone,
+ updated_at timestamp without time zone
+);
+
+
+--
+-- Name: amazon_backups_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE amazon_backups_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+
+--
+-- Name: amazon_backups_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE amazon_backups_id_seq OWNED BY amazon_backups.id;
+
+
--
-- Name: artist_urls; Type: TABLE; Schema: public; Owner: -; Tablespace:
--
@@ -2252,7 +2283,7 @@ CREATE TABLE tag_aliases (
creator_id integer NOT NULL,
creator_ip_addr inet NOT NULL,
forum_topic_id integer,
- status character varying(255) DEFAULT 'pending'::character varying NOT NULL,
+ status text DEFAULT 'pending'::text NOT NULL,
created_at timestamp without time zone,
updated_at timestamp without time zone
);
@@ -2289,7 +2320,7 @@ CREATE TABLE tag_implications (
creator_id integer NOT NULL,
creator_ip_addr inet NOT NULL,
forum_topic_id integer,
- status character varying(255) DEFAULT 'pending'::character varying NOT NULL,
+ status text DEFAULT 'pending'::text NOT NULL,
created_at timestamp without time zone,
updated_at timestamp without time zone
);
@@ -2623,6 +2654,13 @@ ALTER TABLE advertisement_hits ALTER COLUMN id SET DEFAULT nextval('advertisemen
ALTER TABLE advertisements ALTER COLUMN id SET DEFAULT nextval('advertisements_id_seq'::regclass);
+--
+-- Name: id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE amazon_backups ALTER COLUMN id SET DEFAULT nextval('amazon_backups_id_seq'::regclass);
+
+
--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
@@ -2877,6 +2915,14 @@ ALTER TABLE ONLY advertisements
ADD CONSTRAINT advertisements_pkey PRIMARY KEY (id);
+--
+-- Name: amazon_backups_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY amazon_backups
+ ADD CONSTRAINT amazon_backups_pkey PRIMARY KEY (id);
+
+
--
-- Name: artist_urls_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
@@ -5224,4 +5270,6 @@ INSERT INTO schema_migrations (version) VALUES ('20110607194023');
INSERT INTO schema_migrations (version) VALUES ('20110717010705');
-INSERT INTO schema_migrations (version) VALUES ('20110722211855');
\ No newline at end of file
+INSERT INTO schema_migrations (version) VALUES ('20110722211855');
+
+INSERT INTO schema_migrations (version) VALUES ('20110815233456');
\ No newline at end of file
diff --git a/db/migrate/20110815233456_create_amazon_backups.rb b/db/migrate/20110815233456_create_amazon_backups.rb
new file mode 100644
index 000000000..25c41e8d6
--- /dev/null
+++ b/db/migrate/20110815233456_create_amazon_backups.rb
@@ -0,0 +1,9 @@
+class CreateAmazonBackups < ActiveRecord::Migration
+ def change
+ create_table :amazon_backups do |t|
+ t.integer :last_id
+
+ t.timestamps
+ end
+ end
+end
diff --git a/lib/danbooru_image_resizer/ConvertToRGB.cpp b/lib/danbooru_image_resizer/ConvertToRGB.cpp
new file mode 100644
index 000000000..eb1e950d3
--- /dev/null
+++ b/lib/danbooru_image_resizer/ConvertToRGB.cpp
@@ -0,0 +1,66 @@
+#include
+#include
+#include
+#include "ConvertToRGB.h"
+#include "Filter.h"
+#include
+using namespace std;
+
+ConvertToRGB::ConvertToRGB(auto_ptr pCompressor):
+ m_pCompressor(pCompressor)
+{
+ m_pBuffer = NULL;
+}
+
+ConvertToRGB::~ConvertToRGB()
+{
+ delete[] m_pBuffer;
+}
+
+bool ConvertToRGB::Init(int iSourceWidth, int iSourceHeight, int iBPP)
+{
+ m_iSourceWidth = iSourceWidth;
+ // m_iSourceHeight = iSourceHeight;
+ m_iBPP = iBPP;
+ m_pBuffer = new uint8_t[iSourceWidth * 3];
+ assert(m_iBPP == 1 || m_iBPP == 3 || m_iBPP == 4); // greyscale, RGB or RGBA
+
+ return m_pCompressor->Init(iSourceWidth, iSourceHeight, 3);
+}
+
+bool ConvertToRGB::WriteRow(uint8_t *pNewRow)
+{
+ if(m_iBPP == 3)
+ return m_pCompressor->WriteRow(pNewRow);
+ if(m_iBPP == 1)
+ {
+ uint8_t *pBuffer = m_pBuffer;
+ for(int i = 0; i < m_iSourceWidth; ++i)
+ {
+ *pBuffer++ = *pNewRow;
+ *pBuffer++ = *pNewRow;
+ *pBuffer++ = *pNewRow;
+ ++pNewRow;
+ }
+ }
+ else if(m_iBPP == 4)
+ {
+ uint8_t *pBuffer = m_pBuffer;
+ for(int i = 0; i < m_iSourceWidth; ++i)
+ {
+ uint8_t iR = *pNewRow++;
+ uint8_t iG = *pNewRow++;
+ uint8_t iB = *pNewRow++;
+ uint8_t iA = *pNewRow++;
+ iR = uint8_t((iR * iA) / 255.0f);
+ iG = uint8_t((iG * iA) / 255.0f);
+ iB = uint8_t((iB * iA) / 255.0f);
+ *pBuffer++ = iR;
+ *pBuffer++ = iG;
+ *pBuffer++ = iB;
+ }
+ }
+
+ return m_pCompressor->WriteRow(m_pBuffer);
+}
+
diff --git a/lib/danbooru_image_resizer/ConvertToRGB.h b/lib/danbooru_image_resizer/ConvertToRGB.h
new file mode 100644
index 000000000..be117c90f
--- /dev/null
+++ b/lib/danbooru_image_resizer/ConvertToRGB.h
@@ -0,0 +1,27 @@
+#ifndef CONVERT_TO_RGB_H
+#define CONVERT_TO_RGB_H
+
+#include "Filter.h"
+#include
+using namespace std;
+
+class ConvertToRGB: public Filter
+{
+public:
+ ConvertToRGB(auto_ptr pCompressor);
+ ~ConvertToRGB();
+
+ bool Init(int iSourceWidth, int iSourceHeight, int BPP);
+ bool WriteRow(uint8_t *pNewRow);
+ bool Finish() { return true; }
+
+ const char *GetError() const { return NULL; }
+
+private:
+ uint8_t *m_pBuffer;
+ auto_ptr m_pCompressor;
+ int m_iSourceWidth;
+ int m_iBPP;
+};
+
+#endif
diff --git a/lib/danbooru_image_resizer/Filter.h b/lib/danbooru_image_resizer/Filter.h
new file mode 100644
index 000000000..03b1093e6
--- /dev/null
+++ b/lib/danbooru_image_resizer/Filter.h
@@ -0,0 +1,16 @@
+#ifndef FILTER_H
+#define FILTER_H
+
+#include
+
+class Filter
+{
+public:
+ virtual ~Filter() { }
+ virtual bool Init(int iSourceWidth, int iSourceHeight, int iSourceBPP) = 0;
+ virtual bool WriteRow(uint8_t *row) = 0;
+ virtual bool Finish() = 0;
+ virtual const char *GetError() const = 0;
+};
+
+#endif
diff --git a/lib/danbooru_image_resizer/GIFReader.cpp b/lib/danbooru_image_resizer/GIFReader.cpp
index f02744e78..b4bbc9eb4 100644
--- a/lib/danbooru_image_resizer/GIFReader.cpp
+++ b/lib/danbooru_image_resizer/GIFReader.cpp
@@ -1,12 +1,11 @@
+#include
#include
#include
#include "GIFReader.h"
-#include "RowBuffer.h"
#include "Resize.h"
-bool GIF::Read(FILE *f, Resizer *resizer, char error[1024])
+bool GIF::Read(FILE *f, Filter *pOutput, char error[1024])
{
- RowBuffer Rows;
bool Ret = false;
gdImage *image = gdImageCreateFromGif(f);
@@ -16,21 +15,18 @@ bool GIF::Read(FILE *f, Resizer *resizer, char error[1024])
return false;
}
- if(!Rows.Init(image->sx, image->sy, 3))
+ uint8_t *pBuf = NULL;
+ pBuf = (uint8_t *) malloc(image->sx * 3);
+ if(pBuf == NULL)
{
strcpy(error, "out of memory");
goto cleanup;
}
- resizer->SetSource(image->sx, image->sy, 3);
+ pOutput->Init(image->sx, image->sy, 3);
for(int y = 0; y < image->sy; ++y)
{
- uint8_t *p = Rows.GetRow(y);
- if(p == NULL)
- {
- strcpy(error, "out of memory");
- goto cleanup;
- }
+ uint8_t *p = pBuf;
for(int x = 0; x < image->sx; ++x)
{
@@ -40,19 +36,25 @@ bool GIF::Read(FILE *f, Resizer *resizer, char error[1024])
(*p++) = gdTrueColorGetBlue(c);
}
- int DiscardRow;
- if(!resizer->Run(Rows.GetRows(), Rows.GetStartRow(), Rows.GetEndRow(), DiscardRow))
+ if(!pOutput->WriteRow(pBuf))
{
- strcpy(error, resizer->GetError());
+ strcpy(error, pOutput->GetError());
goto cleanup;
}
+ }
- Rows.DiscardRows(DiscardRow);
+ if(!pOutput->Finish())
+ {
+ strcpy(error, pOutput->GetError());
+ goto cleanup;
}
Ret = true;
cleanup:
+ if(pBuf != NULL)
+ free(pBuf);
+
gdImageDestroy(image);
return Ret;
}
diff --git a/lib/danbooru_image_resizer/GIFReader.h b/lib/danbooru_image_resizer/GIFReader.h
index 61c200fa5..42488c1b1 100644
--- a/lib/danbooru_image_resizer/GIFReader.h
+++ b/lib/danbooru_image_resizer/GIFReader.h
@@ -2,10 +2,11 @@
#define GIF_READER_H
#include "Reader.h"
+class Filter;
class GIF: public Reader
{
public:
- bool Read(FILE *f, Resizer *resizer, char error[1024]);
+ bool Read(FILE *f, Filter *pOutput, char error[1024]);
};
#endif
diff --git a/lib/danbooru_image_resizer/Histogram.cpp b/lib/danbooru_image_resizer/Histogram.cpp
new file mode 100644
index 000000000..49da2e107
--- /dev/null
+++ b/lib/danbooru_image_resizer/Histogram.cpp
@@ -0,0 +1,48 @@
+#include
+#include
+#include
+#include
+#include
+#include "Histogram.h"
+#include "Filter.h"
+#include
+using namespace std;
+
+Histogram::Histogram()
+{
+ memset(m_Histogram, 0, sizeof(m_Histogram));
+}
+
+bool Histogram::Init(int iSourceWidth, int iSourceHeight, int iBPP)
+{
+ assert(iBPP >= 3);
+ m_SourceWidth = iSourceWidth;
+ m_SourceBPP = iBPP;
+
+ return true;
+}
+int Histogram::GetChannels() const
+{
+ return min(m_SourceBPP, 3);
+}
+
+bool Histogram::WriteRow(uint8_t *pNewRow)
+{
+ uint8_t *pInput = pNewRow;
+ int channels = GetChannels();
+ for(int x = 0; x < m_SourceWidth; ++x)
+ {
+ for(int c = 0; c < channels; ++c)
+ {
+ int color = pInput[c];
+ if(m_SourceBPP == 3)
+ color = (color * pInput[3]) / 255;
+ ++m_Histogram[c][color];
+ }
+
+ pInput += m_SourceBPP;
+ }
+
+ return true;
+}
+
diff --git a/lib/danbooru_image_resizer/Histogram.h b/lib/danbooru_image_resizer/Histogram.h
new file mode 100644
index 000000000..dbbef1ea5
--- /dev/null
+++ b/lib/danbooru_image_resizer/Histogram.h
@@ -0,0 +1,29 @@
+#ifndef HISTOGRAM_H
+#define HISTOGRAM_H
+
+#include "Filter.h"
+#include
+using namespace std;
+#include
+
+class Histogram: public Filter
+{
+public:
+ Histogram();
+
+ bool Init(int iSourceWidth, int iSourceHeight, int BPP);
+ bool WriteRow(uint8_t *pNewRow);
+ bool Finish() { return true; }
+
+ const char *GetError() const { return NULL; }
+ int GetChannels() const;
+ const unsigned *GetHistogram(int iChannel) const { return m_Histogram[iChannel]; }
+
+private:
+ unsigned m_Histogram[3][256];
+
+ int m_SourceWidth;
+ int m_SourceBPP;
+};
+
+#endif
diff --git a/lib/danbooru_image_resizer/JPEGReader.cpp b/lib/danbooru_image_resizer/JPEGReader.cpp
index e3d61eab9..d235940b9 100644
--- a/lib/danbooru_image_resizer/JPEGReader.cpp
+++ b/lib/danbooru_image_resizer/JPEGReader.cpp
@@ -1,6 +1,6 @@
#include
+#include
#include "JPEGReader.h"
-#include "RowBuffer.h"
#include "Resize.h"
#include
using namespace std;
@@ -32,9 +32,14 @@ const char *JPEGCompressor::GetError() const
return m_JErr.buffer;
}
-
-bool JPEGCompressor::Init(int width, int height, int quality)
+void JPEGCompressor::SetQuality(int quality)
{
+ m_iQuality = quality;
+}
+
+bool JPEGCompressor::Init(int width, int height, int bpp)
+{
+ assert(bpp == 3);
m_CInfo.err = jpeg_std_error(&m_JErr.pub);
m_JErr.pub.error_exit = jpeg_error_exit;
@@ -53,8 +58,18 @@ bool JPEGCompressor::Init(int width, int height, int quality)
m_CInfo.in_color_space = JCS_RGB; /* colorspace of input image */
jpeg_set_defaults(&m_CInfo);
- jpeg_simple_progression(&m_CInfo);
- jpeg_set_quality(&m_CInfo, quality, TRUE); // limit to baseline-JPEG values
+ jpeg_set_quality(&m_CInfo, m_iQuality, TRUE); // limit to baseline-JPEG values
+
+ /* For high-quality compression, disable color subsampling. */
+ if(m_iQuality >= 95)
+ {
+ m_CInfo.comp_info[0].h_samp_factor = 1;
+ m_CInfo.comp_info[0].v_samp_factor = 1;
+ m_CInfo.comp_info[1].h_samp_factor = 1;
+ m_CInfo.comp_info[1].v_samp_factor = 1;
+ m_CInfo.comp_info[2].h_samp_factor = 1;
+ m_CInfo.comp_info[2].v_samp_factor = 1;
+ }
jpeg_start_compress(&m_CInfo, TRUE);
@@ -89,13 +104,10 @@ bool JPEGCompressor::Finish()
return true;
}
-bool JPEG::Read(FILE *f, Resizer *resizer, char error[1024])
+bool JPEG::Read(FILE *f, Filter *pOutput, char error[1024])
{
// JMSG_LENGTH_MAX <= sizeof(error)
- m_JErr.buffer = error;
- RowBuffer Rows;
-
- m_Resizer = resizer;
+ m_pOutputFilter = pOutput;
struct jpeg_decompress_struct CInfo;
CInfo.err = jpeg_std_error(&m_JErr.pub);
@@ -103,44 +115,55 @@ bool JPEG::Read(FILE *f, Resizer *resizer, char error[1024])
m_JErr.pub.emit_message = jpeg_warning;
bool Ret = false;
+ uint8_t *pBuf = NULL;
if(setjmp(m_JErr.setjmp_buffer))
+ {
+ memcpy(error, m_JErr.buffer, JMSG_LENGTH_MAX);
goto cleanup;
+ }
jpeg_create_decompress(&CInfo);
jpeg_stdio_src(&CInfo, f);
jpeg_read_header(&CInfo, TRUE);
CInfo.out_color_space = JCS_RGB;
+ if(CInfo.jpeg_color_space == JCS_CMYK || CInfo.jpeg_color_space == JCS_YCCK)
+ {
+ strcpy(error, "CMYK JPEGs are not supported; please convert to RGB");
+ goto cleanup;
+ }
jpeg_start_decompress(&CInfo);
- if(!Rows.Init(CInfo.output_width, CInfo.output_height, 3))
+ if(!m_pOutputFilter->Init(CInfo.output_width, CInfo.output_height, 3))
+ {
+ strncpy(error, m_pOutputFilter->GetError(), sizeof(error));
+ error[sizeof(error)-1] = 0;
+ goto cleanup;
+ }
+
+ pBuf = (uint8_t *) malloc(CInfo.output_width * 3);
+ if(pBuf == NULL)
{
strcpy(error, "out of memory");
goto cleanup;
}
- m_Resizer->SetSource(CInfo.output_width, CInfo.output_height, 3);
-
while(CInfo.output_scanline < CInfo.output_height)
{
- uint8_t *p = Rows.GetRow(CInfo.output_scanline);
- if(p == NULL)
+ jpeg_read_scanlines(&CInfo, &pBuf, 1);
+
+ if(!m_pOutputFilter->WriteRow(pBuf))
{
- strcpy(error, "out of memory");
+ strcpy(error, m_pOutputFilter->GetError());
goto cleanup;
}
+ }
- jpeg_read_scanlines(&CInfo, &p, 1);
-
- int DiscardRow;
- if(!m_Resizer->Run(Rows.GetRows(), Rows.GetStartRow(), min(Rows.GetEndRow(), (int) CInfo.output_scanline+1), DiscardRow))
- {
- strcpy(error, m_Resizer->GetError());
- goto cleanup;
- }
-
- Rows.DiscardRows(DiscardRow);
+ if(!m_pOutputFilter->Finish())
+ {
+ strcpy(error, m_pOutputFilter->GetError());
+ goto cleanup;
}
jpeg_finish_decompress(&CInfo);
@@ -148,6 +171,8 @@ bool JPEG::Read(FILE *f, Resizer *resizer, char error[1024])
Ret = true;
cleanup:
+ if(pBuf != NULL)
+ free(pBuf);
jpeg_destroy_decompress(&CInfo);
return Ret;
diff --git a/lib/danbooru_image_resizer/JPEGReader.h b/lib/danbooru_image_resizer/JPEGReader.h
index 14479979e..06c8625c6 100644
--- a/lib/danbooru_image_resizer/JPEGReader.h
+++ b/lib/danbooru_image_resizer/JPEGReader.h
@@ -6,31 +6,33 @@
#include
#include "jpeglib-extern.h"
#include "Reader.h"
+#include "Filter.h"
struct jpeg_error
{
struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
- char *buffer;
+ char buffer[JMSG_LENGTH_MAX];
};
class JPEG: public Reader
{
public:
- bool Read(FILE *f, Resizer *resizer, char error[1024]);
+ bool Read(FILE *f, Filter *pOutput, char error[1024]);
private:
- Resizer *m_Resizer;
+ Filter *m_pOutputFilter;
struct jpeg_error m_JErr;
};
-class JPEGCompressor
+class JPEGCompressor: public Filter
{
public:
JPEGCompressor(FILE *f);
~JPEGCompressor();
- bool Init(int width, int height, int quality);
+ bool Init(int iSourceWidth, int iSourceHeight, int iBPP);
+ void SetQuality(int quality);
bool WriteRow(uint8_t *row);
bool Finish();
@@ -40,6 +42,7 @@ public:
private:
FILE *m_File;
+ int m_iQuality;
struct jpeg_compress_struct m_CInfo;
struct jpeg_error m_JErr;
};
diff --git a/lib/danbooru_image_resizer/Makefile b/lib/danbooru_image_resizer/Makefile
index 88a6fd1ac..b4690b847 100644
--- a/lib/danbooru_image_resizer/Makefile
+++ b/lib/danbooru_image_resizer/Makefile
@@ -4,11 +4,11 @@ SHELL = /bin/sh
#### Start of system configuration section. ####
srcdir = .
-topdir = /Users/ayi/.rvm/rubies/ruby-1.9.2-preview1/include/ruby-1.9.1
-hdrdir = /Users/ayi/.rvm/rubies/ruby-1.9.2-preview1/include/ruby-1.9.1
-arch_hdrdir = /Users/ayi/.rvm/rubies/ruby-1.9.2-preview1/include/ruby-1.9.1/$(arch)
+topdir = /Users/ayi/.rvm/rubies/ruby-1.9.2-p0/include/ruby-1.9.1
+hdrdir = /Users/ayi/.rvm/rubies/ruby-1.9.2-p0/include/ruby-1.9.1
+arch_hdrdir = /Users/ayi/.rvm/rubies/ruby-1.9.2-p0/include/ruby-1.9.1/$(arch)
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
-prefix = $(DESTDIR)/Users/ayi/.rvm/rubies/ruby-1.9.2-preview1
+prefix = $(DESTDIR)/Users/ayi/.rvm/rubies/ruby-1.9.2-p0
rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
exec_prefix = $(prefix)
vendorhdrdir = $(rubyhdrdir)/vendor_ruby
@@ -45,7 +45,7 @@ vendorarchdir = $(vendorlibdir)/$(sitearch)
CC = g++
CXX = g++
-LIBRUBY = $(LIBRUBY_A)
+LIBRUBY = $(LIBRUBY_SO)
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static
@@ -55,29 +55,29 @@ COUTFLAG = -o
RUBY_EXTCONF_H =
cflags = $(optflags) $(debugflags) $(warnflags)
optflags = -O3
-debugflags = -g
-warnflags = -Wall -Wno-unused-parameter -Wno-parentheses -Wno-missing-field-initializers -Wshorten-64-to-32 -Wpointer-arith -Wwrite-strings
-CFLAGS = -fno-common -O2 -fno-exceptions -Wall
+debugflags = -ggdb
+warnflags = -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-strings -Wno-missing-field-initializers -Wshorten-64-to-32 -Wno-long-long
+CFLAGS = -fno-common -O2 -Wall
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
DEFS =
CPPFLAGS = -DHAVE_GD_H -DHAVE_GDIMAGECREATEFROMGIF -DHAVE_GDIMAGEJPEG -DHAVE_JPEG_SET_QUALITY -DHAVE_PNG_SET_EXPAND_GRAY_1_2_4_TO_8 -I/opt/local/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE $(DEFS) $(cppflags)
CXXFLAGS = $(CFLAGS) $(cxxflags)
ldflags = -L.
-dldflags =
-archflag =
-DLDFLAGS = $(ldflags) $(dldflags) $(archflag)
-LDSHARED = $(CC) -dynamic -bundle -undefined suppress -flat_namespace
-LDSHAREDXX = $(CXX) -dynamic -bundle -undefined suppress -flat_namespace
+dldflags = -Wl,-undefined,dynamic_lookup -Wl,-multiply_defined,suppress -Wl,-flat_namespace
+ARCH_FLAG =
+DLDFLAGS = $(ldflags) $(dldflags)
+LDSHARED = $(CC) -dynamic -bundle
+LDSHAREDXX = $(CXX) -dynamic -bundle
AR = ar
EXEEXT =
RUBY_BASE_NAME = ruby
RUBY_INSTALL_NAME = ruby
-RUBY_SO_NAME = ruby
-arch = i386-darwin10.4.0
+RUBY_SO_NAME = ruby.1.9.1
+arch = x86_64-darwin10.4.0
sitearch = $(arch)
ruby_version = 1.9.1
-ruby = /Users/ayi/.rvm/rubies/ruby-1.9.2-preview1/bin/ruby
+ruby = /Users/ayi/.rvm/rubies/ruby-1.9.2-p0/bin/ruby
RUBY = $(ruby)
RM = rm -f
RM_RF = $(RUBY) -run -e rm -- -rf
@@ -104,9 +104,9 @@ extout =
extout_prefix =
target_prefix =
LOCAL_LIBS =
-LIBS = -lpng -ljpeg -lgd -lpthread -ldl -lobjc
-SRCS = danbooru_image_resizer.cpp GIFReader.cpp JPEGReader.cpp PNGReader.cpp Resize.cpp RowBuffer.cpp
-OBJS = danbooru_image_resizer.o GIFReader.o JPEGReader.o PNGReader.o Resize.o RowBuffer.o
+LIBS = $(LIBRUBYARG_SHARED) -lpng -ljpeg -lgd -lpthread -ldl -lobjc
+SRCS = ConvertToRGB.cpp danbooru_image_resizer.cpp GIFReader.cpp Histogram.cpp JPEGReader.cpp PNGReader.cpp Resize.cpp
+OBJS = ConvertToRGB.o danbooru_image_resizer.o GIFReader.o Histogram.o JPEGReader.o PNGReader.o Resize.o
TARGET = danbooru_image_resizer
DLLIB = $(TARGET).bundle
EXTSTATIC =
@@ -147,8 +147,9 @@ install: install-so install-rb
install-so: $(RUBYARCHDIR)
install-so: $(RUBYARCHDIR)/$(DLLIB)
-$(RUBYARCHDIR)/$(DLLIB): $(RUBYARCHDIR) $(DLLIB)
- $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
+$(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
+ @-$(MAKEDIRS) $(@D)
+ $(INSTALL_PROG) $(DLLIB) $(@D)
install-rb: pre-install-rb install-rb-default
install-rb-default: pre-install-rb-default
pre-install-rb: Makefile
diff --git a/lib/danbooru_image_resizer/PNGReader.cpp b/lib/danbooru_image_resizer/PNGReader.cpp
index d65886f19..7fa1bd7a7 100644
--- a/lib/danbooru_image_resizer/PNGReader.cpp
+++ b/lib/danbooru_image_resizer/PNGReader.cpp
@@ -44,7 +44,7 @@ void PNG::InfoCallback(png_struct *png, png_info *info_ptr)
png_read_update_info(png, info_ptr);
- data->m_Resizer->SetSource(width, height, 4);
+ data->m_pOutputFilter->Init(width, height, 4);
}
void PNG::RowCallback(png_struct *png, png_byte *new_row, png_uint_32 row_num, int pass)
@@ -62,14 +62,12 @@ void PNG::RowCallback(png_struct *png, png_byte *new_row, png_uint_32 row_num, i
/* We've allocated data->m_RowsAllocated, but if we're doing multiple passes, only
* rows 0 to row_num will actually have usable data. */
- int DiscardRow;
- int LastRow = min(data->m_Rows.GetEndRow(), (int) row_num+1);
- if(!data->m_Resizer->Run(data->m_Rows.GetRows(), data->m_Rows.GetStartRow(), LastRow, DiscardRow))
- Error(png, data->m_Resizer->GetError());
+ if(!data->m_pOutputFilter->WriteRow(p))
+ Error(png, data->m_pOutputFilter->GetError());
/* If we're interlaced, never discard rows. */
if(data->m_Passes == 1)
- data->m_Rows.DiscardRows(DiscardRow);
+ data->m_Rows.DiscardRows(row_num+1);
}
void PNG::EndCallback(png_struct *png, png_info *info)
@@ -79,9 +77,9 @@ void PNG::EndCallback(png_struct *png, png_info *info)
}
-bool PNG::Read(FILE *f, Resizer *resizer, char error[1024])
+bool PNG::Read(FILE *f, Filter *pOutput, char error[1024])
{
- m_Resizer = resizer;
+ m_pOutputFilter = pOutput;
png_error_info err;
err.err = error;
@@ -125,6 +123,9 @@ bool PNG::Read(FILE *f, Resizer *resizer, char error[1024])
png_process_data(png, info_ptr, buf, ret);
}
+ if(!m_pOutputFilter->Finish())
+ Error(png, m_pOutputFilter->GetError());
+
if(!m_Done)
{
strcpy(error, "incomplete file");
diff --git a/lib/danbooru_image_resizer/PNGReader.h b/lib/danbooru_image_resizer/PNGReader.h
index 5eeabc847..a065152d3 100644
--- a/lib/danbooru_image_resizer/PNGReader.h
+++ b/lib/danbooru_image_resizer/PNGReader.h
@@ -3,6 +3,7 @@
#include
#include "Reader.h"
+#include "Filter.h"
#include "RowBuffer.h"
struct png_error_info
@@ -18,11 +19,11 @@ public:
m_Done = false;
}
- bool Read(FILE *f, Resizer *resizer, char error[1024]);
+ bool Read(FILE *f, Filter *pOutput, char error[1024]);
private:
- RowBuffer m_Rows;
- Resizer *m_Resizer;
+ RowBuffer m_Rows;
+ Filter *m_pOutputFilter;
bool m_Done;
int m_Passes;
diff --git a/lib/danbooru_image_resizer/Reader.h b/lib/danbooru_image_resizer/Reader.h
index c7b6892f6..eb217ccfd 100644
--- a/lib/danbooru_image_resizer/Reader.h
+++ b/lib/danbooru_image_resizer/Reader.h
@@ -3,12 +3,12 @@
#include
-class Resizer;
+class Filter;
class Reader
{
public:
virtual ~Reader() { }
- virtual bool Read(FILE *f, Resizer *rp, char errorbuf[1024]) = 0;
+ virtual bool Read(FILE *f, Filter *rp, char errorbuf[1024]) = 0;
};
#endif
diff --git a/lib/danbooru_image_resizer/Resize.cpp b/lib/danbooru_image_resizer/Resize.cpp
index a6ad03fef..353f0b8ae 100644
--- a/lib/danbooru_image_resizer/Resize.cpp
+++ b/lib/danbooru_image_resizer/Resize.cpp
@@ -1,17 +1,101 @@
#include
#include
#include
+#include
#include
#include "Resize.h"
-#include "JPEGReader.h"
+#include "Filter.h"
#include
using namespace std;
-Resizer::Resizer(JPEGCompressor *Compressor)
+namespace
{
- m_Compressor = Compressor;
+ inline float sincf(float x)
+ {
+ if(fabsf(x) < 1e-9)
+ return 1.0;
+
+ return sinf(x) / x;
+ }
+
+ inline double fract(double f)
+ {
+ return f - floor(f);
+ }
+}
+
+static const int KERNEL_SIZE = 3;
+
+LanczosFilter::LanczosFilter()
+{
+ m_pFilters = NULL;
+}
+
+LanczosFilter::~LanczosFilter()
+{
+ delete[] m_pFilters;
+}
+
+void LanczosFilter::Init(float fFactor)
+{
+ /* If we're reducing the image, each output pixel samples each input pixel in the
+ * range once, so we step one pixel. If we're enlarging it by 2x, each output pixel
+ * samples each input pixel twice, so we step half a pixel. */
+ m_fStep = 1;
+ if(fFactor > 1.0)
+ m_fStep = 1.0 / fFactor;
+
+ /* If we're sampling each pixel twice (m_fStep is .5), then we need twice as many taps
+ * to sample KERNEL_SIZE pixels. */
+ m_iTaps = (int) ceil(KERNEL_SIZE / m_fStep) * 2;
+
+ delete[] m_pFilters;
+ m_pFilters = NULL; // in case of exception
+ m_pFilters = new float[m_iTaps * 256];
+
+ float *pOutput = m_pFilters;
+ for(int i=0; i < 256; ++i)
+ {
+ float fOffset = i / 256.0f;
+
+ float fSum = 0;
+ for(int i = 0; i < m_iTaps; ++i)
+ {
+ float fPos = -(m_iTaps/2-1) - fOffset + i;
+ fPos *= m_fStep;
+
+ float fValue = 0;
+ if(fabs(fPos) < KERNEL_SIZE)
+ fValue = sincf(M_PI*fPos) * sincf(M_PI / KERNEL_SIZE * fPos);
+
+ pOutput[i] = fValue;
+ fSum += fValue;
+ }
+
+ /* Scale the filter so it sums to 1. */
+ for(int i = 0; i pOutput):
+ m_pCompressor(pOutput)
+{
+ m_DestWidth = -1;
+ m_DestHeight = -1;
m_CurrentY = 0;
m_OutBuf = NULL;
+ m_szError = NULL;
+ m_iInputY = 0;
}
Resizer::~Resizer()
@@ -22,160 +106,178 @@ Resizer::~Resizer()
const char *Resizer::GetError() const
{
- return m_Compressor->GetError();
+ if(m_szError != NULL)
+ return m_szError;
+ return m_pCompressor->GetError();
}
-void Resizer::SetSource(int Width, int Height, int BPP)
+bool Resizer::Init(int iSourceWidth, int iSourceHeight, int iBPP)
{
- m_SourceWidth = Width;
- m_SourceHeight = Height;
- m_SourceBPP = BPP;
-}
+ assert(m_DestWidth != -1);
+ assert(m_DestHeight != -1);
+ assert(iBPP == 3);
+ m_SourceWidth = iSourceWidth;
+ m_SourceHeight = iSourceHeight;
+ m_SourceBPP = iBPP;
-bool Resizer::SetDest(int Width, int Height, int Quality)
-{
- m_DestWidth = Width;
- m_DestHeight = Height;
- m_OutBuf = (uint8_t *) malloc(Width*3);
+ float fXFactor = float(m_SourceWidth) / m_DestWidth;
+ m_XFilter.Init(fXFactor);
- return m_Compressor->Init(Width, Height, Quality);
-}
+ float fYFactor = float(m_SourceHeight) / m_DestHeight;
+ m_YFilter.Init(fYFactor);
-#define scale(x, l1, h1, l2, h2) (((x)-(l1))*((h2)-(l2))/((h1)-(l1))+(l2))
-
-static void Average(const uint8_t *const *src, float Colors[3], float SourceXStart, float SourceXEnd, float SourceYStart, float SourceYEnd, int SourceBPP)
-{
- float Total = 0.0f;
- for(float y = SourceYStart; y < SourceYEnd; ++y)
+ if(!m_Rows.Init(m_DestWidth, m_SourceHeight, m_SourceBPP, m_YFilter.m_iTaps))
{
- float YCoverage = 1.0f;
- if(int(y) == int(SourceYStart))
- YCoverage -= y - int(y);
- if(int(y) == int(SourceYEnd))
- YCoverage -= 1.0f - (SourceYEnd - int(SourceYEnd));
-
- const uint8_t *xsrc=src[(int) y]+(int)SourceXStart*SourceBPP;
-
- /* The two conditionals can only be true on the first and last iteration of the loop,
- * so unfold those iterations and pull the conditionals out of the inner loop. */
-/* while(x < SourceXEnd)
- {
- float XCoverage = 1.0f;
- if(int(x) == int(SourceXStart))
- XCoverage -= x - int(x);
- if(int(x) == int(SourceXEnd))
- XCoverage -= 1.0f - (SourceXEnd - int(SourceXEnd));
-
- Colors[0] += xsrc[0] * XCoverage * YCoverage;
- Colors[1] += xsrc[1] * XCoverage * YCoverage;
- Colors[2] += xsrc[2] * XCoverage * YCoverage;
- if(SourceBPP == 4)
- Colors[3] += xsrc[3] * XCoverage * YCoverage;
- xsrc += SourceBPP;
-
- Total += XCoverage * YCoverage;
- ++x;
- }
-*/
- float x = int(SourceXStart);
- if(x < SourceXEnd)
- {
- float XCoverage = 1.0f;
- if(int(x) == int(SourceXStart))
- XCoverage -= x - int(x);
- if(int(x) == int(SourceXEnd))
- XCoverage -= 1.0f - (SourceXEnd - int(SourceXEnd));
-
- Colors[0] += xsrc[0] * XCoverage * YCoverage;
- Colors[1] += xsrc[1] * XCoverage * YCoverage;
- Colors[2] += xsrc[2] * XCoverage * YCoverage;
- if(SourceBPP == 4)
- Colors[3] += xsrc[3] * XCoverage * YCoverage;
- xsrc += SourceBPP;
-
- Total += XCoverage * YCoverage;
- ++x;
- }
-
- while(x < SourceXEnd-1)
- {
- Colors[0] += xsrc[0] * YCoverage;
- Colors[1] += xsrc[1] * YCoverage;
- Colors[2] += xsrc[2] * YCoverage;
- if(SourceBPP == 4)
- Colors[3] += xsrc[3] * YCoverage;
- xsrc += SourceBPP;
-
- Total += YCoverage;
- ++x;
- }
-
- if(x < SourceXEnd)
- {
- float XCoverage = 1.0f;
- if(int(x) == int(SourceXStart))
- XCoverage -= x - int(x);
- if(int(x) == int(SourceXEnd))
- XCoverage -= 1.0f - (SourceXEnd - int(SourceXEnd));
-
- Colors[0] += xsrc[0] * XCoverage * YCoverage;
- Colors[1] += xsrc[1] * XCoverage * YCoverage;
- Colors[2] += xsrc[2] * XCoverage * YCoverage;
- if(SourceBPP == 4)
- Colors[3] += xsrc[3] * XCoverage * YCoverage;
- xsrc += SourceBPP;
-
- Total += XCoverage * YCoverage;
- }
+ m_szError = "out of memory";
+ return false;
}
- if(Total != 0.0f)
- for(int i = 0; i < 4; ++i)
- Colors[i] /= Total;
+ m_OutBuf = (uint8_t *) malloc(m_DestWidth * m_SourceBPP);
+ if(m_OutBuf == NULL)
+ {
+ m_szError = "out of memory";
+ return false;
+ }
+
+ return m_pCompressor->Init(m_DestWidth, m_DestHeight, m_SourceBPP);
}
-bool Resizer::Run(const uint8_t *const *Source, int StartRow, int EndRow, int &DiscardRow)
+void Resizer::SetDest(int iDestWidth, int iDestHeight)
{
- while(m_CurrentY < m_DestHeight)
+ m_DestWidth = iDestWidth;
+ m_DestHeight = iDestHeight;
+}
+
+static uint8_t *PadRow(const uint8_t *pSourceRow, int iWidth, int iBPP, int iPadding)
+{
+ uint8_t *pRow = new uint8_t[(iWidth + iPadding*2) * iBPP];
+ uint8_t *pDest = pRow;
+ for(int x = 0; x < iPadding; ++x)
{
- float SourceYStart = scale((float) m_CurrentY, 0.0f, (float) m_DestHeight, 0.0f, (float) m_SourceHeight);
- float SourceYEnd = scale((float) m_CurrentY + 1, 0.0f, (float) m_DestHeight, 0.0f, (float) m_SourceHeight);
- DiscardRow = int(SourceYStart)-1;
+ for(int i = 0; i < iBPP; ++i)
+ pDest[i] = pSourceRow[i];
+ pDest += iBPP;
+ }
- if(EndRow != m_SourceHeight && int(SourceYEnd)+1 > EndRow-1)
+ memcpy(pDest, pSourceRow, iWidth*iBPP*sizeof(uint8_t));
+ pDest += iWidth*iBPP;
+
+ for(int x = 0; x < iPadding; ++x)
+ {
+ for(int i = 0; i < iBPP; ++i)
+ pDest[i] = pSourceRow[i];
+ pDest += iBPP;
+ }
+
+ return pRow;
+}
+
+bool Resizer::WriteRow(uint8_t *pNewRow)
+{
+ if(m_SourceWidth == m_DestWidth && m_SourceHeight == m_DestHeight)
+ {
+ ++m_CurrentY;
+
+ /* We don't actually have any resizing to do, so short-circuit. */
+ if(!m_pCompressor->WriteRow((uint8_t *) pNewRow))
+ return false;
+
+ if(m_CurrentY != m_DestHeight)
return true;
- assert(SourceYStart>=StartRow);
- uint8_t *Output = m_OutBuf;
+ return m_pCompressor->Finish();
+ }
+
+ /* Make a copy of pNewRow with the first and last pixel duplicated, so we don't have to do
+ * bounds checking in the inner loop below. */
+ uint8_t *pActualPaddedRow = PadRow(pNewRow, m_SourceWidth, m_SourceBPP, m_XFilter.m_iTaps/2);
+ const uint8_t *pPaddedRow = pActualPaddedRow + (m_XFilter.m_iTaps/2)*m_SourceBPP;
+
+ const float fXFactor = float(m_SourceWidth) / m_DestWidth;
+ const float fYFactor = float(m_SourceHeight) / m_DestHeight;
+
+ /* Run the horizontal filter on the incoming row, and drop the result into m_Rows. */
+ {
+ float *pRow = m_Rows.GetRow(m_iInputY);
+ ++m_iInputY;
+
+ float *pOutput = pRow;
for(int x = 0; x < m_DestWidth; ++x)
{
- float SourceXStart = scale((float) x, 0.0f, (float) m_DestWidth, 0.0f, (float) m_SourceWidth);
- float SourceXEnd = scale((float) x + 1, 0.0f, (float) m_DestWidth, 0.0f, (float) m_SourceWidth);
+ const double fSourceX = (x + 0.5f) * fXFactor;
+ const double fOffset = fract(fSourceX + 0.5);
+ const float *pFilter = m_XFilter.GetFilter(fOffset);
+ const int iStartX = lrint(fSourceX - m_XFilter.m_iTaps/2 + 1e-6);
- float Colors[4] = { 0.0 };
- Average(Source, Colors, SourceXStart, SourceXEnd, SourceYStart, SourceYEnd, m_SourceBPP);
+ const uint8_t *pSource = pPaddedRow + iStartX*3;
- if(m_SourceBPP == 4)
+ float fR = 0, fG = 0, fB = 0;
+ for(int i = 0; i < m_XFilter.m_iTaps; ++i)
{
- for(int i = 0; i < 3; ++i)
- Colors[i] *= Colors[3]/255.0f;
+ float fWeight = *pFilter++;
+
+ fR += pSource[0] * fWeight;
+ fG += pSource[1] * fWeight;
+ fB += pSource[2] * fWeight;
+ pSource += 3;
}
- Output[0] = (uint8_t) min(255, int(Colors[0]));
- Output[1] = (uint8_t) min(255, int(Colors[1]));
- Output[2] = (uint8_t) min(255, int(Colors[2]));
+ pOutput[0] = fR;
+ pOutput[1] = fG;
+ pOutput[2] = fB;
- Output += 3;
+ pOutput += m_SourceBPP;
+ }
+ }
+ delete[] pActualPaddedRow;
+
+ const float *const *pSourceRows = m_Rows.GetRows();
+ while(m_CurrentY < m_DestHeight)
+ {
+ const double fSourceY = (m_CurrentY + 0.5) * fYFactor;
+ const double fOffset = fract(fSourceY + 0.5);
+ const int iStartY = lrint(fSourceY - m_YFilter.m_iTaps/2 + 1e-6);
+
+ /* iStartY is the first row we'll need, and we never move backwards. Discard rows
+ * before it to save memory. */
+ m_Rows.DiscardRows(iStartY);
+
+ if(m_iInputY != m_SourceHeight && iStartY+m_YFilter.m_iTaps >= m_iInputY)
+ return true;
+
+ /* Process the next output row. */
+ uint8_t *pOutput = m_OutBuf;
+ for(int x = 0; x < m_DestWidth; ++x)
+ {
+ const float *pFilter = m_YFilter.GetFilter(fOffset);
+
+ float fR = 0, fG = 0, fB = 0;
+ for(int i = 0; i < m_YFilter.m_iTaps; ++i)
+ {
+ const float *pSource = pSourceRows[iStartY+i];
+ pSource += x * m_SourceBPP;
+
+ float fWeight = *pFilter++;
+ fR += pSource[0] * fWeight;
+ fG += pSource[1] * fWeight;
+ fB += pSource[2] * fWeight;
+ }
+
+ pOutput[0] = (uint8_t) max(0, min(255, (int) lrintf(fR)));
+ pOutput[1] = (uint8_t) max(0, min(255, (int) lrintf(fG)));
+ pOutput[2] = (uint8_t) max(0, min(255, (int) lrintf(fB)));
+
+ pOutput += 3;
}
- if(!m_Compressor->WriteRow((JSAMPLE *) m_OutBuf))
+ if(!m_pCompressor->WriteRow((uint8_t *) m_OutBuf))
return false;
++m_CurrentY;
}
if(m_CurrentY == m_DestHeight)
{
- if(!m_Compressor->Finish())
+ if(!m_pCompressor->Finish())
return false;
}
diff --git a/lib/danbooru_image_resizer/Resize.h b/lib/danbooru_image_resizer/Resize.h
index fb1090a8e..946daada9 100644
--- a/lib/danbooru_image_resizer/Resize.h
+++ b/lib/danbooru_image_resizer/Resize.h
@@ -1,32 +1,43 @@
#ifndef RESIZE_H
#define RESIZE_H
+#include "RowBuffer.h"
+#include "Filter.h"
+#include
+using namespace std;
#include
-class JPEGCompressor;
+struct LanczosFilter
+{
+ LanczosFilter();
+ ~LanczosFilter();
+ void Init(float fFactor);
+ const float *GetFilter(float fOffset) const;
-class Resizer
+ float m_fStep;
+ int m_iTaps;
+ float *m_pFilters;
+};
+
+class Resizer: public Filter
{
public:
- Resizer(JPEGCompressor *Compressor);
+ Resizer(auto_ptr pCompressor);
~Resizer();
// BPP is 3 or 4, indicating RGB or RGBA.
- void SetSource(int Width, int Height, int BPP);
- bool SetDest(int Width, int Height, int Quality);
+ bool Init(int iSourceWidth, int iSourceHeight, int BPP);
+ void SetDest(int iDestWidth, int iDestHeight);
+ bool WriteRow(uint8_t *pNewRow);
+ bool Finish() { return true; }
- /*
- * Resize part of an image.
- *
- * [FirstRow,LastRow) is a range indicating which elements in src[] are available.
- * On return, any rows in [0,DiscardRow) are no longer needed and can be deleted.
- */
- bool Run(const uint8_t *const *src, int FirstRow, int LastRow, int &DiscardRow);
const char *GetError() const;
private:
- JPEGCompressor *m_Compressor;
+ auto_ptr m_pCompressor;
uint8_t *m_OutBuf;
+ RowBuffer m_Rows;
+ const char *m_szError;
int m_SourceWidth;
int m_SourceHeight;
@@ -35,7 +46,11 @@ private:
int m_DestWidth;
int m_DestHeight;
- float m_CurrentY;
+ LanczosFilter m_XFilter;
+ LanczosFilter m_YFilter;
+
+ int m_iInputY;
+ int m_CurrentY;
};
#endif
diff --git a/lib/danbooru_image_resizer/RowBuffer.cpp b/lib/danbooru_image_resizer/RowBuffer.cpp
deleted file mode 100644
index e09179990..000000000
--- a/lib/danbooru_image_resizer/RowBuffer.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-#include
-#include
-#include
-#include "RowBuffer.h"
-#include
-using namespace std;
-
-RowBuffer::RowBuffer()
-{
- m_Rows = NULL;
- m_StartRow = 0;
- m_EndRow = 0;
- m_BPP = 0;
- m_Height = 0;
-}
-
-RowBuffer::~RowBuffer()
-{
- for(int i = 0; i < m_Height; ++i)
- delete [] m_Rows[i];
-
- delete [] m_Rows;
-}
-
-bool RowBuffer::Init(int Width, int Height, int BPP)
-{
- m_Width = Width;
- m_Height = Height;
- m_BPP = BPP;
-
- m_Rows = new uint8_t *[Height];
- if(m_Rows == NULL)
- return false;
- memset(m_Rows, 0, sizeof(uint8_t *) * Height);
-
- return true;
-}
-
-uint8_t *RowBuffer::GetRow(int Row)
-{
- assert(m_BPP > 0);
-
- if(m_Rows[Row] == NULL)
- {
- m_Rows[Row] = new uint8_t[m_Width*m_BPP];
- if(m_Rows[Row] == NULL)
- return NULL;
- if(m_StartRow == m_EndRow)
- {
- m_StartRow = Row;
- m_EndRow = m_StartRow + 1;
- }
- }
-
- if(int(Row) == m_StartRow+1)
- {
- while(m_StartRow != 0 && m_Rows[m_StartRow-1])
- --m_StartRow;
- }
-
- if(int(Row) == m_EndRow)
- {
- while(m_EndRow < m_Height && m_Rows[m_EndRow])
- ++m_EndRow;
- }
- return m_Rows[Row];
-}
-
-void RowBuffer::DiscardRows(int DiscardRow)
-{
- assert(m_BPP > 0);
-
- for(int i = m_StartRow; i < DiscardRow; ++i)
- {
- delete [] m_Rows[i];
- m_Rows[i] = NULL;
- }
-
- m_StartRow = max(m_StartRow, DiscardRow);
- m_EndRow = max(m_EndRow, DiscardRow);
-}
diff --git a/lib/danbooru_image_resizer/RowBuffer.h b/lib/danbooru_image_resizer/RowBuffer.h
index 2c792236f..2c9618302 100644
--- a/lib/danbooru_image_resizer/RowBuffer.h
+++ b/lib/danbooru_image_resizer/RowBuffer.h
@@ -2,30 +2,126 @@
#define ROW_BUFFER_H
#include
+#include
+#include
+#include
+#include "RowBuffer.h"
+#include
+using namespace std;
+template
class RowBuffer
{
public:
- RowBuffer();
- ~RowBuffer();
+ RowBuffer()
+ {
+ m_Rows = NULL;
+ m_ActualRows = NULL;
+ m_StartRow = 0;
+ m_EndRow = 0;
+ m_BPP = 0;
+ m_Height = 0;
+ }
- bool Init(int Width, int Height, int BPP);
+ ~RowBuffer()
+ {
+ for(int i = 0; i < m_Height; ++i)
+ delete [] m_Rows[i];
+
+ delete [] m_ActualRows;
+ }
+
+ /*
+ * If iVertPadding is non-zero, simulate padding on the top and bottom of the image. After
+ * row 0 is written, rows [-1 ... -iVertPadding] will point to the same row. After the bottom
+ * row is written, the following iVertPadding will also point to the last row. These rows
+ * are discarded when the row they refer to is discarded.
+ */
+ bool Init(int iWidth, int iHeight, int iBPP, int iVertPadding = 0)
+ {
+ m_Width = iWidth;
+ m_Height = iHeight;
+ m_BPP = iBPP;
+ m_iVertPadding = iVertPadding;
+
+ m_ActualRows = new T *[iHeight + iVertPadding*2];
+ m_Rows = m_ActualRows + iVertPadding;
+ memset(m_ActualRows, 0, sizeof(T *) * (iHeight + iVertPadding*2));
+
+ return true;
+ }
/* Return row, allocating if necessary. */
- uint8_t *GetRow(int row);
+ T *GetRow(int Row)
+ {
+ assert(m_BPP > 0);
+
+ if(m_Rows[Row] == NULL)
+ {
+ m_Rows[Row] = new T[m_Width*m_BPP];
+ if(Row == 0)
+ {
+ for(int i = -m_iVertPadding; i < 0; ++i)
+ m_Rows[i] = m_Rows[0];
+ }
+ if(Row == m_Height - 1)
+ {
+ for(int i = m_Height; i < m_Height + m_iVertPadding; ++i)
+ m_Rows[i] = m_Rows[m_Height - 1];
+ }
+ if(m_Rows[Row] == NULL)
+ return NULL;
+ if(m_StartRow == m_EndRow)
+ {
+ m_StartRow = Row;
+ m_EndRow = m_StartRow + 1;
+ }
+ }
+
+ if(int(Row) == m_StartRow+1)
+ {
+ while(m_StartRow != 0 && m_Rows[m_StartRow-1])
+ --m_StartRow;
+ }
+
+ if(int(Row) == m_EndRow)
+ {
+ while(m_EndRow < m_Height && m_Rows[m_EndRow])
+ ++m_EndRow;
+ }
+ return m_Rows[Row];
+ }
// Free rows [0,DiscardRow).
- void DiscardRows(int DiscardRow);
+ void DiscardRows(int DiscardRow)
+ {
+ assert(m_BPP > 0);
+ if(DiscardRow > m_Height)
+ DiscardRow = m_Height;
+
+ for(int i = m_StartRow; i < DiscardRow; ++i)
+ {
+ delete [] m_Rows[i];
+ m_Rows[i] = NULL;
+ }
+
+ m_StartRow = max(m_StartRow, DiscardRow);
+ m_EndRow = max(m_EndRow, DiscardRow);
+ }
/* Get a range of rows allocated in m_Rows: [m_StartRow,m_EndRow). If
* more than one allocated range exists, which range is returned is undefined. */
int GetStartRow() const { return m_StartRow; }
int GetEndRow() const { return m_EndRow; }
- const uint8_t *const *GetRows() const { return m_Rows; }
+ const T *const *GetRows() const { return m_Rows; }
private:
/* Array of image rows. These are allocated as needed. */
- uint8_t **m_Rows;
+ T **m_Rows;
+
+ /* The actual pointer m_Rows is contained in. m_Rows may be offset from this to
+ * implement padding. */
+ T **m_ActualRows;
/* in m_Rows is allocated: */
int m_StartRow;
@@ -34,6 +130,7 @@ private:
int m_Width;
int m_Height;
int m_BPP;
+ int m_iVertPadding;
};
#endif
diff --git a/lib/danbooru_image_resizer/danbooru_image_resizer.cpp b/lib/danbooru_image_resizer/danbooru_image_resizer.cpp
index 8c502450a..cca12b6e0 100644
--- a/lib/danbooru_image_resizer/danbooru_image_resizer.cpp
+++ b/lib/danbooru_image_resizer/danbooru_image_resizer.cpp
@@ -1,15 +1,31 @@
#include
#include
#include
-
+#include
+using namespace std;
#include "PNGReader.h"
#include "GIFReader.h"
#include "JPEGReader.h"
#include "Resize.h"
+#include "Histogram.h"
+#include "ConvertToRGB.h"
static VALUE danbooru_module;
-static VALUE danbooru_resize_image(VALUE module, VALUE file_ext_val, VALUE read_path_val, VALUE write_path_val, VALUE output_width_val, VALUE output_height_val, VALUE output_quality_val)
+static Reader *GetReader(const char *file_ext)
+{
+ if (!strcmp(file_ext, "jpg") || !strcmp(file_ext, "jpeg"))
+ return new JPEG;
+ if (!strcmp(file_ext, "gif"))
+ return new GIF;
+ if (!strcmp(file_ext, "png"))
+ return new PNG;
+ return NULL;
+}
+
+static VALUE danbooru_resize_image(VALUE module, VALUE file_ext_val, VALUE read_path_val, VALUE write_path_val,
+ VALUE output_width_val, VALUE output_height_val,
+ VALUE output_quality_val)
{
const char * file_ext = StringValueCStr(file_ext_val);
const char * read_path = StringValueCStr(read_path_val);
@@ -31,44 +47,44 @@ static VALUE danbooru_resize_image(VALUE module, VALUE file_ext_val, VALUE read_
bool ret = false;
char error[1024];
- JPEGCompressor *Compressor = NULL;
- Resizer *resizer = NULL;
- Reader *Reader = NULL;
- if (!strcmp(file_ext, "jpg") || !strcmp(file_ext, "jpeg"))
- Reader = new JPEG;
- else if (!strcmp(file_ext, "gif"))
- Reader = new GIF;
- else if (!strcmp(file_ext, "png"))
- Reader = new PNG;
- else
+ try
{
- strcpy(error, "unknown filetype");
- goto cleanup;
- }
+ auto_ptr pReader(GetReader(file_ext));
+ if(pReader.get() == NULL)
+ {
+ strcpy(error, "unknown filetype");
+ goto cleanup;
+ }
- Compressor = new JPEGCompressor(write_file);
- if(Compressor == NULL)
+ auto_ptr pFilter(NULL);
+
+ {
+ auto_ptr pCompressor(new JPEGCompressor(write_file));
+ pCompressor->SetQuality(output_quality);
+ pFilter.reset(pCompressor.release());
+ }
+
+ {
+ auto_ptr pResizer(new Resizer(pFilter));
+ pResizer->SetDest(output_width, output_height);
+ pFilter.reset(pResizer.release());
+ }
+
+
+ {
+ auto_ptr pConverter(new ConvertToRGB(pFilter));
+ pFilter.reset(pConverter.release());
+ }
+
+ ret = pReader->Read(read_file, pFilter.get(), error);
+ }
+ catch(const std::bad_alloc &e)
{
strcpy(error, "out of memory");
- goto cleanup;
}
- resizer = new Resizer(Compressor);
- if(resizer == NULL || Reader == NULL)
- {
- strcpy(error, "out of memory");
- goto cleanup;
- }
-
- resizer->SetDest(output_width, output_height, output_quality);
- ret = Reader->Read(read_file, resizer, error);
-
cleanup:
- delete Reader;
- delete resizer;
- delete Compressor;
-
fclose(read_file);
fclose(write_file);
@@ -78,7 +94,68 @@ cleanup:
return INT2FIX(0);
}
+static VALUE danbooru_histogram(VALUE module, VALUE file_ext_val, VALUE read_path_val)
+{
+ const char * file_ext = StringValueCStr(file_ext_val);
+ const char * read_path = StringValueCStr(read_path_val);
+
+ FILE *read_file = fopen(read_path, "rb");
+ if(read_file == NULL)
+ rb_raise(rb_eIOError, "can't open %s\n", read_path);
+
+ bool ret = false;
+ char error[1024];
+ VALUE results = Qnil;
+
+ try
+ {
+ auto_ptr pReader(GetReader(file_ext));
+ if(pReader.get() == NULL)
+ {
+ strcpy(error, "unknown filetype");
+ goto cleanup;
+ }
+
+ auto_ptr pFilter(NULL);
+
+ Histogram *pHistogram = new Histogram();
+ pFilter.reset(pHistogram);
+
+ {
+ auto_ptr pConverter(new ConvertToRGB(pFilter));
+ pFilter.reset(pConverter.release());
+ }
+
+ ret = pReader->Read(read_file, pFilter.get(), error);
+
+ results = rb_ary_new();
+ int channels = pHistogram->GetChannels();
+ for(int channel = 0; channel < channels; ++channel)
+ {
+ const unsigned *pChannelData = pHistogram->GetHistogram(channel);
+ VALUE channel_array = rb_ary_new();
+ rb_ary_push(results, channel_array);
+
+ for(int i = 0; i < 256; ++i)
+ rb_ary_push(channel_array, INT2NUM(pChannelData[i]));
+ }
+ }
+ catch(const std::bad_alloc &e)
+ {
+ strcpy(error, "out of memory");
+ }
+
+cleanup:
+ fclose(read_file);
+
+ if(!ret)
+ rb_raise(rb_eException, "%s", error);
+
+ return results;
+}
+
extern "C" void Init_danbooru_image_resizer() {
danbooru_module = rb_define_module("Danbooru");
rb_define_module_function(danbooru_module, "resize_image", (VALUE(*)(...))danbooru_resize_image, 6);
+ rb_define_module_function(danbooru_module, "histogram", (VALUE(*)(...))danbooru_histogram, 2);
}
diff --git a/lib/danbooru_image_resizer/danbooru_image_resizer.rb b/lib/danbooru_image_resizer/danbooru_image_resizer.rb
index 68f718ee2..0f4c8f628 100644
--- a/lib/danbooru_image_resizer/danbooru_image_resizer.rb
+++ b/lib/danbooru_image_resizer/danbooru_image_resizer.rb
@@ -5,23 +5,24 @@ module Danbooru
Danbooru.resize_image(file_ext, read_path, write_path, output_size[:width], output_size[:height], output_quality)
end
- def reduce_to(size, max_size)
- size.dup.tap do |new_size|
- if new_size[:width] > max_size[:width]
- scale = max_size[:width].to_f / new_size[:width].to_f
- new_size[:width] = new_size[:width] * scale
- new_size[:height] = new_size[:height] * scale
- end
-
- if max_size[:height] && (new_size[:height] > max_size[:height])
- scale = max_size[:height].to_f / new_size[:height].to_f
- new_size[:width] = new_size[:width] * scale
- new_size[:height] = new_size[:height] * scale
- end
+ def reduce_to(size, max_size, ratio = 1)
+ ret = size.dup
- new_size[:width] = new_size[:width].to_i
- new_size[:height] = new_size[:height].to_i
+ if ret[:width] > ratio * max_size[:width]
+ scale = max_size[:width].to_f / ret[:width].to_f
+ ret[:width] = ret[:width] * scale
+ ret[:height] = ret[:height] * scale
end
+
+ if max_size[:height] && (ret[:height] > ratio * max_size[:height])
+ scale = max_size[:height].to_f / ret[:height].to_f
+ ret[:width] = ret[:width] * scale
+ ret[:height] = ret[:height] * scale
+ end
+
+ ret[:width] = ret[:width].to_i
+ ret[:height] = ret[:height].to_i
+ ret
end
module_function :resize
diff --git a/lib/danbooru_image_resizer/extconf.rb b/lib/danbooru_image_resizer/extconf.rb
index 5ffbbfea3..a690b9141 100644
--- a/lib/danbooru_image_resizer/extconf.rb
+++ b/lib/danbooru_image_resizer/extconf.rb
@@ -20,7 +20,7 @@ have_func("gdImageJpeg", "gd.h")
have_func("jpeg_set_quality", ["stdlib.h", "stdio.h", "jpeglib-extern.h"])
have_func("png_set_expand_gray_1_2_4_to_8", "png.h")
-with_cflags("-O2 -fno-exceptions -Wall") {true}
+with_cflags("-O2 -Wall") {true}
#with_cflags("-O0 -g -fno-exceptions -Wall") {true}
create_makefile("danbooru_image_resizer")
diff --git a/lib/danbooru_image_resizer/test.rb b/lib/danbooru_image_resizer/test.rb
new file mode 100644
index 000000000..c0d41cf66
--- /dev/null
+++ b/lib/danbooru_image_resizer/test.rb
@@ -0,0 +1,5 @@
+#!/usr/bin/env ruby
+
+require 'danbooru_image_resizer'
+Danbooru.resize_image("jpg", "test.jpg", "test-out.jpg", 2490, 3500, 95)
+
diff --git a/lib/jpgcrush.tar.gz b/lib/jpgcrush.tar.gz
deleted file mode 100644
index 4205b24b3..000000000
Binary files a/lib/jpgcrush.tar.gz and /dev/null differ
diff --git a/public/404.html b/public/404.html
index 9a48320a5..be5c82b42 100644
--- a/public/404.html
+++ b/public/404.html
@@ -2,7 +2,11 @@
The page you were looking for doesn't exist (404)
-
+
+
+ The site is down for maintenance.
+ Check Twitter for updates.
+
+
diff --git a/script/delayed_job b/script/delayed_job
new file mode 100755
index 000000000..edf195985
--- /dev/null
+++ b/script/delayed_job
@@ -0,0 +1,5 @@
+#!/usr/bin/env ruby
+
+require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment'))
+require 'delayed/command'
+Delayed::Command.new(ARGV).daemonize
diff --git a/script/install/danbooru_local_config.rb.templ b/script/install/danbooru_local_config.rb.templ
new file mode 100644
index 000000000..64bc3dced
--- /dev/null
+++ b/script/install/danbooru_local_config.rb.templ
@@ -0,0 +1,5 @@
+module Danbooru
+ class CustomConfiguration < Configuration
+ # Define your custom overloads here
+ end
+end
diff --git a/config/database.yml.template b/script/install/database.yml.templ
similarity index 100%
rename from config/database.yml.template
rename to script/install/database.yml.templ
diff --git a/script/install/init_d.templ b/script/install/init_d.templ
new file mode 100644
index 000000000..ff46f3518
--- /dev/null
+++ b/script/install/init_d.templ
@@ -0,0 +1,399 @@
+#! /bin/sh
+### BEGIN INIT INFO
+# Provides: nginx
+# Required-Start: $remote_fs $syslog
+# Required-Stop: $remote_fs $syslog
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: nginx init.d dash script for Ubuntu <=9.10.
+# Description: nginx init.d dash script for Ubuntu <=9.10.
+### END INIT INFO
+#------------------------------------------------------------------------------
+# nginx - this Debian Almquist shell (dash) script, starts and stops the nginx
+# daemon for ubuntu 9.10 and lesser version numbered releases.
+#
+# description: Nginx is an HTTP(S) server, HTTP(S) reverse \
+# proxy and IMAP/POP3 proxy server. This \
+# script will manage the initiation of the \
+# server and it's process state.
+#
+# processname: nginx
+# config: /opt/nginx/conf/nginx.conf
+# pidfile: /opt/nginx/logs/nginx.pid
+# Provides: nginx
+#
+# Authoer: Andrew Carter
+#
+#
+# Version: 0.1 27-DEC-2010 ascarter@gmail.com
+# Notes: Forked from original script to support default Passenger nginx
+# install via RVM.
+#
+#------------------------------------------------------------------------------
+# MIT X11 License
+#------------------------------------------------------------------------------
+#
+# Copyright (c) 2010 Andrew Carter
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+#------------------------------------------------------------------------------
+#
+# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+# ORIGINAL SCRIPT:
+#
+# Author: Jason Giedymin
+# .
+#
+# Version: 2.0 02-NOV-2009 jason.giedymin AT gmail.com
+# URL: http://code.google.com/p/nginx-init-ubuntu/
+#
+#------------------------------------------------------------------------------
+# MIT X11 License
+#------------------------------------------------------------------------------
+#
+# Copyright (c) 2009 Jason Giedymin, http://Amuxbit.com formerly
+# http://AcronymLabs.com
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+#
+#------------------------------------------------------------------------------
+
+#------------------------------------------------------------------------------
+# Functions
+#------------------------------------------------------------------------------
+. /lib/lsb/init-functions
+
+#------------------------------------------------------------------------------
+# Consts
+#------------------------------------------------------------------------------
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+DAEMON=/opt/nginx/sbin/nginx
+
+PS="nginx"
+PIDNAME="nginx" #lets you do $PS-slave
+PIDFILE=$PIDNAME.pid #pid file
+PIDSPATH=/var/run
+
+DESCRIPTION="Nginx Server..."
+
+RUNAS=root #user to run as
+
+SCRIPT_OK=0 #ala error codes
+SCRIPT_ERROR=1 #ala error codes
+TRUE=1 #boolean
+FALSE=0 #boolean
+
+lockfile=/var/lock/subsys/nginx
+NGINX_CONF_FILE="/opt/nginx/conf/nginx.conf"
+
+#------------------------------------------------------------------------------
+# Simple Tests
+#------------------------------------------------------------------------------
+
+#test if nginx is a file and executable
+test -x $DAEMON || exit 0
+
+# Include nginx defaults if available
+if [ -f /etc/default/nginx ] ; then
+ . /etc/default/nginx
+fi
+
+#set exit condition
+#set -e
+
+#------------------------------------------------------------------------------
+# Functions
+#------------------------------------------------------------------------------
+
+setFilePerms(){
+
+ if [ -f $PIDSPATH/$PIDFILE ]; then
+ chmod 400 $PIDSPATH/$PIDFILE
+ fi
+}
+
+configtest() {
+ $DAEMON -t -c $NGINX_CONF_FILE
+}
+
+getPSCount() {
+ return `pgrep -f $PS | wc -l`
+}
+
+isRunning() {
+ if [ $1 ]; then
+ pidof_daemon $1
+ PID=$?
+
+ if [ $PID -gt 0 ]; then
+ return 1
+ else
+ return 0
+ fi
+ else
+ pidof_daemon
+ PID=$?
+
+ if [ $PID -gt 0 ]; then
+ return 1
+ else
+ return 0
+ fi
+ fi
+}
+
+#courtesy of php-fpm
+wait_for_pid () {
+ try=0
+
+ while test $try -lt 35 ; do
+
+ case "$1" in
+ 'created')
+ if [ -f "$2" ] ; then
+ try=''
+ break
+ fi
+ ;;
+
+ 'removed')
+ if [ ! -f "$2" ] ; then
+ try=''
+ break
+ fi
+ ;;
+ esac
+
+ #echo -n .
+ try=`expr $try + 1`
+ sleep 1
+ done
+}
+
+status(){
+ isRunning
+ isAlive=$?
+
+ if [ "${isAlive}" -eq $TRUE ]; then
+ echo "$PIDNAME found running with processes: `pidof $PS`"
+ else
+ echo "$PIDNAME is NOT running."
+ fi
+
+
+}
+
+removePIDFile(){
+ if [ $1 ]; then
+ if [ -f $1 ]; then
+ rm -f $1
+ fi
+ else
+ #Do default removal
+ if [ -f $PIDSPATH/$PIDFILE ]; then
+ rm -f $PIDSPATH/$PIDFILE
+ fi
+ fi
+}
+
+start() {
+ log_daemon_msg "Starting $DESCRIPTION"
+
+ isRunning
+ isAlive=$?
+
+ if [ "${isAlive}" -eq $TRUE ]; then
+ log_end_msg $SCRIPT_ERROR
+ else
+ start-stop-daemon --start --quiet --chuid $RUNAS --pidfile $PIDSPATH/$PIDFILE --exec $DAEMON \
+ -- -c $NGINX_CONF_FILE
+ setFilePerms
+ log_end_msg $SCRIPT_OK
+ fi
+}
+
+stop() {
+ log_daemon_msg "Stopping $DESCRIPTION"
+
+ isRunning
+ isAlive=$?
+ if [ "${isAlive}" -eq $TRUE ]; then
+ start-stop-daemon --stop --quiet --pidfile $PIDSPATH/$PIDFILE
+
+ wait_for_pid 'removed' $PIDSPATH/$PIDFILE
+
+ if [ -n "$try" ] ; then
+ log_end_msg $SCRIPT_ERROR
+ else
+ removePIDFile
+ log_end_msg $SCRIPT_OK
+ fi
+
+ else
+ log_end_msg $SCRIPT_ERROR
+ fi
+}
+
+reload() {
+ configtest || return $?
+
+ log_daemon_msg "Reloading (via HUP) $DESCRIPTION"
+
+ isRunning
+ if [ $? -eq $TRUE ]; then
+ kill -HUP `cat $PIDSPATH/$PIDFILE`
+
+ log_end_msg $SCRIPT_OK
+ else
+ log_end_msg $SCRIPT_ERROR
+ fi
+}
+
+quietupgrade() {
+ log_daemon_msg "Peforming Quiet Upgrade $DESCRIPTION"
+
+ isRunning
+ isAlive=$?
+ if [ "${isAlive}" -eq $TRUE ]; then
+ kill -USR2 `cat $PIDSPATH/$PIDFILE`
+ kill -WINCH `cat $PIDSPATH/$PIDFILE.oldbin`
+
+ isRunning
+ isAlive=$?
+ if [ "${isAlive}" -eq $TRUE ]; then
+ kill -QUIT `cat $PIDSPATH/$PIDFILE.oldbin`
+ wait_for_pid 'removed' $PIDSPATH/$PIDFILE.oldbin
+ removePIDFile $PIDSPATH/$PIDFILE.oldbin
+
+ log_end_msg $SCRIPT_OK
+ else
+ log_end_msg $SCRIPT_ERROR
+
+ log_daemon_msg "ERROR! Reverting back to original $DESCRIPTION"
+
+ kill -HUP `cat $PIDSPATH/$PIDFILE`
+ kill -TERM `cat $PIDSPATH/$PIDFILE.oldbin`
+ kill -QUIT `cat $PIDSPATH/$PIDFILE.oldbin`
+
+ wait_for_pid 'removed' $PIDSPATH/$PIDFILE.oldbin
+ removePIDFile $PIDSPATH/$PIDFILE.oldbin
+
+ log_end_msg $SCRIPT_ok
+ fi
+ else
+ log_end_msg $SCRIPT_ERROR
+ fi
+}
+
+terminate() {
+ log_daemon_msg "Force terminating (via KILL) $DESCRIPTION"
+
+ PIDS=`pidof $PS` || true
+
+ [ -e $PIDSPATH/$PIDFILE ] && PIDS2=`cat $PIDSPATH/$PIDFILE`
+
+ for i in $PIDS; do
+ if [ "$i" = "$PIDS2" ]; then
+ kill $i
+ wait_for_pid 'removed' $PIDSPATH/$PIDFILE
+ removePIDFile
+ fi
+ done
+
+ log_end_msg $SCRIPT_OK
+}
+
+destroy() {
+ log_daemon_msg "Force terminating and may include self (via KILLALL) $DESCRIPTION"
+ killall $PS -q >> /dev/null 2>&1
+ log_end_msg $SCRIPT_OK
+}
+
+pidof_daemon() {
+ PIDS=`pidof $PS` || true
+
+ [ -e $PIDSPATH/$PIDFILE ] && PIDS2=`cat $PIDSPATH/$PIDFILE`
+
+ for i in $PIDS; do
+ if [ "$i" = "$PIDS2" ]; then
+ return 1
+ fi
+ done
+ return 0
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart|force-reload)
+ stop
+ sleep 1
+ start
+ ;;
+ reload)
+ $1
+ ;;
+ status)
+ status
+ ;;
+ configtest)
+ $1
+ ;;
+ quietupgrade)
+ $1
+ ;;
+ terminate)
+ $1
+ ;;
+ destroy)
+ $1
+ ;;
+ *)
+ FULLPATH=/etc/init.d/$PS
+ echo "Usage: $FULLPATH {start|stop|restart|force-reload|status|configtest|quietupgrade|terminate|destroy}"
+ echo " The 'destroy' command should only be used as a last resort."
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/script/install/nginx.conf b/script/install/nginx.conf
new file mode 100644
index 000000000..91626ec5c
--- /dev/null
+++ b/script/install/nginx.conf
@@ -0,0 +1,30 @@
+pid /var/run/nginx.pid;
+
+events {
+ use epoll;
+ worker_connections 1024;
+}
+
+http {
+ passenger_root /usr/local/rvm/gems/ruby-1.9.2-p290/gems/passenger-3.0.8;
+ passenger_ruby /usr/local/rvm/wrappers/default/ruby;
+ passenger_user_switching on;
+ passenger_default_user danbooru;
+ rails_spawn_method smart;
+ passenger_max_pool_size 3;
+
+ include mime.types;
+ default_type application/octet-stream;
+
+ sendfile on;
+
+ gzip on;
+ gzip_http_version 1.1;
+ gzip_vary off;
+ gzip_comp_level 6;
+ gzip_proxied any;
+ gzip_types text/plain text/html text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
+ gzip_buffers 16 8k;
+
+ include /opt/nginx/conf/sites/*.conf;
+}
diff --git a/script/install/nginx.danbooru.conf b/script/install/nginx.danbooru.conf
new file mode 100644
index 000000000..feffce17b
--- /dev/null
+++ b/script/install/nginx.danbooru.conf
@@ -0,0 +1,35 @@
+server {
+ passenger_enabled on;
+ listen 80;
+ server_name HOSTNAME;
+ root /var/www/danbooru/current/public;
+ index index.html;
+ access_log /var/log/www/danbooru.access.log;
+ error_log /var/log/www/danbooru.error.log;
+ client_max_body_size 30m;
+ location /stylesheets {
+ expires max;
+ break;
+ }
+ location /javascripts {
+ expires max;
+ break;
+ }
+ location /data {
+ valid_referers none HOSTNAME;
+ if ($invalid_referer) {
+ return 403;
+ }
+ expires max;
+ break;
+ }
+ location /maintenance.html {
+ expires 10;
+ }
+ if (-f $document_root/maintenance.html) {
+ rewrite ^(.*)$ /maintenance.html last;
+ break;
+ }
+ error_page 404 /404.html;
+ error_page 500 502 503 504 /500.html;
+}
\ No newline at end of file
diff --git a/test/fixtures/amazon_backups.yml b/test/fixtures/amazon_backups.yml
new file mode 100644
index 000000000..833d34ed4
--- /dev/null
+++ b/test/fixtures/amazon_backups.yml
@@ -0,0 +1,7 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/Fixtures.html
+
+one:
+ last_id: 1
+
+two:
+ last_id: 1
diff --git a/test/functional/uploads_controller_test.rb b/test/functional/uploads_controller_test.rb
index dba826462..5e9197c7d 100644
--- a/test/functional/uploads_controller_test.rb
+++ b/test/functional/uploads_controller_test.rb
@@ -64,7 +64,9 @@ class UploadsControllerTest < ActionController::TestCase
context "create action" do
should "create a new upload" do
assert_difference("Upload.count", 1) do
- post :create, {:upload => {:file => upload_jpeg("#{Rails.root}/test/files/test.jpg"), :tag_string => "aaa", :rating => "q", :source => "aaa"}}, {:user_id => @user.id}
+ file = Rack::Test::UploadedFile.new("#{Rails.root}/test/files/test.jpg", "image/jpeg")
+ file.stubs(:tempfile).returns(file)
+ post :create, {:upload => {:file => file, :tag_string => "aaa", :rating => "q", :source => "aaa"}}, {:user_id => @user.id}
end
end
end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 66e7adb60..e64bc3780 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -18,7 +18,7 @@ module UploadTestMethods
define_method(:original_filename) {filename}
define_method(:content_type) {content_type}
end
-
+
tempfile
end
diff --git a/test/unit/amazon_backup_test.rb b/test/unit/amazon_backup_test.rb
new file mode 100644
index 000000000..cd0312b68
--- /dev/null
+++ b/test/unit/amazon_backup_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class AmazonBackupTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/test/unit/upload_test.rb b/test/unit/upload_test.rb
index 68a3bfe3b..554b9024d 100644
--- a/test/unit/upload_test.rb
+++ b/test/unit/upload_test.rb
@@ -111,11 +111,11 @@ class UploadTest < ActiveSupport::TestCase
@upload.calculate_dimensions(@upload.file_path)
assert_nothing_raised {@upload.generate_resizes(@upload.file_path)}
assert(File.exists?(@upload.resized_file_path_for(Danbooru.config.small_image_width)))
- assert_equal(6556, File.size(@upload.resized_file_path_for(Danbooru.config.small_image_width)))
+ assert_equal(7265, File.size(@upload.resized_file_path_for(Danbooru.config.small_image_width)))
assert(File.exists?(@upload.resized_file_path_for(Danbooru.config.medium_image_width)))
- assert_equal(39411, File.size(@upload.resized_file_path_for(Danbooru.config.medium_image_width)))
+ assert_equal(43474, File.size(@upload.resized_file_path_for(Danbooru.config.medium_image_width)))
assert(File.exists?(@upload.resized_file_path_for(Danbooru.config.large_image_width)))
- assert_equal(179324, File.size(@upload.resized_file_path_for(Danbooru.config.large_image_width)))
+ assert_equal(198583, File.size(@upload.resized_file_path_for(Danbooru.config.large_image_width)))
end
end
diff --git a/tmp/test.jpg b/tmp/test.jpg
deleted file mode 100644
index 253c508d9..000000000
Binary files a/tmp/test.jpg and /dev/null differ