amahi/platform

View on GitHub
app/controllers/search_controller.rb

Summary

Maintainability
C
7 hrs
Test Coverage
# encoding: UTF-8
# Amahi Home Server
# Copyright (C) 2007-2013 Amahi
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License v3
# (29 June 2007), as published in the COPYING file.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# file COPYING for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Amahi
# team at http://www.amahi.org/ under "Contact Us."

require 'digest/sha1'

class SearchController < ApplicationController

    before_action :login_required
    layout 'basic'

    RESULTS_PER_PAGE = 20

    SEARCH_CACHE = File.join(Rails.root, 'tmp/cache/search')

    EXT_AUDIO = ['aac', 'aif', 'iff', 'm3u', 'm4a', 'mid', 'midi', 'mp3', 'mpa', 'ra', 'ram', 'wav', 'wma']
    EXT_IMAGES = ['mng', 'pct', 'bmp', 'gif', 'jpeg', 'jpg', 'png', 'psd', 'psp', 'thm', 'tif', 'ai', 'drw', 'dxf', 'eps', 'ps', 'svg', '3dm', '3dmf']
    EXT_VIDEO = ['3g2', '3gp', 'asf', 'asx', 'avi', 'flv', 'mkv', 'mov', 'mp4', 'mpg', 'mpeg', 'qt', 'rm', 'swf', 'vob', 'wmv']

    def hda
        @page_title = 'Search Results'
        @search_value = 'HDA'

        if params[:button] && params[:button] == "Web"
            require 'uri'
            redirect_to URI.escape("http://www.google.com/search?q=#{params[:query]}")
        else
            @query = params[:query]
            @page = (params[:page] && params[:page].to_i.abs) || 1
            @rpp = (params[:per_page] && params[:per_page].to_i.abs) || RESULTS_PER_PAGE
            unless use_sample_data?
                @results = hda_search(@query, nil, @page, @rpp)
            else
                # NOTE: this is some sample fake data for development
                @results = SampleData.load('search')
            end
        end
    end

    def images
        @query = params[:query]
        @page = (params[:page] && params[:page].to_i.abs) || 1
        @rpp = (params[:per_page] && params[:per_page].to_i.abs) || RESULTS_PER_PAGE
        @results = hda_search(@query, EXT_IMAGES, @page, @rpp)
        render 'hda'
    end

    def audio
        @query = params[:query]
        @page = (params[:page] && params[:page].to_i.abs) || 1
        @rpp = (params[:per_page] && params[:per_page].to_i.abs) || RESULTS_PER_PAGE
        @results = hda_search(@query, EXT_AUDIO, @page, @rpp)
        render 'hda'
    end

    def video
        @query = params[:query]
        @page = (params[:page] && params[:page].to_i.abs) || 1
        @rpp = (params[:per_page] && params[:per_page].to_i.abs) || RESULTS_PER_PAGE
        @results = hda_search(@query, EXT_VIDEO, @page, @rpp)
        render 'hda'
    end

    def web
    end

protected

    def hda_search(term, filter, page, rpp = RESULTS_PER_PAGE)
        return [] unless term && !term.blank?
        locate_search(term, filter && filter.join('|'), page, rpp)
    end

    # FIXME: implement pagination
    def locate_search(term, filter, page, rpp)
        base = Share.basenames
        # lines to skip
        skip = (page - 1) * rpp
        open locate_cache(term) do |l|
            skipped = 0
            res = []
            while (file = l.gets) && (res.size < rpp)
                file.strip!
                path = pathname(file, base)
                next unless path
                r = locate2result(file, path)
                next unless r
                next if filter && r[:title] !~ /\.(#{filter})$/
                if skipped < skip
                    skipped += 1
                    next
                end
                res << r
            end
            res
        end
    end

    def locate_cache(term)
        FileUtils.mkdir_p(SEARCH_CACHE)
        sha1 = Digest::SHA1.hexdigest(term)
        cache = File.join(SEARCH_CACHE, sha1)
        # expire old entries in the cache to prevent accumulation (12 hours, index is 24 hours old)
        Dir.glob(File.join(SEARCH_CACHE, '*')) do |f|
            FileUtils.rm_f(f) if Time.now - File.mtime(f) > 12.hours
        end
        # is the search already in the cache? if so, return it, if not make it
        if File.exists?(cache)
            cache
        else
            # ignore case unless the search is done with some capitalization
            case_sensitive = (term =~ /[A-Z]/) ? "" : "-i"
            system "locate #{case_sensitive} -e '#{term}' > #{cache}"
            cache
        end
    end

    def locate2result(file, path)
        begin
            stat = File::Stat.new file
        rescue
            return nil
        end
        unless ["directory", "file"].include? stat.ftype
            raise "can't handle file type #{t} for #{file}"
        end

        {    :title => File.basename(file),
            :path => File.join(path),
            :size => stat.size,
            :owner => begin Etc.getpwuid(stat.uid).name; rescue ; stat.uid.to_s; end,
            :type => File.ftype(file)
        }
    end

    def pathname(file, basenames)
        basenames.each { |b,name| return [name, $1] if file =~ /^#{b}(.*)/ }
        nil
    end
end