Announcing Kubeplayer (youtube w/o flash)

Announcing Kubeplayer (youtube w/o flash)

Hello Planet KDE, hello RubyCorner

Earlier this year I started writing a KDE application using the Ruby programming language. Coming from the web development, I am no expert at all in C++. So I decided to give KDE development a try using the Ruby bindings for KDE 4 called korundum4.

It wasn’t as easy as expected. Unfortunatly the korundum4 project didn’t got very popular by now, so I found a lot of bugs on the way. Now, three months later, all the bugfixes are included in the last stable KDE. The idea, of just copying the project folder and starting the app without the need of any further setup1, becomes finally true.

Announcing Kubeplayer

Screenshot of Kubeplayer

I started a kind of clone of the well known Qt-based Youtube player Minitube. When starting the development, I had in mind to support Vimeo and maybe some other online video platforms, too. The app should integrate itself better into the KDE workspace, and lately I had the idea of turning it into a mobile app, too. It was possible to get a much cleaner and shorter code, not only because of the nice KDE KIO classes, but also by using Ruby.

Let’s Try It Out In Less Than 1 Minute!

Ok, to be fair, I have to admit, this is only possible using a recent Linux OS2 with korundum4 and Ruby 1.9.x preinstalled.

To give Kubeplayer a try, you just have to checkout the repository, and start the application.

git clone git://git.kde.org/kubeplayer
ruby kubeplayer/kubeplayer

That’s it. You can also install kubeplayer using CMake. Installation works like you would it expect it from a CMake based C++ application.

If you get an error related to json, you are probably using an Ruby version prior to 1.9.x. In this case you have to install rubygems via you distribution package manager. Then you can use Ruby’s own package manager to install the missing dependency.

gem install json

Get Involved!

