Archive

Archive for June, 2009

Rails auto_complete nested list

June 9th, 2009 1 comment

Yesterday I was implementing auto completion for categories. The idea was to get possibility to write by hand categories but keep them as has_many_through association in database.

After six hours of coding I got this working – that is why I’m against rails, with such big amount of plugins, gems and blogs you have to spent a lot of time to do small things.

This code works with rails 2.3

So first we have to install auto_complete plugin which was quite handy:

script/plugin install auto_complete
script/plugin discover

Second we need to define migration:

  create_table :book_categories do |t|
    t.references :books
    t.references :categories
  end
  create_table :books do |t|
    t.string :title
  end
  create_table :categories do |t|
    t.string :name
  end

At third step we define models:

class Book < ActiveRecord::Base
  has_many :book_categories, :dependent => :destroy
  has_many :categories, :through => :book_categories
end
class BookCategory < ActiveRecord::Base
  belongs_to :book
  belongs_to :category
end
class Category < ActiveRecord::Base
  has_many :book_categories, :dependent => :destroy
  has_many :books, :through => :book_categories
end

Time for forth step – faking Book properties:

class Book < ActiveRecord::Base
has_many :book_categories, :dependent => :destroy
has_many :categories, :through => :book_categories
def categories_list
self.categories.map{|c| c.name}*”, ”
end

def categories_list=(list)
list_names = list.split(‘,’).map{ |e| e.strip }.uniq
list_existings = Category.find(:all, :conditions => [ ‘name IN (?)’, list_names ], :select => ‘id,name’ )
list_existings_names = list_existings.map { |e| e.name }
list_add = list_names-list_existings_names
list_new = list_existings.map { |e| e.id }
list_old = self.categories.map { |e| e.id }.uniq
list_add.each do |name|
self.categories << Category.new(:name => name)
end
self.categories << Category.find(:all, :conditions => [ ‘id IN (?)’, list_new-list_old ], :select => ‘id’ )
self.categories.delete Category.find(:all, :conditions => [ ‘id IN (?)’, list_old-list_new ], :select => ‘id’ )
end
end

Now we can have some rest from coding and play a bit with new models using “script/console”:

>> Category.new(:name => 'a').save
>> Category.new(:name => 'b').save
>> Category.new(:name => 'c').save
>> Category.all.map{|c| [c.id,c.name] }
=> [[1, "a"], [2. "b"], [3, "c"]]
>> b = Book.new(:title=>'some book')
>> b.categories_list='a,b,c'
>> b.categories
=> [#<Category id: 1>, #<Category id: 2>, #<Category id: 3>]
>> b.categories_list='a,c,d'
>> b.categories
=> [#<Category id: 1>, #<Category id: 3>, #<Category id: nil, name: "d">]
>> b.save
>> Category.all.map{|c| [c.id,c.name] }
=> [[1, "a"], [2. "b"], [3, "c"], [4, "d"]]

Fifth step is to write edit/new view:

<% form_for(@book) do |f| %>
  <div>
    <%= f.label :title, t(:book_label_title) %><br />
    <%= f.text_field :title %>
  </div>
  <div>
    <%= f.label :categories_list, t(:book_label_category) %><br />
    <%= text_field_with_auto_complete :book, :categories_list, {}, { :method => :get } %>
  </div>
<% end %>

Six step is to define action in controller:

class BooksController < ApplicationController
  def auto_complete_for_book_categories_list
    list = params['book']['categories_list'].split(',').map{ |e| e.strip }

    name = list.pop

    find_options = {
      :conditions => [ "name LIKE ?", '%' + name + '%' ],
      :order => "name ASC",
      :limit => 10,
      :select => 'id, name',
    }

    @items = Category.find(:all, find_options).select { |e| !list.include?(e.name) }.
      map { |e| list.push(e.name); e.name=(list*', '); list.pop(); e }

    render :inline => "<%= auto_complete_result @items, 'name' %>"
  end
  #other actions go here ...
end

The seventh and last step is to add routing:

map.resources :books, :collection => { :auto_complete_for_book_categories_list => :get }

Hope that this will help someone.

Categories: Development Tags: , ,

Converting movies for sony ericsson with ffmpeg

June 3rd, 2009 Comments off

I know there is already a lot of similar blogs and tutorials available on the net about converting movies for mobile devices, but as already some peoeple have discovered writing a blog is to keep log of our experiences and have easy way back to our experiences.

So I got new sony ericsson cell phone “w890i” the first criteria to chose it was its thin casing, but I got in the suite playback of movies.

First step was to find out format of the movie handled by the phone, I have grabbed one of example movies to my disk and used ffmpeg to see its format:

ffmpeg -i example.mp4 /dev/shm

And the most important lines of the output are:

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'example.mp4':
...
Stream #0.0(und): Video: h263 ...
Stream #0.1(und): Audio: aac...

So in the line “Input” we see list of container formats – extensions we can use for our file, in next lines we see Stream for video and its codec “h263” and audio codec “aac”, we will use this values for converting movies for the phone.

My first problem was to merge movie from few parts, I have tried to use avidemux, but without sucess, next one was mencoder, after few tries I have found good combination for my movies:

mencoder -ofps 24 -ovc copy -oac mp3lame input*.wmv -o movie.avi

I have used “-ofps” switch to eliminate duplikate frames, “-ovc copy” is to copy the video stream, for the audio codec “-oac” I was forced to use mp3lame, it was not working with standard “copy”. There is a lot of combination, but mostly important are those one that I have used, consult manual page of mencoder to get them all.

After getting all parts into one file I could finally use ffmpeg to convert the movie for the phone:

ffmpeg -i movie.avi -s qcif -vcodec h263 -acodec libfaac movie.3gp

Ffmpeg will autodetect input format and use given params to build output, like you can see above I have added lib to aac, this should work for other codecs if the short name is not working.

Finally after few minutes of conversion I got 80 minutes movie that I could watch on my phone, most funny was that I was watching movie in low res while still working on my laptop – I could use laptop to play it with a lot better resolution … but how else I could check that conversions went fine.

The last hint is to load phone batery watchin 80 minutes of movie can eat 30 upto 50% of it.

Categories: Linux Tags: , ,