ipodcastly 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Denis Barushev
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # Ipodcastly
2
+
3
+ Ipodcastly is a console client for syncing local iTunes library with
4
+ [Podcastly](http://podcastly.com). The gem was built as a simple example to
5
+ demonstrate how [Podcastly](http://podcastly.com)'s API works.
6
+
7
+ ## Installation
8
+
9
+ The installation is pretty standard:
10
+
11
+ sudo gem install ipodcastly
12
+
13
+ ## Usage
14
+
15
+ Just run it in the Terminal:
16
+
17
+ ipodcastly
18
+
19
+ ## Copyright
20
+
21
+ Copyright (c) 2010 Denis Barushev. See LICENSE for details.
data/bin/ipodcastly ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
3
+
4
+ require 'ipodcastly'
5
+ require 'choice'
6
+
7
+ Choice.options do
8
+ banner "Usage: #{File.basename(__FILE__)}"
9
+ separator ''
10
+ separator 'Common options: '
11
+
12
+ option :help do
13
+ long '--help'
14
+ desc 'Show this message'
15
+ end
16
+
17
+ option :version do
18
+ short '-v'
19
+ long '--version'
20
+ desc 'Show version'
21
+ action do
22
+ puts "#{File.basename(__FILE__)} #{File.exist?('VERSION') ? File.read('VERSION') : ''}"
23
+ exit
24
+ end
25
+ end
26
+ end
27
+
28
+ options = Choice.choices
29
+ Ipodcastly::Sync.start(options)
data/lib/ipodcastly.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+
3
+ require 'appscript'
4
+ require 'httparty'
5
+
6
+ require 'ipodcastly/sync'
7
+
8
+ require 'ipodcastly/client/podcast'
9
+ require 'ipodcastly/client/episode'
10
+
11
+ require 'ipodcastly/server/podcast'
12
+ require 'ipodcastly/server/episode'
@@ -0,0 +1,52 @@
1
+ module Ipodcastly
2
+ module Client
3
+ class Episode
4
+ attr_reader :reference
5
+
6
+ def initialize(reference)
7
+ @reference = reference
8
+ end
9
+
10
+ def title
11
+ @title ||= @reference.name.get
12
+ end
13
+
14
+ def duration
15
+ @duration ||= @reference.duration.get
16
+ end
17
+
18
+ def position
19
+ @position ||= @reference.played_count.get > 0 ? duration : @reference.bookmark.get
20
+ end
21
+
22
+ def position=(time)
23
+ @position = time
24
+ @reference.bookmark.set(time)
25
+ end
26
+
27
+ def mark_as_listened
28
+ @reference.played_count.set(1)
29
+ end
30
+
31
+ def enabled?
32
+ @reference.enabled.get
33
+ end
34
+
35
+ def audio?
36
+ @reference.video_kind.get == :none
37
+ end
38
+
39
+ def never_played?
40
+ position.zero? && @reference.played_count.get.zero?
41
+ end
42
+
43
+ def updateable?
44
+ enabled? && never_played?
45
+ end
46
+
47
+ def listened?
48
+ enabled? && !never_played?
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,30 @@
1
+ module Ipodcastly
2
+ module Client
3
+ class Podcast
4
+ attr_accessor :title
5
+
6
+ @@itunes = Appscript.app('iTunes')
7
+ @@podcasts = @@itunes.playlists.get.detect { |p| p.special_kind.get == :Podcasts }
8
+
9
+ def initialize(title)
10
+ @title = title
11
+ end
12
+
13
+ def episodes
14
+ @@podcasts.tracks.get.select { |t| t.album.get == title }.map { |t| Episode.new(t) }
15
+ end
16
+
17
+ def self.all
18
+ @@podcasts.tracks.get.map { |t| t.album.get }.uniq.map { |title| new(title) }
19
+ end
20
+
21
+ def self.find_by_title(title)
22
+ all.detect { |p| p.title == title }
23
+ end
24
+
25
+ def self.subscribe(url)
26
+ @@itunes.subscribe(url)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,35 @@
1
+ module Ipodcastly
2
+ module Server
3
+ class Episode
4
+ include HTTParty
5
+ base_uri 'http://podcastly.com/api/v1'
6
+ default_params :api_key => Ipodcastly::Sync.api_key
7
+
8
+ attr_reader :id, :podcast_id, :title, :position, :duration
9
+
10
+ def initialize(id, podcast_id, title, position, duration = 0)
11
+ @id = id
12
+ @podcast_id = podcast_id
13
+ @title = title
14
+ @position = position
15
+ @duration = duration
16
+ end
17
+
18
+ def to_param
19
+ {
20
+ :podcast_id => podcast_id,
21
+ :episode => {
22
+ :title => title,
23
+ :position => position.to_i,
24
+ :duration => duration.to_i
25
+ }
26
+ }
27
+ end
28
+
29
+ def save
30
+ response = self.class.post("/episodes.json", :body => to_param)
31
+ response.code == 200
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,41 @@
1
+ module Ipodcastly
2
+ module Server
3
+ class Podcast
4
+ include HTTParty
5
+ base_uri 'http://podcastly.com/api/v1'
6
+ default_params :api_key => Ipodcastly::Sync.api_key
7
+
8
+ attr_reader :id, :title, :url
9
+
10
+ def initialize(id, title, url)
11
+ @id = id
12
+ @title = title
13
+ @url = url
14
+ end
15
+
16
+ def episodes
17
+ response = self.class.get("/episodes.json?podcast_id=#{id}")
18
+
19
+ if response.code == 200
20
+ response.map { |e| Episode.new(e['id'], id, e['title'], e['position'], e['duration']) }
21
+ else
22
+ []
23
+ end
24
+ end
25
+
26
+ def self.all
27
+ response = get('/podcasts.json')
28
+
29
+ if response.code == 200
30
+ response.map { |p| new(p['id'], p['title'], p['url']) }
31
+ else
32
+ []
33
+ end
34
+ end
35
+
36
+ def self.find_by_title(title)
37
+ all.detect { |p| p.title == title }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,76 @@
1
+ module Ipodcastly
2
+ class Sync
3
+ def self.api_key
4
+ begin
5
+ @api_key ||= YAML::load(File.open(File.join(ENV['HOME'], '.ipodcastly')))['api_key']
6
+ rescue
7
+ puts "API key is missing."
8
+ exit
9
+ end
10
+ end
11
+
12
+ # User listened an episode in Podcastly, he didn't listen it in the
13
+ # iTunes, the episode already downloaded in the iTunes.
14
+ def self.sync_itunes
15
+ Server::Podcast.all.each do |server_podcast|
16
+ until client_podcast = Client::Podcast.find_by_title(server_podcast.title)
17
+ Client::Podcast.subscribe(server_podcast.url)
18
+ puts "Subscribing to #{server_podcast.url}"
19
+ end
20
+
21
+ puts "\n+ Podcast \"#{server_podcast.title}\""
22
+
23
+ # Only episodes the user listened
24
+ server_podcast.episodes.each do |server_episode|
25
+ next unless client_episode = client_podcast.episodes.detect { |client_episode| client_episode.title == server_episode.title }
26
+
27
+ if client_episode.updateable?
28
+ if !server_episode.duration.zero? && (server_episode.position == server_episode.duration)
29
+ client_episode.mark_as_listened
30
+ else
31
+ client_episode.position = server_episode.position / 1000.0
32
+ end
33
+
34
+ puts " + Episode \"#{server_episode.title}\" #{server_episode.duration.zero? ? "#{(server_episode.position / 1000)} sec." : "#{(server_episode.position * 100/server_episode.duration).to_i}%"}"
35
+ end
36
+ end
37
+ end
38
+ end # self.sync_itunes
39
+
40
+ def self.sync_podcastly
41
+ Client::Podcast.all.each do |client_podcast|
42
+ unless server_podcast = Server::Podcast.find_by_title(client_podcast.title)
43
+ puts "\n- Podcast \"#{client_podcast.title}\" skipped"
44
+ next
45
+ else
46
+ puts "\n+ Podcast \"#{client_podcast.title}\""
47
+ end
48
+
49
+ client_podcast.episodes.each do |client_episode|
50
+ if client_episode.listened? && client_episode.audio?
51
+ if server_podcast.episodes.detect { |e| e.title == client_episode.title }
52
+ puts " - Episode \"#{client_episode.title}\" skipped"
53
+ next
54
+ end
55
+
56
+ server_episode = Server::Episode.new(nil, server_podcast.id, client_episode.title, client_episode.position * 1000, client_episode.duration * 1000)
57
+
58
+ if server_episode.save
59
+ puts " + Episode \"#{client_episode.title}\" #{(client_episode.position * 100/client_episode.duration).to_i}%"
60
+ else
61
+ puts " ? Episode \"#{client_episode.title}\" failed"
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end # self.sync_podcastly
67
+
68
+ def self.start(options = {})
69
+ puts "Syncing Podcastly -> iTunes"
70
+ sync_itunes
71
+
72
+ puts "\nSyncing iTunes -> Podcastly"
73
+ sync_podcastly
74
+ end # self.start
75
+ end # Sync
76
+ end # Ipodcastly
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ipodcastly
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Denis Barushev
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-10 00:00:00 +02:00
13
+ default_executable: ipodcastly
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rb-appscript
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.5.3
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: httparty
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.4.5
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: choice
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.1.4
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: rspec
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.2.9
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: mocha
57
+ type: :development
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 0.9.8
64
+ version:
65
+ description: The gem was built as a simple example to demonstrate how podcastly.com API works.
66
+ email: barushev@gmail.com
67
+ executables:
68
+ - ipodcastly
69
+ extensions: []
70
+
71
+ extra_rdoc_files:
72
+ - LICENSE
73
+ - README.md
74
+ files:
75
+ - LICENSE
76
+ - README.md
77
+ - bin/ipodcastly
78
+ - lib/ipodcastly.rb
79
+ - lib/ipodcastly/client/episode.rb
80
+ - lib/ipodcastly/client/podcast.rb
81
+ - lib/ipodcastly/server/episode.rb
82
+ - lib/ipodcastly/server/podcast.rb
83
+ - lib/ipodcastly/sync.rb
84
+ has_rdoc: true
85
+ homepage: http://github.com/denis/ipodcastly
86
+ licenses: []
87
+
88
+ post_install_message:
89
+ rdoc_options:
90
+ - --charset=UTF-8
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: "0"
98
+ version:
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: "0"
104
+ version:
105
+ requirements: []
106
+
107
+ rubyforge_project:
108
+ rubygems_version: 1.3.5
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: Ipodcastly is a console client for syncing local iTunes library with podcastly.com.
112
+ test_files: []
113
+