Not everything is done by now. Youtube works in the most a lot of cases. The GUI needs a lot of small improvements. I even thought about a maemo/meego port, paid for an N900 on eBay, but unfortunately got duped :(. So the mobile development has to wait.

After all, the project is still quite young, the code is very short, nice and clear. If you ever felt like trying to develop for/with KDE, the barrier is now quite low.

  • You don’t have to deal with a KDE development environment. Just clone the repo and you are done. Since yesterday, you can find the project in playground.
    git clone git://git.kde.org/kubeplayer
  • You don’t need to know C++.
  • You don’t have to compile anything at all!
  • You can use the shiny new git infrastructure of KDE.

Take a look at the code base. There are not many files at all. Summed up, there are less then 1000 lines of code (including rare comments). For an already functional multimedia application, this is really few.

kubeplayer
├── CMakeLists.txt
├── kubeplayer
├── lib
│   ├── CMakeLists.txt
│   ├── kubeplayer.desktop
│   ├── kubeplayer.rb
│   ├── List.rb
│   ├── MainWindow.rb
│   ├── provider
│   │   └── youtube
│   │       └── Youtube.rb
│   └── Video.rb
├── main.rb
└── README.rdoc

How does the future of Kubeplayer look like? I have already a lof of ideas, what could be done. Maybe you have also many, but different ones. What about:

  • allowing the use to login and comment on all supported video platforms
  • make flash needless on netbooks by providing an optimized app
  • create a scripted KPart to fetch youtube pages and open the video using kubeplayer
  • let the user download the videos (in a video library)
  • make it possible to share supported video platforms with the plasma media center team

So what do you think, can Kubeplayer do for you? What can you do for Kubeplayer?

Kind regards,
Robert

  1. This isn’t 100% true. You need to have korundum4 installed, but most distros seems to ship this package by default. On opensuse it is called “ruby-kde4”. ↩︎

  2. As KDE and Ruby is also available for many other OS, Kubeplayer is actually cross platform. ↩︎

Making Of ruby-stocks Plasma DataEngine

Making Of ruby-stocks Plasma DataEngine

Hello Planet KDE, hello RubyCorner

Since I wrote my last Blog post much time has past. After my last exam last Friday I just started to do some hacking on KDE. As it should be something simple for the beginning and something useful, too, I decided to provide a new plasma dataengine for retrieving stocks information.

I used ruby, because plasma hacking with ruby is freaking awesome and totally easy. Let’s prove it!

How To Use It

Simple Plasmoid made with Ruby Before diving into the development process, you probably want to test the final result yourself. First you have to download the plasma package. Then you can install it easily with the plasma package manager.

plasmapkg -t dataengine -i plasma-dataengine-ruby-stocks-v1.0.zip

You won’t need to be root for that. You should get a success response afterwards.

As there is by now no plasmoid to display the provided data, you have to use the plasmaengineexplorer to test the engine. Call this program on your shell and look for ruby-stocks. You have to type in a stocks symbol like GOOG (Google) or NOK (Nokia) to get the information. Is is also possible to just call:

plasmaengineexplorer --engine ruby-stocks --source NOK

You can remove the engine with this short command:

plasmapkg -t dataengine -r ruby-stocks

How To Create It

I started with a look on the plasma ruby examples and copied the inital construct from the given time data engine example. There was also an attempt to create a stocks engine with C++. The code is available in the playground.

The most time took the clean implementation of the cvs data parsing.

If you want to start yourself with a ruby based dataengine, I recommend to start with the time engine example, too. Do not change the given file structure, which should be:

plasma-dataengine-ruby-stocks
├── contents
│   └── code
│       ├── main.rb
│       └── …
└── metadata.desktop

The top directory gets packaged into a zip for distribution.

Let’s take a look on the main.rb. There a some important things you have to be aware of, when you want to use ruby for plasma dataengines:

  • when you name your engine “ruby-stocks” (see metadata.desktop), you have to name the top module RubyStocks. Otherwise your engine will fail.
  • Plasma looks for a class called Main in that module, which have to inherit from PlasmaScripting::DataEngine
  • make sure, that you implement the member methods: sourceRequestEvent and updateSourceEvent

As both methods should do the same in my example, I used an method alias. If you ever did a plasma dataengine using C++ you have noticed, that it is a straight forward port from C++ to ruby. There is hardly something different.

# kate: remove-trailing-space on; replace-trailing-space-save on; indent-width 2; indent-mode ruby; syntax ruby; replace-tabs on; replace-tabs-save on; space-indent on;
require 'plasma_applet'

# the dictonary replaces the ruby hash as we need an ordered hash. only ruby 1.9 uses ordered one.
# when ruby1.9 is the default version, we can switch back to native ruby hash
require 'dictionary'

module RubyStocks
  class Main < PlasmaScripting::DataEngine

    # the url needs to be extended by the stock ID at the end of the string
    # format code: http://brusdeylins.info/projects/yahoo-finance-api/
    DATA = Dictionary[
      "j1" => ["market capitalization", Float],
      "p2" => ["percent change", String],
      "s0" => ["symbol",String],
      "d1" => ["last trade date", Qt::Date],
      "t1" => ["last trade time", Qt::Time],
      "c1" => ["change", Float],
      "o0" => ["open", Float],
      "h0" => ["days high", Float],
      "g0" => ["days low", Float],
      "v0" => ["volume", Float],
      "a2" => ["average daily volume", Float],
      "l1" => ["last trade", Float],
      "c4" => ["currency", String]
    ]

    SOURCE_URL = "http://download.finance.yahoo.com/d/quotes.csv?f=#{DATA.keys.join}&e=.csv&s="

    def initialize parent, args = nil
      super parent

      # don't update faster than once a minute
      setMinimumPollingInterval 60000
      # dafault update rate is 10 minutes
      setPollingInterval 600000

    end

    def updateSourceEvent source
      request_url = SOURCE_URL + Qt::Url::toPercentEncoding(source).data.strip
      job = KIO::storedGet KDE::Url.new(request_url), KIO::NoReload, KIO::HideProgressInfo
      job.connect( SIGNAL( 'result( KJob* )' ) ) do |aJob|
        parseCSVLine source, aJob.data
      end

      return false
    end

    def parseCSVLine source, dataByteArray
      $stderr.puts "ruby-stocks plasma dataengine: retrieved data: " + dataByteArray.data
      dataArray = dataByteArray.data.strip.split ","
      if dataArray.size >= DATA.size
        DATA.values.each do |aValue|
          data =  dataArray.shift
          unless data =~ %r{N/A}
            if aValue[1] == Float
                data.gsub! /B$/, "E6"
                data.gsub! /M$/, "E3"
                setData source, aValue[0], data.to_f unless data == "N/A"
            elsif aValue[1] == String
              data = data[1..-2]
              setData source, aValue[0], data unless data.empty?
            elsif aValue[1] == Qt::Time
              setData source, aValue[0], Qt::Time.fromString(data, '"h:mmap"')
            elsif aValue[1] == Qt::Date
              setData source, aValue[0], Qt::Date.fromString(data, '"M/d/yyyy"')
            end
          end
        end
      end
    end

    alias sourceRequestEvent updateSourceEvent

  end

end

main.rb

The second file you need is the metadata.desktop.

[Desktop Entry]
Name=Stocks Data Engine
Comment=Stocks Data Engine powered by Yahoo! Finance (real time delayed by around 15 minutes

Type=Service
ServiceTypes=Plasma/DataEngine
X-Plasma-API=ruby-script
X-Plasma-MainScript=code/main.rb

X-KDE-PluginInfo-Author=Robert Riemann
X-KDE-PluginInfo-Email=saloution@googlemail.com
X-KDE-PluginInfo-Name=ruby-stocks
X-KDE-PluginInfo-Version=1.0
X-KDE-PluginInfo-Website=http://plasma.kde.org/
X-KDE-PluginInfo-Category=Online Services
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-EnabledByDefault=true

# Icon=alarmclock

metadata.desktop

What Comes Next?

The engine should be quite usable by now. I plan to create a plasmoid which lets you search for a company name to get the stocks symbol and then shows some important numbers (like the weather widget, but without these nice graphics).

After that I want to create a second plasmoid which just shows a chart from Yahoo! Finance. The most work will probably be to create a settings dialog similar to the one you find here below the chart.

What do you think? Would you use a stocks plasmoid? What do you expect it to display? Why didn’t you have already used ruby to create a dataengine? :wink: And after all, how do I get this engine into KDE trunk?

Does owncloud really requires a server-side backend?

Hi planetkde, hi readers from planet gnome (so called aliens1),

I’ve just stumbled upon a blog post dealing with a potential collaboration of KDE and gnome to initiate a replacement for dropbox, called ownCloud.

There are already thoughts about possible GSoC projects giving attention to the gnome/KDE frontend clients and to the server backend.

Nice idea, but in my humble opinion there is a problem, at least for me: I haven’t a hosted root server, but a lot of unix accounts with user webspace, where it is impossible to run any kind of background services/daemons.

Think of:

  • university accounts
  • (scientific) institute accounts
  • accounts from your IT related work

Think of people, who probably uses linux.

In the case that ownCloud is meant to be the center of all data synchronisations, it would be sad that those people who only have webspace cannot take advantage of it.

So I encourage the developer to design the ownCloud in a way that makes the server-side backend software only optional, but not a requirement.

This would be similar to git, which can use a server side backend (git://), but is also satisfied with a simple sftp connection (ssh://). (Please correct me if I am wrong.)

After all you might want to read an article about the disadvantages of SaaS published by http://www.gnu.org. Exaggerated message in one sentence: Don’t get dependant of other people server services. :wink:

  1. Just a joke. Don’t take it serious. ↩︎

The next Klipper action

The next Klipper action

In my last blog entry I explained how to send the clipboard content via KDE Klipper to a pastebin service.

Now I wrote a quick-and-dirty script to transform a URI in the clipboard to a shortenend version using http://ur1.ca (U R One; it is GPL).

I never figured out how to get automatic URL shortening with http://identi.ca and Choqok. That’s a kind of universal work-around for me.

Copy the file ur1.rb to a folder which is in your $PATH and add a Klipper action as I explained in the last post. To setup your Klipper action you will need the following:

RegExp for matching URLs
(^(http|https|ftp):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$)
Command for Klipper action
ur1.rb %s

You can trigger actions manually by <Ctrl>+<Alt>+<R> (KDE default).

ur1.rb

Copy this file to your ~/bin and make it executable.

Usage
ur1.rb URL
Example
ur1.rb http://linux.com

The script will return the url in case of no errors and otherwise a short string indicating the error.

#!/usr/bin/env ruby
# kate: remove-trailing-space on; replace-trailing-space-save on; indent-width 2; indent-mode ruby; syntax ruby;
# ur1.rb

require 'net/http'
require 'cgi'
require 'uri'

URL = URI.parse "http://ur1.ca/"
raise if ARGV.size == 0
longurl = ARGV[0]

http = Net::HTTP.new URL.host, URL.port

query_string = "longurl=#{CGI.escape longurl}"

response, body = http.start do |http|
  http.post URL.path, query_string
end

abort "error (wrong response code)" unless response.code == '200'

puts (body[/<p class="success">Your ur1 is: <a href="(.+)"/,1] or "error (no url returned)")

ur1.rb

Copy your clipboard to the pastebin

Copy your clipboard to the pastebin

Klipper Pastie.org Menu When you are a power IRC user, you might know the problem. You cannot copy the whole source code, error message or log file etc. directly in the IRC channel. You need a pastebin. I like http://pastie.org really much. It has a clean interface and supports highlighting for many languages. But how to copy the text to the pastebin in a handy and short way?

Do the following to copy the clipboard content to the pastebin by a simple <Ctrl>+<Alt>+<R> (global shortcut to open Klipper actions) and a click:

  • Copy the file pastie.rb to a folder which is in your $PATH
  • Make the file executable for you
  • Edit the actions of Klipper and add for the .* Regexp (means: no special string) a new action (do not activate automatic).
  • Add the command echo '%s' | pastie.rb, feedback should go to clipboard and set the description to “post as plain text” for instance
  • You can add another command echo '%s' | pastie.rb -f ruby to paste the text with ruby syntax highlighting

Klipper Pastie.org Settings After that you should be able to send your clipboard to the pastebin with one selection (to copy text into clipboard), one hotkey (to trigger Klipper actions) and one click (to choose between different highlighters). You can paste the URL with a single click on the middle button of your mouse. You don’t even have to open the pastebin page yourself!

I like it. Just want to share this with you in the case you was locking for something similiar. :)

pastie.rb

Copy this file to your ~/bin and make it executable.

A big thank to the unknown author of this file. I found it via google on http://pastie.org and did only some minor modifications on it.

You can use the pastie.rb script via command line by pipe a file to it. To set the code highlighting use the switch -f LANG. To get all supported languages you want to try a pastie.rb -h.

#!/usr/bin/env ruby
# kate: remove-trailing-space on; replace-trailing-space-save on; indent-width 2; indent-mode ruby; syntax ruby;
# file: pastie.rb

require 'net/http'
require 'optparse'
require 'timeout'
require 'cgi'
require 'uri'

class Hash

  def to_query_string
    map { |k, v|
      if v.instance_of?(Hash)
        v.map { |sk, sv|
          "#{k}[#{sk}]=#{sv}"
        }.join('&')
      else
        "#{k}=#{v}"
      end
    }.join('&')
  end

end

module Pastie

  AVAILABLE_PARSERS = %w( objective-c++ actionscript ruby ruby_on_rails diff
    plain_text c++ css java javascript html html_rails shell shell-unix-generic
    sql php python pascal perl yaml csharp go apache lua io lisp d erlang fortran
    haskell literate_haskell makefile scala scheme smarty ini nu tex clojure
  )

  class API

    PASTIE_URL = URI.parse "http://pastie.org/pastes"

    def paste(body, format = 'plain_text', is_private = false)
      raise InvalidParser unless valid_parser? format

      http = Net::HTTP.new PASTIE_URL.host, PASTIE_URL.port

      query_string = { :paste => {
        :body => CGI.escape(body),
        :parser => format,
        :restricted => is_private,
        :authorization => 'burger'
      }}.to_query_string

      response, body = http.start do |http|
        http.post PASTIE_URL.path, query_string
      end

      raise Pastie::Error unless response.code == '302'

      response['location']
    end

    private

    def valid_parser?(format)
      Pastie::AVAILABLE_PARSERS.include? format
    end

  end

  class Error < StandardError; end
  class InvalidParser < StandardError; end

  class ConsoleOptions

    attr_reader :parser, :options

    def initialize
      @options = {
        :format => 'plain_text',
        :private => false
      }

      @parser = OptionParser.new do |cmd|
        cmd.banner = "Ruby Pastie CLI - takes paste input from STDIN"

        cmd.separator ''

        cmd.on('-h', '--help', 'Displays this help message') do
          puts @parser
          exit
        end

        cmd.on('-f', '--format FORMAT', "The format of the text being pasted. Available parsers: #{Pastie::AVAILABLE_PARSERS.join(', ')}") do |format|
          @options[:format] = format
        end

        cmd.on('-p', '--private', 'Create a private paste') do
          @options[:private] = true
        end
      end
    end

    def run arguments
      @parser.parse!(arguments)

      body = ''

      Timeout.timeout(1) do
        body += STDIN.read
      end

      if body.strip.empty?
        puts "Please pipe in some content to paste on STDIN."
        exit 1
      end

      pastie = API.new
      puts pastie.paste(body, @options[:format], @options[:private])

      exit 0
    rescue InvalidParser
      puts "Please specify a valid format parser."
      exit 1
    rescue Error
      puts "An unknown error occurred"
      exit 1
    rescue Timeout::Error
      puts "Could not read from STDIN."
      exit 1
    end
  end
end

if ($0 == __FILE__)
  app = Pastie::ConsoleOptions.new
  app.run(ARGV)
end

pastie.rb

Pagination