Skip to content

Instantly share code, notes, and snippets.

@sbbosco
Created May 11, 2023 17:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sbbosco/4877a303566916414c08db99dcd0313b to your computer and use it in GitHub Desktop.
Save sbbosco/4877a303566916414c08db99dcd0313b to your computer and use it in GitHub Desktop.
Simple Pythonista script that read iOS Music Library
import sys, os, time
import sqlite3
from datetime import date
from threading import current_thread, main_thread
from objc_util import *
import ui
import dialogs
"""
Simple Pythonista script that reads iOS Music Library
"""
winx, winy = ui.get_window_size()
if winx > 850:
w, h = (540, 640)
else:
w, h = (winx, winy)
class MainViewer(ui.View):
def __init__(self):
self.name = 'My Music'
self.frame = (0, 0, w, h)
self.flex = 'WH'
self.background_color = 'gray'
btnplay = ui.ButtonItem(image=ui.Image.named('iob:ios7_play_24'), action=self.playall)
btnpause = ui.ButtonItem(image=ui.Image.named('iob:ios7_pause_24'), action=self.pause)
self.right_button_items = [btnplay, btnpause]
btnw = w/3
btntitle = ui.Button(title='Title')
btntitle.background_color = 'gray'
btntitle.tint_color = 'white'
btntitle.border_width = 1
btntitle.frame = (0, 0, btnw, 40)
btntitle.action = self.do_filter
self.btn1 = btntitle
self.add_subview(btntitle)
btnartist = ui.Button(title='Artist')
btnartist.background_color = 'gray'
btnartist.tint_color = 'white'
btnartist.border_width = 1
btnartist.frame = (btnw, 0, btnw, 40)
btnartist.action = self.do_filter
self.btn2 = btnartist
self.add_subview(btnartist)
btnalbum = ui.Button(title='Album')
btnalbum.background_color = 'gray'
btnalbum.tint_color = 'white'
btnalbum.border_width = 1
btnalbum.frame = (btnw*2, 0, btnw, 40)
btnalbum.action = self.do_filter
self.btn3 = btnalbum
self.add_subview(btnalbum)
self.tv = ui.TableView()
self.tv.frame = (0, 40, w, h - 40)
self.tv.flex = 'RB'
self.ds = ui.ListDataSource([])
self.tv.data_source = self.tv.delegate = self.ds
self.ds.tableview_cell_for_row = self.table_cell_for_row
self.ds.tableview_accessory_button_tapped = self.tableview_accessory_button_tapped
self.add_subview(self.tv)
self.filters = {
'artist': None,
'album': None,
'title': None
}
self.use_db = True
if self.use_db:
self.db = SqliteDb()
self.imusic = {}
self.music = iMusic()
self.build_imusic()
def layout(self):
x, y, w, h = self.frame
btnw = w/3
self.btn1.frame = (0, 0, btnw, 40)
self.btn2.frame = (btnw, 0, btnw, 40)
self.btn3.frame = (btnw * 2, 0, btnw, 40)
self.tv.frame = (0, 40, w, h - 40)
@ui.in_background
def build_imusic(self):
print(current_thread().name)
self.imusic['Songs'] = {}
for i, item in enumerate(self.music.songs):
#for item in music.songs:
id = str(item.persistentID())
title = str(item.title())
artist = str(item.artist())
artistid = str(item.artistPersistentID())
album = str(item.albumTitle())
albumid = str(item.albumPersistentID())
storeid = str(item.playbackStoreID())
self.imusic['Songs'][id] = {
'id': id,
'title': title,
'artist': artist,
'artistid': artistid,
'album': album,
'albumid': albumid,
'index': i,
'storeid': storeid,
}
print('done')
self.reload_ds()
@ui.in_background
def playall(self, sender):
print('play')
songs = self.imusic['Songs']
playlist = []
for item in self.ds.items:
id = item[4]
mediaitem = songs.get(id, None)
if mediaitem:
playlist.append(self.music.songs[mediaitem['index']])
print('playlist:', len(playlist))
self.music.playsong(playlist)
@ui.in_background
def pause(self, sender):
print('pause')
self.music.pause()
def reload(self):
if self.use_db:
self.reload_db()
else:
self.reload_ds()
@ui.in_background
def reload_db(self):
rows = self.db.query('songs',
[
self.filters['title'],
self.filters['artist'],
self.filters['album']
]
)
print('reload db')
self.ds.items = rows
def reload_ds(self):
print('reload ds')
print(current_thread().name)
songs = self.imusic['Songs']
dsitems = []
for key in songs:
#print(songs[key]['title'])
idx = songs[key]['index']
title = songs[key]['title']
artist = songs[key]['artist']
album = songs[key]['album']
persistentid = songs[key]['id']
if self.filters['title']:
if not self.filters['title'] in title.lower():
continue
if self.filters['artist']:
if not self.filters['artist'] in artist.lower():
continue
if self.filters['album']:
if not self.filters['album'] in album.lower():
continue
dsitems.append([idx, title, artist, album, persistentid])
#print(dsitems)
if self.use_db:
self.db.build_db(dsitems)
self.reload_db()
else:
self.ds.items = dsitems
def will_close(self):
@ui.in_background
def _close_db():
self.db.close_db()
_close_db()
def table_cell_for_row(self, tableview, section, row):
item = self.ds.items[row]
cell = ui.TableViewCell('subtitle')
cell.text_label.text = str(item[1])
cell.detail_text_label.text = ' ' + item[2]
"""
cell.detail_text_label.text = ' ' + \
'{:{align}{width}s}'.format(str(item[2])[:30], width=34, align='<') + item[3]
"""
cell.accessory_type = 'detail_button'
cell.detail_text_label.font = ('Menlo', 14)
return cell
def tableview_accessory_button_tapped(self, tableview, section, row):
print('accessory: ', row)
print(current_thread().name)
@ui.in_background
def do_filter(self, sender):
print(sender.title)
print(current_thread().name)
filter_type = sender.title.lower()
#data = FilterDialog.display('filter_data')
if filter_type == 'artist':
print('artist')
value = self.filters['artist'] if self.filters['artist'] else ''
fields = [
{'key': 'artist', 'title': 'Artist', 'type': 'text', 'value': value}
]
result = dialogs.form_dialog(title='Artist', fields=fields)
elif filter_type == 'album':
print('album')
value = self.filters['album'] if self.filters['album'] else ''
fields = [
{'key': 'album', 'title': 'Album', 'type': 'text', 'value': value}
]
result = dialogs.form_dialog(title='Album', fields=fields)
elif filter_type == 'title':
print('title')
value = self.filters['title'] if self.filters['title'] else ''
fields = [
{'key': 'title', 'title': 'Title', 'type': 'text', 'value': value}
]
result = dialogs.form_dialog(title='Title', fields=fields)
else:
result = None
#print('data', data)
data = None
self.apply_filter(filter_type, result)
def apply_filter(self, filter_type, result):
print('result', result)
print(current_thread().name)
if filter_type == 'artist':
if result:
self.filters['artist'] = result['artist'].lower()
else:
self.filters['artist'] = None
elif filter_type == 'album':
if result:
self.filters['album'] = result['album'].lower()
else:
self.filters['album'] = None
elif filter_type == 'title':
if result:
self.filters['title'] = result['title'].lower()
else:
self.filters['title'] = None
print(self.filters)
self.reload()
class SqliteDb:
def __init__(self, dbname=':memory:'):
self.dbname = dbname
self.open_db()
self.create_db()
def open_db(self):
self.con = None
if os.path.isfile(self.dbname) or self.dbname == ':memory:':
self.con = sqlite3.connect(self.dbname,
detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES,
check_same_thread=True) #allows us to use date and datetime
self.con.execute("PRAGMA foreign_keys = 1") # allows us to use foreign keys
else:
print("Cannot find DB")
def close_db(self):
print('close db')
self.con.close()
def create_db(self):
sql = """
create table Songs (SongId INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
Title TEXT, Artist TEXT, Album TEXT, PersistentId TEXT)
"""
self.con.execute(sql)
self.con.commit()
def build_db(self, rows):
sql = """
insert into Songs (SongId, Title, Artist, Album, PersistentId)
values (?, ?, ?, ?, ?)
"""
for row in rows:
self.con.execute(sql, row)
self.con.commit()
def query(self, dataset, filters):
title = '%%' if filters[0] is None else '%' + filters[0] + '%'
artist = '%%' if filters[1] is None else '%' + filters[1] + '%'
album = '%%' if filters[2] is None else '%' + filters[2] + '%'
parms = [title, artist, album]
sql = """select SongId, Title, Artist, Album, PersistentId from Songs
where Title like ? and Artist like ? and Album like ?
order by Title
"""
cursor = self.con.execute(sql, parms)
rows = cursor.fetchall()
return rows
class iMusic:
def __init__(self):
MPMediaQuery = ObjCClass('MPMediaQuery')
self.MPMediaItemCollection = ObjCClass('MPMediaItemCollection')
MPMediaItem = ObjCClass('MPMediaItem')
MPMediaPropertyPredicate = ObjCClass('MPMediaPropertyPredicate')
MPMusicPlayerController = ObjCClass('MPMusicPlayerController')
self.player = MPMusicPlayerController.systemMusicPlayer()
NSBundle.bundleWithPath_('/System/Library/Frameworks/MediaPlayer.framework').load()
MPVolumeView = ObjCClass('MPVolumeView')
self.volume_view = MPVolumeView.new().autorelease()
self.firstrun = True
query = MPMediaQuery.songsQuery()
songs = list(query.items())
self.songs = songs
def playsong(self, songitems):
collection = self.MPMediaItemCollection.collectionWithItems(songitems)
self.player.setQueueWithItemCollection(collection)
if self.firstrun:
time.sleep(2)
self.firstrun = False
self.player.play()
def pause(self):
print('pause player')
self.player.pause()
print('begin')
v = MainViewer()
if winx > 850:
v.present('sheet')
else:
v.present('fullscreen')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment