itunes-controller 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.buildpath +5 -0
- data/.gitignore +3 -0
- data/.project +17 -0
- data/.rvmrc +81 -0
- data/LICENSE +674 -0
- data/README +27 -0
- data/Rakefile +27 -0
- data/TODO +8 -0
- data/bin/addFiles.rb +35 -0
- data/bin/dummyiTunesController.rb +109 -0
- data/bin/itunesController.rb +78 -0
- data/bin/listDeadTracks.rb +45 -0
- data/bin/listNewTracks.rb +88 -0
- data/bin/recreateTrackCache.rb +31 -0
- data/bin/refreshFiles.rb +42 -0
- data/bin/removeDeadTracks.rb +31 -0
- data/bin/removeFiles.rb +42 -0
- data/bin/trackInfo.rb +50 -0
- data/itunes-controller.gemspec +24 -0
- data/jar-stuff.sh +24 -0
- data/lib/itunesController/application.rb +127 -0
- data/lib/itunesController/cachedcontroller.rb +188 -0
- data/lib/itunesController/config.rb +95 -0
- data/lib/itunesController/controller_creator.rb +29 -0
- data/lib/itunesController/controllserver.rb +583 -0
- data/lib/itunesController/database/backend.rb +41 -0
- data/lib/itunesController/database/database.rb +166 -0
- data/lib/itunesController/database/sqlite3_backend.rb +67 -0
- data/lib/itunesController/debug.rb +124 -0
- data/lib/itunesController/dummy_itunes_track.rb +40 -0
- data/lib/itunesController/dummy_itunescontroller.rb +142 -0
- data/lib/itunesController/itunescontroller.rb +90 -0
- data/lib/itunesController/itunescontroller_factory.rb +51 -0
- data/lib/itunesController/kinds.rb +138 -0
- data/lib/itunesController/logging.rb +119 -0
- data/lib/itunesController/macosx_itunescontroller.rb +204 -0
- data/lib/itunesController/platform.rb +53 -0
- data/lib/itunesController/sqlite_creator.rb +35 -0
- data/lib/itunesController/track.rb +39 -0
- data/lib/itunesController/version.rb +25 -0
- data/lib/itunesController/windows_itunescontroller.rb +145 -0
- data/test/base_server_test_case.rb +125 -0
- data/test/dummy_client.rb +44 -0
- data/test/test_server.rb +312 -0
- metadata +185 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2011-2012 John-Paul.Stanford <dev@stanwood.org.uk>
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
#
|
17
|
+
# Author:: John-Paul Stanford <dev@stanwood.org.uk>
|
18
|
+
# Copyright:: Copyright (C) 2011 John-Paul.Stanford <dev@stanwood.org.uk>
|
19
|
+
# License:: GNU General Public License v3 <http://www.gnu.org/licenses/>
|
20
|
+
#
|
21
|
+
|
22
|
+
module ItunesController
|
23
|
+
|
24
|
+
class Platform
|
25
|
+
|
26
|
+
def self.getUserHomeDir()
|
27
|
+
homes = ["HOME", "HOMEPATH"]
|
28
|
+
realHome = homes.detect {|h| ENV[h] != nil}
|
29
|
+
if not realHome
|
30
|
+
raise "Could not find home directory"
|
31
|
+
end
|
32
|
+
|
33
|
+
return ENV[realHome]
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.isWindows()
|
37
|
+
return RUBY_PLATFORM =~ /mswin|mingw/
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.isMacOSX()
|
41
|
+
return RUBY_PLATFORM =~ /darwin/
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.isLinux()
|
45
|
+
return RUBY_PLATFORM =~ /linux/
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.isJRuby()
|
49
|
+
return RUBY_PLATFORM =~ /java/
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2011-2012 John-Paul.Stanford <dev@stanwood.org.uk>
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
#
|
17
|
+
# Author:: John-Paul Stanford <dev@stanwood.org.uk>
|
18
|
+
# Copyright:: Copyright (C) 2011 John-Paul.Stanford <dev@stanwood.org.uk>
|
19
|
+
# License:: GNU General Public License v3 <http://www.gnu.org/licenses/>
|
20
|
+
#
|
21
|
+
|
22
|
+
require 'itunesController/database/sqlite3_backend'
|
23
|
+
require 'itunesController/itunescontroller_factory'
|
24
|
+
require 'itunesController/controller_creator'
|
25
|
+
|
26
|
+
module ItunesController
|
27
|
+
|
28
|
+
class SQLLiteControllerCreator < ControllerCreator
|
29
|
+
def createController()
|
30
|
+
dbBackend = ItunesController::SQLite3DatabaseBackend.new(nil)
|
31
|
+
return ItunesController::CachedController.new(ItunesController::ITunesControllerFactory::createController(),dbBackend)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2011-2012 John-Paul.Stanford <dev@stanwood.org.uk>
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
#
|
17
|
+
# Author:: John-Paul Stanford <dev@stanwood.org.uk>
|
18
|
+
# Copyright:: Copyright (C) 2011 John-Paul.Stanford <dev@stanwood.org.uk>
|
19
|
+
# License:: GNU General Public License v3 <http://www.gnu.org/licenses/>
|
20
|
+
#
|
21
|
+
|
22
|
+
module ItunesController
|
23
|
+
|
24
|
+
# Used to store track information
|
25
|
+
class Track
|
26
|
+
attr_accessor :location,:databaseId,:title
|
27
|
+
|
28
|
+
def initialize(location,databaseId,title)
|
29
|
+
@location = location
|
30
|
+
@databaseId = databaseId
|
31
|
+
@title = title
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
return "Location: '#{@location}' - Database ID: #{@databaseId} - Title: '#{@title}' "
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2011-2012 John-Paul.Stanford <dev@stanwood.org.uk>
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
#
|
17
|
+
# Author:: John-Paul Stanford <dev@stanwood.org.uk>
|
18
|
+
# Copyright:: Copyright (C) 2011 John-Paul.Stanford <dev@stanwood.org.uk>
|
19
|
+
# License:: GNU General Public License v3 <http://www.gnu.org/licenses/>
|
20
|
+
#
|
21
|
+
|
22
|
+
module ItunesController
|
23
|
+
# The itunes-remote-control-server version
|
24
|
+
VERSION = "0.1.0"
|
25
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2011-2012 John-Paul.Stanford <dev@stanwood.org.uk>
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
#
|
17
|
+
# Author:: John-Paul Stanford <dev@stanwood.org.uk>
|
18
|
+
# Copyright:: Copyright (C) 2011 John-Paul.Stanford <dev@stanwood.org.uk>
|
19
|
+
# License:: GNU General Public License v3 <http://www.gnu.org/licenses/>
|
20
|
+
#
|
21
|
+
|
22
|
+
require 'itunesController/itunescontroller'
|
23
|
+
require 'itunesController/kinds'
|
24
|
+
require 'itunesController/debug'
|
25
|
+
require 'itunesController/logging'
|
26
|
+
require 'win32ole'
|
27
|
+
|
28
|
+
module ItunesController
|
29
|
+
|
30
|
+
class WindowsITunesController < ItunesController::BaseITunesController
|
31
|
+
|
32
|
+
# The constructor
|
33
|
+
def initialize
|
34
|
+
@iTunes = WIN32OLE.new('iTunes.Application')
|
35
|
+
@libraryPlaylists=@iTunes.LibraryPlaylist
|
36
|
+
end
|
37
|
+
|
38
|
+
# Used to get the iTunes version
|
39
|
+
# @return [String] The itunes version
|
40
|
+
def version
|
41
|
+
return @iTunes.version.to_s+" (Windows)"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Used to tell iTunes to refresh a list of tracks data from the info stored in the files
|
45
|
+
# @param [Array] tracks A list of tracks to fresh
|
46
|
+
def refreshTracks(tracks)
|
47
|
+
tracks.reverse.each do | track |
|
48
|
+
track.UpdateInfoFromFile
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Used to remove tracks from the libaray
|
53
|
+
# @param [Array] tracks A list of tracks to remove from the itunes libaray
|
54
|
+
def removeTracksFromLibrary(tracks)
|
55
|
+
tracks.reverse.each do | track |
|
56
|
+
track.Delete
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Used to add a list of files to the itunes library
|
61
|
+
# @param [Array[String]] A list of files to add to the itunes library
|
62
|
+
# @return [Array[ItunesController::Track]] List of ids of the new tracks once they are in the database
|
63
|
+
def addFilesToLibrary(files)
|
64
|
+
tracks=[]
|
65
|
+
files.each do | file |
|
66
|
+
if (!File.exist?(file))
|
67
|
+
ItunesController::ItunesControllerLogging::error("Unable to find file #{file}")
|
68
|
+
else
|
69
|
+
itracks=@libraryPlaylists.AddFile(file)
|
70
|
+
if (itracks!=nil)
|
71
|
+
itracks=itracks.Tracks
|
72
|
+
for i in (1..itracks.Count())
|
73
|
+
itrack=itracks.Item(i)
|
74
|
+
track=ItunesController::Track.new(file,itrack.TrackDatabaseID,itrack.Name)
|
75
|
+
tracks.push(track)
|
76
|
+
end
|
77
|
+
else
|
78
|
+
ItunesController::ItunesControllerLogging::error("Unable to add file '#{file}'")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
return tracks;
|
84
|
+
end
|
85
|
+
|
86
|
+
def getTracks(&b)
|
87
|
+
ItunesController::ItunesControllerLogging::debug("Retriving track information...")
|
88
|
+
playlist=@libraryPlaylists
|
89
|
+
fileTracks = playlist.Tracks
|
90
|
+
size = fileTracks.Count()
|
91
|
+
count = 1
|
92
|
+
for count in (1..size)
|
93
|
+
track=fileTracks.Item(count)
|
94
|
+
location=track.location
|
95
|
+
dead=false
|
96
|
+
if (location!=nil)
|
97
|
+
if (!File.exist?(location))
|
98
|
+
dead = true
|
99
|
+
end
|
100
|
+
else
|
101
|
+
dead = true
|
102
|
+
end
|
103
|
+
if (count % 1000 == 0)
|
104
|
+
ItunesController::ItunesControllerLogging::debug("Found tracks #{count} of #{size}")
|
105
|
+
end
|
106
|
+
if (track.name!=nil)
|
107
|
+
b.call(ItunesController::Track.new(location,track.TrackDatabaseID,track.name),count,size,dead)
|
108
|
+
end
|
109
|
+
count=count+1
|
110
|
+
end
|
111
|
+
ItunesController::ItunesControllerLogging::debug("Found tracks #{count-1} of #{size}")
|
112
|
+
return size
|
113
|
+
end
|
114
|
+
|
115
|
+
# Used to find the number of tracks in the library
|
116
|
+
# @return [Number] The number of tracks
|
117
|
+
def getTrackCount()
|
118
|
+
return @libraryPlaylists.Tracks.Count()
|
119
|
+
end
|
120
|
+
|
121
|
+
# Used to search the itunes library
|
122
|
+
# @param term The search term
|
123
|
+
# @return [Array] a list of iTunes track that match the search term
|
124
|
+
def searchLibrary(term)
|
125
|
+
tracks=[]
|
126
|
+
playlist=@libraryPlaylists
|
127
|
+
foundTracks = playlist.Search(term,1)
|
128
|
+
if (foundTracks!=nil)
|
129
|
+
foundTracks.each do | t |
|
130
|
+
tracks.push(t)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
return tracks
|
135
|
+
end
|
136
|
+
|
137
|
+
# Used to get the database of a itunes track
|
138
|
+
# @abstract Must be overridden
|
139
|
+
# @param track the track
|
140
|
+
# @return The database id
|
141
|
+
def getTrackDatabaseId(track)
|
142
|
+
return track.TrackDatabaseID()
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
|
2
|
+
require 'tempfile'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'socket'
|
5
|
+
require 'timeout'
|
6
|
+
|
7
|
+
require 'itunesController/platform'
|
8
|
+
require 'itunesController/config'
|
9
|
+
require 'itunesController/dummy_itunescontroller'
|
10
|
+
require 'itunesController/controllserver'
|
11
|
+
require 'itunesController/cachedcontroller'
|
12
|
+
require 'itunesController/controller_creator'
|
13
|
+
|
14
|
+
require 'itunesController/database/sqlite3_backend'
|
15
|
+
|
16
|
+
class DummyControllerCreator < ItunesController::ControllerCreator
|
17
|
+
|
18
|
+
def initialize(controller)
|
19
|
+
@controller = controller
|
20
|
+
end
|
21
|
+
|
22
|
+
def createController()
|
23
|
+
return @controller
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class BaseServerTest < Test::Unit::TestCase
|
28
|
+
|
29
|
+
USER="test"
|
30
|
+
PASSWORD="testpass"
|
31
|
+
MIN_PORT_NUMBER = 5000
|
32
|
+
MAX_PORT_NUMBER = 9000
|
33
|
+
|
34
|
+
attr_accessor :server,:port
|
35
|
+
|
36
|
+
def portOpen?(ip, port, seconds=1)
|
37
|
+
Timeout::timeout(seconds) do
|
38
|
+
begin
|
39
|
+
TCPSocket.new(ip, port).close
|
40
|
+
true
|
41
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
rescue Timeout::Error
|
46
|
+
false
|
47
|
+
end
|
48
|
+
|
49
|
+
def findAvaliablePort
|
50
|
+
port=BaseServerTest::MIN_PORT_NUMBER
|
51
|
+
while (port < BaseServerTest::MAX_PORT_NUMBER )
|
52
|
+
if !portOpen?("127.0.0.1",port)
|
53
|
+
return port
|
54
|
+
end
|
55
|
+
port+=1
|
56
|
+
end
|
57
|
+
return -1
|
58
|
+
end
|
59
|
+
|
60
|
+
def setupServer(tracks=nil)
|
61
|
+
ItunesController::DummyITunesController::resetCommandLog()
|
62
|
+
ItunesController::DummyITunesController::resetTracks()
|
63
|
+
if (tracks!=nil)
|
64
|
+
tracks.each do | track |
|
65
|
+
ItunesController::DummyITunesController::forceAddTrack(track)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
ItunesController::DummyITunesController::getCommandLog().take_while {
|
70
|
+
| el |
|
71
|
+
}
|
72
|
+
@dbFile = Tempfile.new('dummyDatabase.db')
|
73
|
+
itunes = ItunesController::DummyITunesController.new
|
74
|
+
dbBackend = ItunesController::SQLite3DatabaseBackend.new(@dbFile.path)
|
75
|
+
controller = ItunesController::CachedController.new(itunes,dbBackend)
|
76
|
+
config=ItunesController::ServerConfig.new
|
77
|
+
config.port = findAvaliablePort
|
78
|
+
@port = config.port
|
79
|
+
config.username = BaseServerTest::USER
|
80
|
+
config.password = BaseServerTest::PASSWORD
|
81
|
+
@server=ItunesController::ITunesControlServer.new(config,config.port,DummyControllerCreator.new(controller))
|
82
|
+
end
|
83
|
+
|
84
|
+
def teardownServer
|
85
|
+
@server.stop
|
86
|
+
while !@server.stopped?
|
87
|
+
end
|
88
|
+
@server.join
|
89
|
+
@dbFile.unlink
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_dummy
|
93
|
+
end
|
94
|
+
|
95
|
+
def assertCommandLog(expected)
|
96
|
+
sleep(2)
|
97
|
+
@server.waitForEmptyJobQueue()
|
98
|
+
actual=ItunesController::DummyITunesController::getCommandLog()
|
99
|
+
error=false
|
100
|
+
if (expected.size()!=actual.size())
|
101
|
+
puts("Worng number of command log entries")
|
102
|
+
error = true
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
for i in (0..expected.size())
|
107
|
+
if (expected[i]!=actual[i])
|
108
|
+
error=true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
if (error)
|
112
|
+
puts("Command log did not match expected.")
|
113
|
+
puts("Expected:")
|
114
|
+
expected.each do | line |
|
115
|
+
puts(" "+line)
|
116
|
+
end
|
117
|
+
puts("Actual:")
|
118
|
+
actual.each do | line |
|
119
|
+
puts(" "+line)
|
120
|
+
end
|
121
|
+
raise("Command log did not match expected.")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
require 'net/telnet'
|
3
|
+
|
4
|
+
require 'itunesController/controllserver'
|
5
|
+
|
6
|
+
class DummyClient
|
7
|
+
|
8
|
+
DEFAULT_TIMEOUT=40
|
9
|
+
|
10
|
+
def connect(hostname,port)
|
11
|
+
@client=Net::Telnet::new('Host' => hostname,
|
12
|
+
'Port' => port,
|
13
|
+
'Telnetmode' => false)
|
14
|
+
@client.waitfor('String'=>"001 hello")
|
15
|
+
sendCommand("HELO",220)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def disconnect
|
20
|
+
@client.close
|
21
|
+
end
|
22
|
+
|
23
|
+
def login(username,password)
|
24
|
+
sendCommand(ItunesController::CommandName::LOGIN+":"+username,222);
|
25
|
+
sendCommand(ItunesController::CommandName::PASSWORD+":"+password,223);
|
26
|
+
end
|
27
|
+
|
28
|
+
def sendCommand(cmd, expectedCode,timeout=DummyClient::DEFAULT_TIMEOUT)
|
29
|
+
@client.cmd(cmd) do | response |
|
30
|
+
if (response!=nil)
|
31
|
+
response.each_line do | line |
|
32
|
+
if ( line =~ /(\d+).*/)
|
33
|
+
code=$1
|
34
|
+
if (code.to_i==expectedCode)
|
35
|
+
return;
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
raise "Did not recive expected response from server"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|