bluemindo-0.3/locale/zh_CN/LC_MESSAGES/bluemindo.po0000644000175000017500000004602411226426031021600 0ustar xbrightxbright# zh_CN translations for bluemindo. # Copyright (C) 2008 Bluemindo'S COPYRIGHT HOLDER # This file is distributed under the same license as the Bluemindo package. # ideal Shang <05281253@bjtu.edu.cn>, 2008. # msgid "" msgstr "" "Project-Id-Version: Bluemindo 0.2.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-07-06 02:04+0400\n" "PO-Revision-Date: 2009-07-12 15:45+0800\n" "Last-Translator: ideal Shang <05281253@bjtu.edu.cn>\n" "Language-Team: zh_CN \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: src/plugins/notification/__init__.py:37 msgid "Show desktop notification" msgstr "显示桌面通知" #: src/plugins/notification/config.py:87 msgid "" "Available tags:\n" "{title}\t\tInsert the title\n" "{artist}\tInsert the artist\n" "{album}\t\tInsert the album\n" "{comment}\tInsert the comment\n" "{genre}\t\tInsert the genre\n" "{year}\t\tInsert the year\n" "{track}\t\tInsert the track number\n" "{length}\tInsert the length" msgstr "" "可用标签:\n" "{title}\t\t插入标题\n" "{artist}\t\t插入艺术家\n" "{album}\t\t插入专辑\n" "{comment}\t插入评论\n" "{genre}\t\t插入流派" "{year}\t\t插入年份\n" "{track}\t\t插入音轨\n" "{length}\t\t插入长度" #: src/plugins/jabber/__init__.py:46 msgid "Send PEP notifications" msgstr "发送 PEP 通知" #: src/plugins/audioscrobbler/__init__.py:40 msgid "Send song notifications" msgstr "发送歌曲通知" #: src/gui/extensionsconfig.py:45 msgid "Preferences" msgstr "首选项" #: src/gui/extensionsconfig.py:192 msgid "This extension is not configurable." msgstr "此插件不可配置。" #: src/gui/extensionsconfig.py:208 msgid "You should start the plugin before trying to configure it." msgstr "在配置插件之前您需要先启动它。" #: src/modules/explorer/views/normal.py:118 #: src/modules/explorer/views/lightweight.py:49 #: src/modules/explorer/views/basic.py:82 #: src/modules/explorer/views/full.py:111 #: src/modules/explorer/views/albums.py:84 msgid "Opening folder…" msgstr "打开文件夹..." #: src/modules/explorer/views/normal.py:147 #: src/modules/explorer/views/lightweight.py:75 #: src/modules/explorer/views/basic.py:109 #: src/modules/explorer/views/full.py:137 #: src/modules/explorer/views/albums.py:110 msgid "Loading music…" msgstr "正在加载音乐..." #: src/modules/explorer/views/normal.py:155 #: src/modules/explorer/views/basic.py:117 #: src/modules/explorer/views/full.py:145 #: src/modules/explorer/views/albums.py:118 msgid "Loading artists…" msgstr "正在加载艺术家..." #: src/modules/explorer/views/normal.py:174 #, python-format msgid "%(art)d artists (%(sng)d songs)" msgstr "%(art)d 艺术家 (%(sng)d 歌曲)" #: src/modules/explorer/views/normal.py:215 #: src/modules/explorer/views/normal.py:232 #, python-format msgid "%(alb)d albums (%(sng)d songs)" msgstr "%(alb)d 专辑 (%(sng)d 歌曲)" #: src/modules/explorer/views/__init__.py:89 msgid "" "Welcome in Bluemindo!\n" "\n" "First of all, you need to configure the explorer module in order to choose " "your root music directory. This is easy! Go in the File menu, click on " "Preferences and you will be able to configure all Bluemindo's modules." msgstr "" "欢迎来到Bluemindo!\n" "首先,请您配置资源管理模块以便选择您的音乐根目录。这很简单!在 文件 菜单中," "点击 首选项,您就可以配置 Bluemindo 的所有模块了。" #: src/modules/explorer/views/full.py:295 #, python-format msgid "%d albums" msgstr "%d 专辑" #: src/modules/explorer/views/full.py:296 #, python-format msgid "%d songs" msgstr "%d 歌曲" #: src/modules/explorer/views/full.py:301 #, python-format msgid "%d plays" msgstr "%d 次播放" #: src/modules/explorer/views/webradios.py:97 msgid "Bookmarks" msgstr "书签" #: src/modules/explorer/views/webradios.py:98 msgid "World Wide Web" msgstr "万维网" #: src/modules/explorer/views/webradios.py:107 #: src/modules/explorer/views/webradios.py:205 #: src/modules/explorer/views/webradios.py:281 msgid "Downloading…" msgstr "正在下载..." #: src/modules/explorer/views/webradios.py:129 msgid "Name" msgstr "名称" #: src/modules/explorer/views/webradios.py:138 msgid "Listener count" msgstr "收听者统计" #: src/modules/explorer/views/playlists.py:96 msgid "Top 50 songs" msgstr "歌曲 Top 50" #: src/modules/explorer/views/playlists.py:97 msgid "Top 100 songs" msgstr "歌曲 Top 50" #: src/modules/explorer/views/playlists.py:98 msgid "Top 10 albums" msgstr "专辑 Top 10" #: src/modules/explorer/views/playlists.py:99 msgid "Random 50 songs" msgstr "随机 50 首歌曲" #: src/modules/explorer/views/playlists.py:100 msgid "Random 100 songs" msgstr "随机 100 首歌曲" #: src/modules/explorer/views/playlists.py:238 msgid "Delete the playlist?" msgstr "删除播放列表?" #: src/modules/explorer/views/playlists.py:247 #, python-format msgid "Do you really want to delete the %s playlist?" msgstr "您真的想删除 %d 个播放列表吗?" #: src/modules/explorer/views/playlists.py:264 #: src/modules/explorer/views/playlists.py:293 msgid "Winamp-like playlists" msgstr "Winamp 风格的播放列表" #: src/modules/explorer/views/playlists.py:266 msgid "Choose a playlist" msgstr "选择一个播放列表" #: src/modules/explorer/views/playlists.py:295 msgid "Export playlist as…" msgstr "导出播放列表为..." #: src/modules/explorer/playlist.py:100 src/modules/explorer/playlist.py:169 #: src/modules/explorer/configuration.glade:187 msgid "Title" msgstr "标题" #: src/modules/explorer/playlist.py:101 src/modules/explorer/playlist.py:170 #: src/modules/explorer/configuration.glade:173 msgid "Artist" msgstr "艺术家" #: src/modules/explorer/playlist.py:102 src/modules/explorer/playlist.py:171 #: src/modules/explorer/configuration.glade:143 msgid "Album" msgstr "专辑" #: src/modules/explorer/playlist.py:168 msgid "#" msgstr "#" #: src/modules/explorer/playlist.py:172 #: src/modules/explorer/configuration.glade:157 msgid "Genre" msgstr "流派" #: src/modules/explorer/playlist.py:173 #: src/modules/explorer/configuration.glade:108 msgid "Comment" msgstr "评论" #: src/modules/explorer/playlist.py:174 #: src/modules/explorer/configuration.glade:94 msgid "Year" msgstr "年" #: src/modules/explorer/playlist.py:175 #: src/modules/explorer/configuration.glade:124 msgid "Length" msgstr "长度" #: src/modules/explorer/playlist.py:417 #, python-format msgid "Added %(nbsongs)u songs (%(totalength)s)." msgstr "已添加 %(nbsongs)u 首歌曲 (%(totalength)s)。" #: src/modules/explorer/musicdb.py:81 #, python-format msgid "Added %d songs" msgstr "已添加 %d 首歌曲" #: src/modules/explorer/musicdb.py:98 msgid "Update statistics…" msgstr "正在更新统计..." #: src/modules/explorer/config.py:118 msgid "Lightweight" msgstr "轻量级" #: src/modules/explorer/config.py:119 msgid "Basic" msgstr "基本" #: src/modules/explorer/config.py:120 msgid "Normal" msgstr "正常" #: src/modules/explorer/config.py:121 msgid "Full" msgstr "完全" #: src/modules/explorer/config.py:122 msgid "Albums" msgstr "专辑" #: src/modules/explorer/config.py:123 msgid "Playlists" msgstr "播放列表" #: src/modules/explorer/config.py:124 src/modules/player/player_gui.py:484 msgid "Webradios" msgstr "网络收音机" #: src/modules/player/player_gui.py:116 msgid "Change album artwork" msgstr "更换专辑封面" #: src/modules/lyrics/__init__.py:119 #, python-format msgid "Downloading lyrics for %(title)s - %(artist)s…" msgstr "正在下载歌词 (%(title)s - %(artist)s)..." #: src/modules/lyrics/__init__.py:124 #, python-format msgid "No lyrics found for %(title)s - %(artist)s!" msgstr "歌词未找到(%(title)s - %(artist)s)!" #: src/modules/lyrics/lyricsdownloader.py:44 msgid "Lyrics fetching is currently disabled." msgstr "歌词下载已禁用。" #: src/bluemindo.py:46 msgid "" "Bluemindo Copyright (C) 2007-2009 Erwan Briand\n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to\n" "redistribute it under certain conditions.\n" "\n" "Usage: bluemindo[.py] [options]\n" "\n" "Available options:\n" "--current\t\tShow the current playing song artist, title and album\n" "--current-cover\t\tShow the path to the cover of the current playing song\n" "--current-lyrics\tShow the lyrics for the current playing song\n" "\n" "--playpause, --play, --pause\tPlay or pause a song\n" "--stop\t\t\tStop a song\n" "--previous\t\tJump to the previous song in playlist\n" "--next\t\t\tJump to the next song in playlist\n" "\n" "--volume-more [STEP]\tIncrease the volume, you can specify a step (0 > 100)\n" "--volume-less [STEP]\tDecrease the volume, you can specify a step (0 > 100)\n" "--volume=VOLUME\t\tSet the volume: 0 > 100\n" "\n" "--reload\t\tReload the songs from your music folder\n" "--quit, --plunge\tQuit Bluemindo" msgstr "" "Bluemindo Copyright (C) 2007-2008 Erwan Briand\n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to\n" "redistribute it under certain conditions.\n" "\n" "使用方法: bluemindo.py [选项]\n" "\n" "可用选项:\n" "--current\t\t显示当前播放歌曲的艺术家,标题及专辑\n" "--current-cover\t\t显示当前播放歌曲封面的路径\n" "--current-lyrics\t显示当前播放歌曲的歌词\n" "\n" "--playpause, --play, --pause\t播放或暂停\n" "--stop\t\t\t\t停止播放\n" "--previous\t\t\t跳到播放列表中的上一首歌曲\n" "--next\t\t\t\t跳到播放列表中的下一首歌曲\n" "\n" "--volume-more [STEP]\t\t提高音量,您可以设置步数 (0 > 100)\n" "--volume-less [STEP]\t\t降低音量,您可以设置步数 (0 > 100)\n" "--volume=VOLUME\t\t\t设置音量: 0 > 100\n" "\n" "--reload\t\t\t从您的音乐文件夹重新加载歌曲\n" "--quit, --plunge\t\t退出 Bluemindo" #: src/bluemindo.py:132 msgid "Bluemindo is already started!" msgstr "Bluemindo 已经启动!" #: src/bluemindo.py:133 msgid "" "The socket file located at /tmp/bluemindo already exists, so " "Bluemindo cannot start. If you still want to start Bluemindo, hit \"yes\" " "but part of the software might be broken." msgstr "" "位于 /tmp/bluemindo 的socket文件已经存在,因此 Bluemindo" "无法启动。如果您仍然希望启动 Bluemindo,请点击“yes”,但可能会导致软件崩溃。" #: src/plugins/notification/configuration.glade:51 #: src/modules/explorer/editsongwindow.glade:140 msgid "Title:" msgstr "标题:" #: src/plugins/notification/configuration.glade:95 msgid "Text:" msgstr "文本:" #: src/plugins/notification/configuration.glade:120 msgid "ms" msgstr "ms" #: src/plugins/notification/configuration.glade:142 msgid "Timeout:" msgstr "超时:" #: src/plugins/jabber/configuration.glade:67 msgid "Jabber identifiant:" msgstr "Jabber 账号:" #: src/plugins/jabber/configuration.glade:80 #: src/plugins/audioscrobbler/configuration.glade:67 msgid "Password:" msgstr "密码:" #: src/plugins/audioscrobbler/configuration.glade:52 #: src/modules/lyrics/configuration.glade:60 msgid "Server:" msgstr "服务器:" #: src/plugins/audioscrobbler/configuration.glade:83 msgid "Username:" msgstr "用户名:" #: src/modules/explorer/configuration.glade:40 #: src/modules/explorer/configuration.glade:239 #: src/modules/player/configuration.glade:39 #: src/modules/player/configuration.glade:72 #: src/modules/player/configuration.glade:155 #: src/modules/lyrics/configuration.glade:39 msgid "Yes" msgstr "是" #: src/modules/explorer/configuration.glade:58 msgid "Fetch artworks:" msgstr "获取艺术品:" #: src/modules/explorer/configuration.glade:74 msgid "View mode:" msgstr "查看模式:" #: src/modules/explorer/configuration.glade:201 msgid "Track" msgstr "音轨" #: src/modules/explorer/configuration.glade:221 msgid "Show columns:" msgstr "显示列:" #: src/modules/explorer/configuration.glade:276 msgid "Folder:" msgstr "文件夹:" #: src/modules/explorer/configuration.glade:289 msgid "Scan at startup:" msgstr "启动时扫描:" #: src/modules/explorer/editsongwindow.glade:91 msgid "Year:" msgstr "年:" #: src/modules/explorer/editsongwindow.glade:103 msgid "Track:" msgstr "音轨" #: src/modules/explorer/editsongwindow.glade:128 msgid "Genre:" msgstr "流派:" #: src/modules/explorer/editsongwindow.glade:148 msgid "Artist:" msgstr "艺术家:" #: src/modules/explorer/editsongwindow.glade:160 msgid "Album:" msgstr "专辑:" #: src/modules/explorer/editsongwindow.glade:209 msgid "Comment:" msgstr "评论:" #: src/modules/explorer/editsongwindow.glade:242 msgid "Edit a song" msgstr "编辑歌曲" #: src/modules/player/configuration.glade:27 msgid "Change window title:" msgstr "更改窗口标题:" #: src/modules/player/configuration.glade:60 msgid "Show a popup with cover:" msgstr "显示带有封面的弹出框:" #: src/modules/player/configuration.glade:95 msgid "GStreamer playback:" msgstr "GStreamer 回放:" #: src/modules/player/configuration.glade:108 msgid "Default" msgstr "默认" #: src/modules/player/configuration.glade:118 msgid "Gapless" msgstr "无缝" #: src/modules/player/configuration.glade:145 msgid "Start minimized:" msgstr "启动时最小化:" #: src/modules/trayicon/traycontext.glade:40 msgid "Bluemindo" msgstr "Bluemindo" #: src/modules/lyrics/configuration.glade:27 msgid "Active lyrics retrieval:" msgstr "激活自动获取歌词:" #: data/glade/playlistmenu.glade:11 msgid "Play now" msgstr "现在播放" #: data/glade/playlistmenu.glade:30 msgid "Add to playlist" msgstr "添加到播放列表" #: data/glade/playlistmenu.glade:49 msgid "Remove from this playlist" msgstr "从播放列表中移除" #: data/glade/playlistmenu.glade:68 msgid "Show lyrics" msgstr "显示歌词" #: data/glade/playlistmenu.glade:81 msgid "Edit song's datas" msgstr "编辑歌曲数据" #: data/glade/playlistmenu.glade:96 msgid "Playlist" msgstr "播放列表" #: data/glade/playlistmenu.glade:127 data/glade/mainwindow.glade:1406 msgid "Name:" msgstr "名称:" #: data/glade/playlistmenu.glade:149 msgid "Create a new playlist" msgstr "创建新播放列表" #: data/glade/prefswindow.glade:69 msgid "Modules" msgstr "模块" #: data/glade/prefswindow.glade:128 msgid "Plugins" msgstr "插件" #: data/glade/aboutdialog.glade:8 msgid "About Bluemindo" msgstr "关于 Bluemindo" #: data/glade/aboutdialog.glade:16 msgid "A really simple but powerful audio player in Python/PyGTK." msgstr "一个使用 Pyhton/PyGTK 的简单但是功能强大的播放器。" #: data/glade/mainwindow.glade:17 msgid "_File" msgstr "文件(_F)" #: data/glade/mainwindow.glade:57 msgid "_Show" msgstr "显示(_S)" #: data/glade/mainwindow.glade:65 msgid "_Lightweight" msgstr "轻量级(_L)" #: data/glade/mainwindow.glade:74 msgid "_Basic" msgstr "基本(_B)" #: data/glade/mainwindow.glade:84 msgid "_Normal" msgstr "正常(_N)" #: data/glade/mainwindow.glade:94 msgid "_Full" msgstr "完全(_F)" #: data/glade/mainwindow.glade:104 msgid "Alb_ums" msgstr "专辑(_U)" #: data/glade/mainwindow.glade:114 msgid "_Playlists" msgstr "播放列表(_P)" #: data/glade/mainwindow.glade:124 msgid "_Webradios" msgstr "网络收音机(_W)" #: data/glade/mainwindow.glade:138 msgid "Fullscreen" msgstr "全屏" #: data/glade/mainwindow.glade:150 msgid "Small display (only player)" msgstr "缩小显示(播放器)" #: data/glade/mainwindow.glade:158 msgid "Display all" msgstr "显示全部" #: data/glade/mainwindow.glade:170 msgid "_Help" msgstr "帮助(_H)" #: data/glade/mainwindow.glade:317 msgid "Add a new playlist" msgstr "新建播放列表" #: data/glade/mainwindow.glade:327 data/glade/mainwindow.glade:1156 msgid "Delete" msgstr "删除" #: data/glade/mainwindow.glade:345 msgid "Import" msgstr "导入" #: data/glade/mainwindow.glade:355 msgid "Export" msgstr "导出" #: data/glade/mainwindow.glade:452 msgid "Group artists" msgstr "组合艺术家" #: data/glade/mainwindow.glade:486 data/glade/mainwindow.glade:1075 msgid "Delete filter" msgstr "删除过滤" #: data/glade/mainwindow.glade:751 msgid "Show song lyrics" msgstr "显示歌词" #: data/glade/mainwindow.glade:1013 msgid "Clean the playlist" msgstr "清空播放列表" #: data/glade/mainwindow.glade:1023 msgid "Shuffle the playlist" msgstr "循环播放" #: data/glade/mainwindow.glade:1034 msgid "Repeat mode" msgstr "重复播放" #: data/glade/mainwindow.glade:1076 msgid "Cancel filter" msgstr "取消过滤" #: data/glade/mainwindow.glade:1098 msgid "Reload lyrics for this song" msgstr "重新加载歌词" #: data/glade/mainwindow.glade:1108 msgid "" "Save lyrics for this song (this feature allows you to edit lyrics or add " "lyrics for a song)" msgstr "保存这首歌曲的歌词(此功能允许您编辑歌词或为某首歌曲添加歌词)" #: data/glade/mainwindow.glade:1146 msgid "Add" msgstr "添加" #: data/glade/mainwindow.glade:1166 msgid "Refresh" msgstr "刷新" #: data/glade/mainwindow.glade:1372 msgid "Webradio" msgstr "网络收音机" #: data/glade/mainwindow.glade:1436 msgid "URL:" msgstr "URL:" #: data/glade/mainwindow.glade:1464 msgid "Add a new radio" msgstr "新建收音机" #~ msgid "Edit a song - Bluemindo" #~ msgstr "编辑歌曲 - Bluemindo" #~ msgid "Audio tags written and database uploaded!" #~ msgstr "音频标签写入并完成数据库更新!" #~ msgid "Information." #~ msgstr "信息。" #~ msgid "Loading" #~ msgstr "正在加载" #~ msgid "Song #%s" #~ msgstr "歌曲 #%s" #~ msgid "No songs found." #~ msgstr "未找到歌曲" #~ msgid "All your %u songs have been imported!" #~ msgstr "您的所有%u首歌曲已被导入!" #~ msgid "Fetching artist %(id)u/%(n)u" #~ msgstr "正在获取艺术家 %(id)u/%(n)u" #~ msgid "Playlists" #~ msgstr "播放列表" #~ msgid "Artists" #~ msgstr "艺术家" #~ msgid "Change Gajim status with the current playing song." #~ msgstr "更改 Gajim 的状态为当前播放的歌曲。" #~ msgid "Send current song to Last.fm profile" #~ msgstr "发送当前歌曲到 Last.fm 的信息" #~ msgid "Active artwork retrieval:" #~ msgstr "激活自动获取封面:" #~ msgid "" #~ "You need to restart Bluemindo to see the changes for this module." #~ msgstr "您需要重启 Bluemindo 以使对模块的更改生效。" #~ msgid "Activated:" #~ msgstr "已激活:" #~ msgid "Status:" #~ msgstr "状态:" #~ msgid "Status message:" #~ msgstr "状态消息:" #~ msgid "" #~ "Current\n" #~ "Online\n" #~ "Free for chat\n" #~ "Away\n" #~ "Extended away\n" #~ "Do not disturb" #~ msgstr "" #~ "当前\n" #~ "在线\n" #~ "可用\n" #~ "离开\n" #~ "暂时离开\n" #~ "请勿打扰" #~ msgid "Song:" #~ msgstr "歌曲:" #~ msgid "More" #~ msgstr "更多" #~ msgid "Load a playlist" #~ msgstr "加载播放列表" #~ msgid "Import a playlist" #~ msgstr "导入播放列表" #~ msgid "Play" #~ msgstr "播放" #~ msgid "New playlist" #~ msgstr "新建播放列表" bluemindo-0.3/locale/bluemindo.pot0000644000175000017500000003312411224222634017174 0ustar xbrightxbright# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-07-06 02:04+0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: src/plugins/notification/__init__.py:37 msgid "Show desktop notification" msgstr "" #: src/plugins/notification/config.py:87 msgid "" "Available tags:\n" "{title}\t\tInsert the title\n" "{artist}\tInsert the artist\n" "{album}\t\tInsert the album\n" "{comment}\tInsert the comment\n" "{genre}\t\tInsert the genre\n" "{year}\t\tInsert the year\n" "{track}\t\tInsert the track number\n" "{length}\tInsert the length" msgstr "" #: src/plugins/jabber/__init__.py:46 msgid "Send PEP notifications" msgstr "" #: src/plugins/audioscrobbler/__init__.py:40 msgid "Send song notifications" msgstr "" #: src/gui/extensionsconfig.py:45 msgid "Preferences" msgstr "" #: src/gui/extensionsconfig.py:192 msgid "This extension is not configurable." msgstr "" #: src/gui/extensionsconfig.py:208 msgid "You should start the plugin before trying to configure it." msgstr "" #: src/modules/explorer/views/normal.py:118 #: src/modules/explorer/views/lightweight.py:49 #: src/modules/explorer/views/basic.py:82 #: src/modules/explorer/views/full.py:111 #: src/modules/explorer/views/albums.py:84 msgid "Opening folder…" msgstr "" #: src/modules/explorer/views/normal.py:147 #: src/modules/explorer/views/lightweight.py:75 #: src/modules/explorer/views/basic.py:109 #: src/modules/explorer/views/full.py:137 #: src/modules/explorer/views/albums.py:110 msgid "Loading music…" msgstr "" #: src/modules/explorer/views/normal.py:155 #: src/modules/explorer/views/basic.py:117 #: src/modules/explorer/views/full.py:145 #: src/modules/explorer/views/albums.py:118 msgid "Loading artists…" msgstr "" #: src/modules/explorer/views/normal.py:174 #, python-format msgid "%(art)d artists (%(sng)d songs)" msgstr "" #: src/modules/explorer/views/normal.py:215 #: src/modules/explorer/views/normal.py:232 #, python-format msgid "%(alb)d albums (%(sng)d songs)" msgstr "" #: src/modules/explorer/views/__init__.py:89 msgid "" "Welcome in Bluemindo!\n" "\n" "First of all, you need to configure the explorer module in order to choose " "your root music directory. This is easy! Go in the File menu, click on " "Preferences and you will be able to configure all Bluemindo's modules." msgstr "" #: src/modules/explorer/views/full.py:295 #, python-format msgid "%d albums" msgstr "" #: src/modules/explorer/views/full.py:296 #, python-format msgid "%d songs" msgstr "" #: src/modules/explorer/views/full.py:301 #, python-format msgid "%d plays" msgstr "" #: src/modules/explorer/views/webradios.py:97 msgid "Bookmarks" msgstr "" #: src/modules/explorer/views/webradios.py:98 msgid "World Wide Web" msgstr "" #: src/modules/explorer/views/webradios.py:107 #: src/modules/explorer/views/webradios.py:205 #: src/modules/explorer/views/webradios.py:281 msgid "Downloading…" msgstr "" #: src/modules/explorer/views/webradios.py:129 msgid "Name" msgstr "" #: src/modules/explorer/views/webradios.py:138 msgid "Listener count" msgstr "" #: src/modules/explorer/views/playlists.py:96 msgid "Top 50 songs" msgstr "" #: src/modules/explorer/views/playlists.py:97 msgid "Top 100 songs" msgstr "" #: src/modules/explorer/views/playlists.py:98 msgid "Top 10 albums" msgstr "" #: src/modules/explorer/views/playlists.py:99 msgid "Random 50 songs" msgstr "" #: src/modules/explorer/views/playlists.py:100 msgid "Random 100 songs" msgstr "" #: src/modules/explorer/views/playlists.py:238 msgid "Delete the playlist?" msgstr "" #: src/modules/explorer/views/playlists.py:247 #, python-format msgid "Do you really want to delete the %s playlist?" msgstr "" #: src/modules/explorer/views/playlists.py:264 #: src/modules/explorer/views/playlists.py:293 msgid "Winamp-like playlists" msgstr "" #: src/modules/explorer/views/playlists.py:266 msgid "Choose a playlist" msgstr "" #: src/modules/explorer/views/playlists.py:295 msgid "Export playlist as…" msgstr "" #: src/modules/explorer/playlist.py:100 src/modules/explorer/playlist.py:169 #: src/modules/explorer/configuration.glade:187 msgid "Title" msgstr "" #: src/modules/explorer/playlist.py:101 src/modules/explorer/playlist.py:170 #: src/modules/explorer/configuration.glade:173 msgid "Artist" msgstr "" #: src/modules/explorer/playlist.py:102 src/modules/explorer/playlist.py:171 #: src/modules/explorer/configuration.glade:143 msgid "Album" msgstr "" #: src/modules/explorer/playlist.py:168 msgid "#" msgstr "" #: src/modules/explorer/playlist.py:172 #: src/modules/explorer/configuration.glade:157 msgid "Genre" msgstr "" #: src/modules/explorer/playlist.py:173 #: src/modules/explorer/configuration.glade:108 msgid "Comment" msgstr "" #: src/modules/explorer/playlist.py:174 #: src/modules/explorer/configuration.glade:94 msgid "Year" msgstr "" #: src/modules/explorer/playlist.py:175 #: src/modules/explorer/configuration.glade:124 msgid "Length" msgstr "" #: src/modules/explorer/playlist.py:417 #, python-format msgid "Added %(nbsongs)u songs (%(totalength)s)." msgstr "" #: src/modules/explorer/musicdb.py:81 #, python-format msgid "Added %d songs" msgstr "" #: src/modules/explorer/musicdb.py:98 msgid "Update statistics…" msgstr "" #: src/modules/explorer/config.py:118 msgid "Lightweight" msgstr "" #: src/modules/explorer/config.py:119 msgid "Basic" msgstr "" #: src/modules/explorer/config.py:120 msgid "Normal" msgstr "" #: src/modules/explorer/config.py:121 msgid "Full" msgstr "" #: src/modules/explorer/config.py:122 msgid "Albums" msgstr "" #: src/modules/explorer/config.py:123 msgid "Playlists" msgstr "" #: src/modules/explorer/config.py:124 src/modules/player/player_gui.py:484 msgid "Webradios" msgstr "" #: src/modules/player/player_gui.py:116 msgid "Change album artwork" msgstr "" #: src/modules/lyrics/__init__.py:119 #, python-format msgid "Downloading lyrics for %(title)s - %(artist)s…" msgstr "" #: src/modules/lyrics/__init__.py:124 #, python-format msgid "No lyrics found for %(title)s - %(artist)s!" msgstr "" #: src/modules/lyrics/lyricsdownloader.py:44 msgid "Lyrics fetching is currently disabled." msgstr "" #: src/bluemindo.py:46 msgid "" "Bluemindo Copyright (C) 2007-2009 Erwan Briand\n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to\n" "redistribute it under certain conditions.\n" "\n" "Usage: bluemindo[.py] [options]\n" "\n" "Available options:\n" "--current\t\tShow the current playing song artist, title and album\n" "--current-cover\t\tShow the path to the cover of the current playing song\n" "--current-lyrics\tShow the lyrics for the current playing song\n" "\n" "--playpause, --play, --pause\tPlay or pause a song\n" "--stop\t\t\tStop a song\n" "--previous\t\tJump to the previous song in playlist\n" "--next\t\t\tJump to the next song in playlist\n" "\n" "--volume-more [STEP]\tIncrease the volume, you can specify a step (0 > 100)\n" "--volume-less [STEP]\tDecrease the volume, you can specify a step (0 > 100)\n" "--volume=VOLUME\t\tSet the volume: 0 > 100\n" "\n" "--reload\t\tReload the songs from your music folder\n" "--quit, --plunge\tQuit Bluemindo" msgstr "" #: src/bluemindo.py:132 msgid "Bluemindo is already started!" msgstr "" #: src/bluemindo.py:133 msgid "" "The socket file located at /tmp/bluemindo already exists, so " "Bluemindo cannot start. If you still want to start Bluemindo, hit \"yes\" " "but part of the software might be broken." msgstr "" #: src/plugins/notification/configuration.glade:51 #: src/modules/explorer/editsongwindow.glade:140 msgid "Title:" msgstr "" #: src/plugins/notification/configuration.glade:95 msgid "Text:" msgstr "" #: src/plugins/notification/configuration.glade:120 msgid "ms" msgstr "" #: src/plugins/notification/configuration.glade:142 msgid "Timeout:" msgstr "" #: src/plugins/jabber/configuration.glade:67 msgid "Jabber identifiant:" msgstr "" #: src/plugins/jabber/configuration.glade:80 #: src/plugins/audioscrobbler/configuration.glade:67 msgid "Password:" msgstr "" #: src/plugins/audioscrobbler/configuration.glade:52 #: src/modules/lyrics/configuration.glade:60 msgid "Server:" msgstr "" #: src/plugins/audioscrobbler/configuration.glade:83 msgid "Username:" msgstr "" #: src/modules/explorer/configuration.glade:40 #: src/modules/explorer/configuration.glade:239 #: src/modules/player/configuration.glade:39 #: src/modules/player/configuration.glade:72 #: src/modules/player/configuration.glade:155 #: src/modules/lyrics/configuration.glade:39 msgid "Yes" msgstr "" #: src/modules/explorer/configuration.glade:58 msgid "Fetch artworks:" msgstr "" #: src/modules/explorer/configuration.glade:74 msgid "View mode:" msgstr "" #: src/modules/explorer/configuration.glade:201 msgid "Track" msgstr "" #: src/modules/explorer/configuration.glade:221 msgid "Show columns:" msgstr "" #: src/modules/explorer/configuration.glade:276 msgid "Folder:" msgstr "" #: src/modules/explorer/configuration.glade:289 msgid "Scan at startup:" msgstr "" #: src/modules/explorer/editsongwindow.glade:91 msgid "Year:" msgstr "" #: src/modules/explorer/editsongwindow.glade:103 msgid "Track:" msgstr "" #: src/modules/explorer/editsongwindow.glade:128 msgid "Genre:" msgstr "" #: src/modules/explorer/editsongwindow.glade:148 msgid "Artist:" msgstr "" #: src/modules/explorer/editsongwindow.glade:160 msgid "Album:" msgstr "" #: src/modules/explorer/editsongwindow.glade:209 msgid "Comment:" msgstr "" #: src/modules/explorer/editsongwindow.glade:242 msgid "Edit a song" msgstr "" #: src/modules/player/configuration.glade:27 msgid "Change window title:" msgstr "" #: src/modules/player/configuration.glade:60 msgid "Show a popup with cover:" msgstr "" #: src/modules/player/configuration.glade:95 msgid "GStreamer playback:" msgstr "" #: src/modules/player/configuration.glade:108 msgid "Default" msgstr "" #: src/modules/player/configuration.glade:118 msgid "Gapless" msgstr "" #: src/modules/player/configuration.glade:145 msgid "Start minimized:" msgstr "" #: src/modules/trayicon/traycontext.glade:40 msgid "Bluemindo" msgstr "" #: src/modules/lyrics/configuration.glade:27 msgid "Active lyrics retrieval:" msgstr "" #: data/glade/playlistmenu.glade:11 msgid "Play now" msgstr "" #: data/glade/playlistmenu.glade:30 msgid "Add to playlist" msgstr "" #: data/glade/playlistmenu.glade:49 msgid "Remove from this playlist" msgstr "" #: data/glade/playlistmenu.glade:68 msgid "Show lyrics" msgstr "" #: data/glade/playlistmenu.glade:81 msgid "Edit song's datas" msgstr "" #: data/glade/playlistmenu.glade:96 msgid "Playlist" msgstr "" #: data/glade/playlistmenu.glade:127 data/glade/mainwindow.glade:1406 msgid "Name:" msgstr "" #: data/glade/playlistmenu.glade:149 msgid "Create a new playlist" msgstr "" #: data/glade/prefswindow.glade:69 msgid "Modules" msgstr "" #: data/glade/prefswindow.glade:128 msgid "Plugins" msgstr "" #: data/glade/aboutdialog.glade:8 msgid "About Bluemindo" msgstr "" #: data/glade/aboutdialog.glade:16 msgid "A really simple but powerful audio player in Python/PyGTK." msgstr "" #: data/glade/mainwindow.glade:17 msgid "_File" msgstr "" #: data/glade/mainwindow.glade:57 msgid "_Show" msgstr "" #: data/glade/mainwindow.glade:65 msgid "_Lightweight" msgstr "" #: data/glade/mainwindow.glade:74 msgid "_Basic" msgstr "" #: data/glade/mainwindow.glade:84 msgid "_Normal" msgstr "" #: data/glade/mainwindow.glade:94 msgid "_Full" msgstr "" #: data/glade/mainwindow.glade:104 msgid "Alb_ums" msgstr "" #: data/glade/mainwindow.glade:114 msgid "_Playlists" msgstr "" #: data/glade/mainwindow.glade:124 msgid "_Webradios" msgstr "" #: data/glade/mainwindow.glade:138 msgid "Fullscreen" msgstr "" #: data/glade/mainwindow.glade:150 msgid "Small display (only player)" msgstr "" #: data/glade/mainwindow.glade:158 msgid "Display all" msgstr "" #: data/glade/mainwindow.glade:170 msgid "_Help" msgstr "" #: data/glade/mainwindow.glade:317 msgid "Add a new playlist" msgstr "" #: data/glade/mainwindow.glade:327 data/glade/mainwindow.glade:1156 msgid "Delete" msgstr "" #: data/glade/mainwindow.glade:345 msgid "Import" msgstr "" #: data/glade/mainwindow.glade:355 msgid "Export" msgstr "" #: data/glade/mainwindow.glade:452 msgid "Group artists" msgstr "" #: data/glade/mainwindow.glade:486 data/glade/mainwindow.glade:1075 msgid "Delete filter" msgstr "" #: data/glade/mainwindow.glade:751 msgid "Show song lyrics" msgstr "" #: data/glade/mainwindow.glade:1013 msgid "Clean the playlist" msgstr "" #: data/glade/mainwindow.glade:1023 msgid "Shuffle the playlist" msgstr "" #: data/glade/mainwindow.glade:1034 msgid "Repeat mode" msgstr "" #: data/glade/mainwindow.glade:1076 msgid "Cancel filter" msgstr "" #: data/glade/mainwindow.glade:1098 msgid "Reload lyrics for this song" msgstr "" #: data/glade/mainwindow.glade:1108 msgid "" "Save lyrics for this song (this feature allows you to edit lyrics or add " "lyrics for a song)" msgstr "" #: data/glade/mainwindow.glade:1146 msgid "Add" msgstr "" #: data/glade/mainwindow.glade:1166 msgid "Refresh" msgstr "" #: data/glade/mainwindow.glade:1372 msgid "Webradio" msgstr "" #: data/glade/mainwindow.glade:1436 msgid "URL:" msgstr "" #: data/glade/mainwindow.glade:1464 msgid "Add a new radio" msgstr "" bluemindo-0.3/locale/pt_BR/LC_MESSAGES/bluemindo.po0000644000175000017500000004172311225155411021606 0ustar xbrightxbright# Portuguese translations for PACKAGE package. # Copyright (C) 2009 THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Bruno Conde , 2009. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-07-06 02:04+0400\n" "PO-Revision-Date: 2009-03-06 07:42-0300\n" "Last-Translator: Bruno Conde \n" "Language-Team: Brazilian Portuguese\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: src/plugins/notification/__init__.py:37 msgid "Show desktop notification" msgstr "Mostra notificação na bandeja do sistema" #: src/plugins/notification/config.py:87 msgid "" "Available tags:\n" "{title}\t\tInsert the title\n" "{artist}\tInsert the artist\n" "{album}\t\tInsert the album\n" "{comment}\tInsert the comment\n" "{genre}\t\tInsert the genre\n" "{year}\t\tInsert the year\n" "{track}\t\tInsert the track number\n" "{length}\tInsert the length" msgstr "" "Tags disponíveis:\n" "{title}\t\tAdicionar título\n" "{artist}\tAdicionar artista\n" "{album}\t\tAdicionar álbum\n" "{comment}\tAdicionar comentário\n" "{genre}\t\tAdicionar gênero\n" "{year}\t\tAdicionar ano\n" "{track}\t\tAdicionar número da faixa\n" "{length}\tAdicionar duração" #: src/plugins/jabber/__init__.py:46 msgid "Send PEP notifications" msgstr "Enviar notificação PEP" #: src/plugins/audioscrobbler/__init__.py:40 #, fuzzy msgid "Send song notifications" msgstr "Enviar notificação PEP" #: src/gui/extensionsconfig.py:45 msgid "Preferences" msgstr "Preferências" #: src/gui/extensionsconfig.py:192 msgid "This extension is not configurable." msgstr "Esta opção não pode ser configurada." #: src/gui/extensionsconfig.py:208 msgid "You should start the plugin before trying to configure it." msgstr "Você deve iniciar o plugin antes de configurar." #: src/modules/explorer/views/normal.py:118 #: src/modules/explorer/views/lightweight.py:49 #: src/modules/explorer/views/basic.py:82 #: src/modules/explorer/views/full.py:111 #: src/modules/explorer/views/albums.py:84 msgid "Opening folder…" msgstr "Abrindo pasta…" #: src/modules/explorer/views/normal.py:147 #: src/modules/explorer/views/lightweight.py:75 #: src/modules/explorer/views/basic.py:109 #: src/modules/explorer/views/full.py:137 #: src/modules/explorer/views/albums.py:110 msgid "Loading music…" msgstr "Carregando músicas…" #: src/modules/explorer/views/normal.py:155 #: src/modules/explorer/views/basic.py:117 #: src/modules/explorer/views/full.py:145 #: src/modules/explorer/views/albums.py:118 msgid "Loading artists…" msgstr "Carregando artistas…" #: src/modules/explorer/views/normal.py:174 #, fuzzy, python-format msgid "%(art)d artists (%(sng)d songs)" msgstr "%d artistas (%d músicas)" #: src/modules/explorer/views/normal.py:215 #: src/modules/explorer/views/normal.py:232 #, fuzzy, python-format msgid "%(alb)d albums (%(sng)d songs)" msgstr "%s álbuns (%s músicas)" #: src/modules/explorer/views/__init__.py:89 msgid "" "Welcome in Bluemindo!\n" "\n" "First of all, you need to configure the explorer module in order to choose " "your root music directory. This is easy! Go in the File menu, click on " "Preferences and you will be able to configure all Bluemindo's modules." msgstr "" "Bem-vindo ao Bluemindo!\n" "\n" "Em primeiro lugar, você precisa configurar a opção Navegador para selecionar " "o seu diretório de música. É muito simples! Vá para o menu Arquivo, clique " "em Preferências e você poderá configurar todos as opções do Bluemindo." #: src/modules/explorer/views/full.py:295 #, python-format msgid "%d albums" msgstr "%d álbuns" #: src/modules/explorer/views/full.py:296 #, python-format msgid "%d songs" msgstr "%d músicas" #: src/modules/explorer/views/full.py:301 #, python-format msgid "%d plays" msgstr "%d vezes tocadas" #: src/modules/explorer/views/webradios.py:97 msgid "Bookmarks" msgstr "Favoritos" #: src/modules/explorer/views/webradios.py:98 msgid "World Wide Web" msgstr "Internet" #: src/modules/explorer/views/webradios.py:107 #: src/modules/explorer/views/webradios.py:205 #: src/modules/explorer/views/webradios.py:281 msgid "Downloading…" msgstr "Fazendo download…" #: src/modules/explorer/views/webradios.py:129 msgid "Name" msgstr "Nome" #: src/modules/explorer/views/webradios.py:138 msgid "Listener count" msgstr "Contagem de músicas" #: src/modules/explorer/views/playlists.py:96 msgid "Top 50 songs" msgstr "Top 50 músicas" #: src/modules/explorer/views/playlists.py:97 msgid "Top 100 songs" msgstr "Top 100 músicas" #: src/modules/explorer/views/playlists.py:98 msgid "Top 10 albums" msgstr "Top 10 álbuns" #: src/modules/explorer/views/playlists.py:99 msgid "Random 50 songs" msgstr "50 músicas aleatórias" #: src/modules/explorer/views/playlists.py:100 msgid "Random 100 songs" msgstr "100 músicas aleatórias" #: src/modules/explorer/views/playlists.py:238 msgid "Delete the playlist?" msgstr "Apagar a lista de reprodução?" #: src/modules/explorer/views/playlists.py:247 #, python-format msgid "Do you really want to delete the %s playlist?" msgstr "Você realmente deseja apagar a %s lista de reprodução?" #: src/modules/explorer/views/playlists.py:264 #: src/modules/explorer/views/playlists.py:293 msgid "Winamp-like playlists" msgstr "Listas de reprodução do Winamp" #: src/modules/explorer/views/playlists.py:266 msgid "Choose a playlist" msgstr "Escolha uma lista de reprodução" #: src/modules/explorer/views/playlists.py:295 msgid "Export playlist as…" msgstr "Exportar uma lista de reprodução como…" #: src/modules/explorer/playlist.py:100 src/modules/explorer/playlist.py:169 #: src/modules/explorer/configuration.glade:187 msgid "Title" msgstr "Título" #: src/modules/explorer/playlist.py:101 src/modules/explorer/playlist.py:170 #: src/modules/explorer/configuration.glade:173 msgid "Artist" msgstr "Artista" #: src/modules/explorer/playlist.py:102 src/modules/explorer/playlist.py:171 #: src/modules/explorer/configuration.glade:143 msgid "Album" msgstr "Álbum" #: src/modules/explorer/playlist.py:168 msgid "#" msgstr "#" #: src/modules/explorer/playlist.py:172 #: src/modules/explorer/configuration.glade:157 msgid "Genre" msgstr "Gênero" #: src/modules/explorer/playlist.py:173 #: src/modules/explorer/configuration.glade:108 msgid "Comment" msgstr "Comentário" #: src/modules/explorer/playlist.py:174 #: src/modules/explorer/configuration.glade:94 msgid "Year" msgstr "Ano" #: src/modules/explorer/playlist.py:175 #: src/modules/explorer/configuration.glade:124 msgid "Length" msgstr "Duração" #: src/modules/explorer/playlist.py:417 #, python-format msgid "Added %(nbsongs)u songs (%(totalength)s)." msgstr "%(nbmúsicas)u músicas adicionadas (%(duração total)s)." #: src/modules/explorer/musicdb.py:81 #, python-format msgid "Added %d songs" msgstr "%d músicas adicionadas" #: src/modules/explorer/musicdb.py:98 msgid "Update statistics…" msgstr "Atualizando estatistícas…" #: src/modules/explorer/config.py:118 msgid "Lightweight" msgstr "Mínimo" #: src/modules/explorer/config.py:119 msgid "Basic" msgstr "Básico" #: src/modules/explorer/config.py:120 msgid "Normal" msgstr "Normal" #: src/modules/explorer/config.py:121 msgid "Full" msgstr "Completo" #: src/modules/explorer/config.py:122 msgid "Albums" msgstr "Álbuns" #: src/modules/explorer/config.py:123 msgid "Playlists" msgstr "Listas de reprodução" #: src/modules/explorer/config.py:124 src/modules/player/player_gui.py:484 msgid "Webradios" msgstr "Rádios pela internet" #: src/modules/player/player_gui.py:116 msgid "Change album artwork" msgstr "Alterar capa de álbum" #: src/modules/lyrics/__init__.py:119 #, fuzzy, python-format msgid "Downloading lyrics for %(title)s - %(artist)s…" msgstr "Fazendo o download de letras de %s - %s…" #: src/modules/lyrics/__init__.py:124 #, fuzzy, python-format msgid "No lyrics found for %(title)s - %(artist)s!" msgstr "Não foram encontradas letras de %s - %s!" #: src/modules/lyrics/lyricsdownloader.py:44 msgid "Lyrics fetching is currently disabled." msgstr "A pesquisa de letras está desabilitada" #: src/bluemindo.py:46 msgid "" "Bluemindo Copyright (C) 2007-2009 Erwan Briand\n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to\n" "redistribute it under certain conditions.\n" "\n" "Usage: bluemindo[.py] [options]\n" "\n" "Available options:\n" "--current\t\tShow the current playing song artist, title and album\n" "--current-cover\t\tShow the path to the cover of the current playing song\n" "--current-lyrics\tShow the lyrics for the current playing song\n" "\n" "--playpause, --play, --pause\tPlay or pause a song\n" "--stop\t\t\tStop a song\n" "--previous\t\tJump to the previous song in playlist\n" "--next\t\t\tJump to the next song in playlist\n" "\n" "--volume-more [STEP]\tIncrease the volume, you can specify a step (0 > 100)\n" "--volume-less [STEP]\tDecrease the volume, you can specify a step (0 > 100)\n" "--volume=VOLUME\t\tSet the volume: 0 > 100\n" "\n" "--reload\t\tReload the songs from your music folder\n" "--quit, --plunge\tQuit Bluemindo" msgstr "" #: src/bluemindo.py:132 msgid "Bluemindo is already started!" msgstr "" #: src/bluemindo.py:133 msgid "" "The socket file located at /tmp/bluemindo already exists, so " "Bluemindo cannot start. If you still want to start Bluemindo, hit \"yes\" " "but part of the software might be broken." msgstr "" #: src/plugins/notification/configuration.glade:51 #: src/modules/explorer/editsongwindow.glade:140 msgid "Title:" msgstr "Título:" #: src/plugins/notification/configuration.glade:95 msgid "Text:" msgstr "Texto:" #: src/plugins/notification/configuration.glade:120 msgid "ms" msgstr "ms" #: src/plugins/notification/configuration.glade:142 msgid "Timeout:" msgstr "Timeout:" #: src/plugins/jabber/configuration.glade:67 msgid "Jabber identifiant:" msgstr "Identificação Jabber:" #: src/plugins/jabber/configuration.glade:80 #: src/plugins/audioscrobbler/configuration.glade:67 msgid "Password:" msgstr "Senha:" #: src/plugins/audioscrobbler/configuration.glade:52 #: src/modules/lyrics/configuration.glade:60 msgid "Server:" msgstr "Servidor:" #: src/plugins/audioscrobbler/configuration.glade:83 msgid "Username:" msgstr "Usuário:" #: src/modules/explorer/configuration.glade:40 #: src/modules/explorer/configuration.glade:239 #: src/modules/player/configuration.glade:39 #: src/modules/player/configuration.glade:72 #: src/modules/player/configuration.glade:155 #: src/modules/lyrics/configuration.glade:39 msgid "Yes" msgstr "Sim" #: src/modules/explorer/configuration.glade:58 msgid "Fetch artworks:" msgstr "Pesquisar capas de ábluns:" #: src/modules/explorer/configuration.glade:74 msgid "View mode:" msgstr "Modo de visualização:" #: src/modules/explorer/configuration.glade:201 msgid "Track" msgstr "Faixa" #: src/modules/explorer/configuration.glade:221 msgid "Show columns:" msgstr "Exibir colunas:" #: src/modules/explorer/configuration.glade:276 msgid "Folder:" msgstr "Pasta" #: src/modules/explorer/configuration.glade:289 msgid "Scan at startup:" msgstr "Atualizar músicas no iníco" #: src/modules/explorer/editsongwindow.glade:91 msgid "Year:" msgstr "Ano:" #: src/modules/explorer/editsongwindow.glade:103 msgid "Track:" msgstr "Faixa:" #: src/modules/explorer/editsongwindow.glade:128 msgid "Genre:" msgstr "Genêro:" #: src/modules/explorer/editsongwindow.glade:148 msgid "Artist:" msgstr "Artista:" #: src/modules/explorer/editsongwindow.glade:160 msgid "Album:" msgstr "Álbum:" #: src/modules/explorer/editsongwindow.glade:209 msgid "Comment:" msgstr "Comentário:" #: src/modules/explorer/editsongwindow.glade:242 msgid "Edit a song" msgstr "Editar música" #: src/modules/player/configuration.glade:27 msgid "Change window title:" msgstr "Exibir música na barra de título da janela: " #: src/modules/player/configuration.glade:60 msgid "Show a popup with cover:" msgstr "Exibir popup da capa:" #: src/modules/player/configuration.glade:95 msgid "GStreamer playback:" msgstr "Reproudução do GStreamer:" #: src/modules/player/configuration.glade:108 msgid "Default" msgstr "Padrão" #: src/modules/player/configuration.glade:118 msgid "Gapless" msgstr "Sem intervalo" #: src/modules/player/configuration.glade:145 msgid "Start minimized:" msgstr "Iniciar minimizado:" #: src/modules/trayicon/traycontext.glade:40 msgid "Bluemindo" msgstr "Bluemindo" #: src/modules/lyrics/configuration.glade:27 msgid "Active lyrics retrieval:" msgstr "Ativar pesquisa de letras:" #: data/glade/playlistmenu.glade:11 msgid "Play now" msgstr "Reproduzir agora" #: data/glade/playlistmenu.glade:30 msgid "Add to playlist" msgstr "Adicionar para lista de reprodução" #: data/glade/playlistmenu.glade:49 msgid "Remove from this playlist" msgstr "Remover da lista de reprodução" #: data/glade/playlistmenu.glade:68 msgid "Show lyrics" msgstr "Exibir letras" #: data/glade/playlistmenu.glade:81 msgid "Edit song's datas" msgstr "Editar tag das músicas" #: data/glade/playlistmenu.glade:96 msgid "Playlist" msgstr "Lista de reprodução" #: data/glade/playlistmenu.glade:127 data/glade/mainwindow.glade:1406 msgid "Name:" msgstr "Nome:" #: data/glade/playlistmenu.glade:149 msgid "Create a new playlist" msgstr "Criar nova lista de reprodução" #: data/glade/prefswindow.glade:69 msgid "Modules" msgstr "Opções" #: data/glade/prefswindow.glade:128 msgid "Plugins" msgstr "Plugins" #: data/glade/aboutdialog.glade:8 msgid "About Bluemindo" msgstr "Sobre Bluemindo" #: data/glade/aboutdialog.glade:16 msgid "A really simple but powerful audio player in Python/PyGTK." msgstr "" "Um simples, mas poderoso, gerenciador de músicas desenvolvido em Python/" "PyGTK." #: data/glade/mainwindow.glade:17 msgid "_File" msgstr "_Arquivo" #: data/glade/mainwindow.glade:57 msgid "_Show" msgstr "_Exibir" #: data/glade/mainwindow.glade:65 #, fuzzy msgid "_Lightweight" msgstr "Mínimo" #: data/glade/mainwindow.glade:74 #, fuzzy msgid "_Basic" msgstr "Básico" #: data/glade/mainwindow.glade:84 #, fuzzy msgid "_Normal" msgstr "Normal" #: data/glade/mainwindow.glade:94 #, fuzzy msgid "_Full" msgstr "Completo" #: data/glade/mainwindow.glade:104 #, fuzzy msgid "Alb_ums" msgstr "Álbuns" #: data/glade/mainwindow.glade:114 #, fuzzy msgid "_Playlists" msgstr "Listas de reprodução" #: data/glade/mainwindow.glade:124 #, fuzzy msgid "_Webradios" msgstr "Rádios pela internet" #: data/glade/mainwindow.glade:138 msgid "Fullscreen" msgstr "Tela cheia" #: data/glade/mainwindow.glade:150 msgid "Small display (only player)" msgstr "Modo compacto" #: data/glade/mainwindow.glade:158 msgid "Display all" msgstr "Exibir todos" #: data/glade/mainwindow.glade:170 msgid "_Help" msgstr "_Ajuda" #: data/glade/mainwindow.glade:317 msgid "Add a new playlist" msgstr "Criar nova lista de reprodução" #: data/glade/mainwindow.glade:327 data/glade/mainwindow.glade:1156 msgid "Delete" msgstr "Apagar" #: data/glade/mainwindow.glade:345 msgid "Import" msgstr "Importar" #: data/glade/mainwindow.glade:355 msgid "Export" msgstr "Exportar" #: data/glade/mainwindow.glade:452 msgid "Group artists" msgstr "Agrupar artistas" #: data/glade/mainwindow.glade:486 data/glade/mainwindow.glade:1075 msgid "Delete filter" msgstr "Deletar filtro" #: data/glade/mainwindow.glade:751 msgid "Show song lyrics" msgstr "Exibir letras" #: data/glade/mainwindow.glade:1013 msgid "Clean the playlist" msgstr "Limpar lista de reprodução" #: data/glade/mainwindow.glade:1023 msgid "Shuffle the playlist" msgstr "Modo aleatório" #: data/glade/mainwindow.glade:1034 msgid "Repeat mode" msgstr "Modo de repetição" #: data/glade/mainwindow.glade:1076 msgid "Cancel filter" msgstr "Cancelar filtro" #: data/glade/mainwindow.glade:1098 msgid "Reload lyrics for this song" msgstr "Recarregar letras para esta música" #: data/glade/mainwindow.glade:1108 msgid "" "Save lyrics for this song (this feature allows you to edit lyrics or add " "lyrics for a song)" msgstr "" "Salvar letra (esta opção permite editar ou adicionar letras para uma música)" #: data/glade/mainwindow.glade:1146 msgid "Add" msgstr "Adicionar" #: data/glade/mainwindow.glade:1166 msgid "Refresh" msgstr "Recarregar" #: data/glade/mainwindow.glade:1372 msgid "Webradio" msgstr "Rádio de internet" #: data/glade/mainwindow.glade:1436 msgid "URL:" msgstr "Link:" #: data/glade/mainwindow.glade:1464 msgid "Add a new radio" msgstr "Adicionar nova rádio" #~ msgid "Send song notification to Last.fm" #~ msgstr "Enviar música para Last.fm" #~ msgid "gtk-refresh" #~ msgstr "gtk-refresh" #~ msgid "label" #~ msgstr "label" #~ msgid "frame3" #~ msgstr "frame3" #~ msgid "gtk-yes" #~ msgstr "gtk-yes" #~ msgid "gtk-no" #~ msgstr "gtk-no" bluemindo-0.3/locale/fr/LC_MESSAGES/bluemindo.po0000644000175000017500000005002011225217611021176 0ustar xbrightxbright# French translations for Bluemindo package. # Copyright (C) 2008 Erwan Briand # This file is distributed under the same license as the Bluemindo package. # xbright , 2008. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-07-06 01:52+0400\n" "PO-Revision-Date: 2008-05-27 02:22+0400\n" "Last-Translator: xbright \n" "Language-Team: French\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: src/plugins/notification/__init__.py:37 msgid "Show desktop notification" msgstr "Afficher les notifications de bureau" #: src/plugins/notification/config.py:87 msgid "" "Available tags:\n" "{title}\t\tInsert the title\n" "{artist}\tInsert the artist\n" "{album}\t\tInsert the album\n" "{comment}\tInsert the comment\n" "{genre}\t\tInsert the genre\n" "{year}\t\tInsert the year\n" "{track}\t\tInsert the track number\n" "{length}\tInsert the length" msgstr "" "Balises disponibles :\n" "{title}\t\tInsère le titre\n" "{artist}\tInsère l'artiste\n" "{album}\t\tInsère l'album\n" "{comment}\tInsère le commentaire\n" "{genre}\t\tInsère le genre\n" "{year}\t\tInsère l'année\n" "{track}\t\tInsère le numéro de la piste\n" "{length}\tInsère la durée" #: src/plugins/jabber/__init__.py:46 msgid "Send PEP notifications" msgstr "Envoyer des notifications PEP" #: src/plugins/audioscrobbler/__init__.py:40 msgid "Send song notifications" msgstr "Envoyer les notifications d'écoute" #: src/gui/extensionsconfig.py:45 msgid "Preferences" msgstr "Préférences" #: src/gui/extensionsconfig.py:192 msgid "This extension is not configurable." msgstr "Cette extension n'est pas configurable." #: src/gui/extensionsconfig.py:208 msgid "You should start the plugin before trying to configure it." msgstr "Vous devriez lancer le plugin avant de tenter de le configurer." #: src/modules/explorer/views/normal.py:118 #: src/modules/explorer/views/lightweight.py:49 #: src/modules/explorer/views/basic.py:82 #: src/modules/explorer/views/full.py:111 #: src/modules/explorer/views/albums.py:84 msgid "Opening folder…" msgstr "Ouverture du dossier…" #: src/modules/explorer/views/normal.py:147 #: src/modules/explorer/views/lightweight.py:75 #: src/modules/explorer/views/basic.py:109 #: src/modules/explorer/views/full.py:137 #: src/modules/explorer/views/albums.py:110 msgid "Loading music…" msgstr "Chargement de la musique…" #: src/modules/explorer/views/normal.py:155 #: src/modules/explorer/views/basic.py:117 #: src/modules/explorer/views/full.py:145 #: src/modules/explorer/views/albums.py:118 msgid "Loading artists…" msgstr "Chargement des artistes…" #: src/modules/explorer/views/normal.py:174 #, python-format msgid "%(art)d artists (%(sng)d songs)" msgstr "%(art)d artistes (%(sng)d chansons)" #: src/modules/explorer/views/normal.py:215 #: src/modules/explorer/views/normal.py:232 #, python-format msgid "%(alb)d albums (%(sng)d songs)" msgstr "%(alb)d albums (%(sng)d chansons)" #: src/modules/explorer/views/__init__.py:89 msgid "" "Welcome in Bluemindo!\n" "\n" "First of all, you need to configure the explorer module in order to choose " "your root music directory. This is easy! Go in the File menu, click on " "Preferences and you will be able to configure all Bluemindo's modules." msgstr "" "Bienvenue dans Bluemindo!\n" "\n" "Premièrement, vous devez configurer le module d'exploration afin de choisir " "votre dossier musical racine. C'est facile ! Allez dans le menu Fichier, " "cliquez sur Préférences et vous serez prêt à configurer tous les modules de " "Bluemindo." #: src/modules/explorer/views/full.py:295 #, python-format msgid "%d albums" msgstr "%d albums" #: src/modules/explorer/views/full.py:296 #, python-format msgid "%d songs" msgstr "%d chansons" #: src/modules/explorer/views/full.py:301 #, python-format msgid "%d plays" msgstr "%d écoutes" #: src/modules/explorer/views/webradios.py:97 msgid "Bookmarks" msgstr "Favoris" #: src/modules/explorer/views/webradios.py:98 msgid "World Wide Web" msgstr "World Wide Web" #: src/modules/explorer/views/webradios.py:107 #: src/modules/explorer/views/webradios.py:205 #: src/modules/explorer/views/webradios.py:281 msgid "Downloading…" msgstr "Téléchargement…" #: src/modules/explorer/views/webradios.py:129 msgid "Name" msgstr "Nom" #: src/modules/explorer/views/webradios.py:138 msgid "Listener count" msgstr "Nombre d'auditeurs" #: src/modules/explorer/views/playlists.py:96 msgid "Top 50 songs" msgstr "Top 50 chansons" #: src/modules/explorer/views/playlists.py:97 msgid "Top 100 songs" msgstr "Top 100 chansons" #: src/modules/explorer/views/playlists.py:98 msgid "Top 10 albums" msgstr "Top 10 albums" #: src/modules/explorer/views/playlists.py:99 msgid "Random 50 songs" msgstr "50 chansons aléatoires" #: src/modules/explorer/views/playlists.py:100 msgid "Random 100 songs" msgstr "100 chansons aléatoires" #: src/modules/explorer/views/playlists.py:238 msgid "Delete the playlist?" msgstr "Effacer la liste de lecture ?" #: src/modules/explorer/views/playlists.py:247 #, python-format msgid "Do you really want to delete the %s playlist?" msgstr "Voulez vous vraiment supprimer la liste de lecture %s ?" #: src/modules/explorer/views/playlists.py:264 #: src/modules/explorer/views/playlists.py:293 msgid "Winamp-like playlists" msgstr "Listes de lecture de type Winamp" #: src/modules/explorer/views/playlists.py:266 msgid "Choose a playlist" msgstr "Choisir une liste de lecture" #: src/modules/explorer/views/playlists.py:295 msgid "Export playlist as…" msgstr "Exporter une liste de lecture comme…" #: src/modules/explorer/playlist.py:100 src/modules/explorer/playlist.py:169 #: src/modules/explorer/configuration.glade:187 msgid "Title" msgstr "Titre" #: src/modules/explorer/playlist.py:101 src/modules/explorer/playlist.py:170 #: src/modules/explorer/configuration.glade:173 msgid "Artist" msgstr "Artiste" #: src/modules/explorer/playlist.py:102 src/modules/explorer/playlist.py:171 #: src/modules/explorer/configuration.glade:143 msgid "Album" msgstr "Album" #: src/modules/explorer/playlist.py:168 msgid "#" msgstr "#" #: src/modules/explorer/playlist.py:172 #: src/modules/explorer/configuration.glade:157 msgid "Genre" msgstr "Genre" #: src/modules/explorer/playlist.py:173 #: src/modules/explorer/configuration.glade:108 msgid "Comment" msgstr "Commentaire" #: src/modules/explorer/playlist.py:174 #: src/modules/explorer/configuration.glade:94 msgid "Year" msgstr "Année" #: src/modules/explorer/playlist.py:175 #: src/modules/explorer/configuration.glade:124 msgid "Length" msgstr "Durée" #: src/modules/explorer/playlist.py:417 #, python-format msgid "Added %(nbsongs)u songs (%(totalength)s)." msgstr "%(nbsongs)u chansons ajoutées (%(totalength)s)." #: src/modules/explorer/musicdb.py:81 #, python-format msgid "Added %d songs" msgstr "%d chansons ajoutées" #: src/modules/explorer/musicdb.py:98 msgid "Update statistics…" msgstr "Mise à jour des statistiques…" #: src/modules/explorer/config.py:118 msgid "Lightweight" msgstr "Léger" #: src/modules/explorer/config.py:119 msgid "Basic" msgstr "Basique" #: src/modules/explorer/config.py:120 msgid "Normal" msgstr "Normal" #: src/modules/explorer/config.py:121 msgid "Full" msgstr "Complet" #: src/modules/explorer/config.py:122 msgid "Albums" msgstr "Albums" #: src/modules/explorer/config.py:123 msgid "Playlists" msgstr "Listes de lecture" #: src/modules/explorer/config.py:124 src/modules/player/player_gui.py:484 msgid "Webradios" msgstr "Webradios" #: src/modules/player/player_gui.py:116 msgid "Change album artwork" msgstr "Changer la pochette de l'album" #: src/modules/lyrics/__init__.py:119 #, python-format msgid "Downloading lyrics for %(title)s - %(artist)s…" msgstr "Téléchargement des paroles pour %(title)s - %(artist)s…" #: src/modules/lyrics/__init__.py:124 #, python-format msgid "No lyrics found for %(title)s - %(artist)s!" msgstr "Paroles non-trouvées pour %(title)s - %(artist)s !" #: src/modules/lyrics/lyricsdownloader.py:44 msgid "Lyrics fetching is currently disabled." msgstr "Récupération de paroles désactivée pour le moment." #: src/bluemindo.py:46 msgid "" "Bluemindo Copyright (C) 2007-2009 Erwan Briand\n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to\n" "redistribute it under certain conditions.\n" "\n" "Usage: bluemindo[.py] [options]\n" "\n" "Available options:\n" "--current\t\tShow the current playing song artist, title and album\n" "--current-cover\t\tShow the path to the cover of the current playing song\n" "--current-lyrics\tShow the lyrics for the current playing song\n" "\n" "--playpause, --play, --pause\tPlay or pause a song\n" "--stop\t\t\tStop a song\n" "--previous\t\tJump to the previous song in playlist\n" "--next\t\t\tJump to the next song in playlist\n" "\n" "--volume-more [STEP]\tIncrease the volume, you can specify a step (0 > 100)\n" "--volume-less [STEP]\tDecrease the volume, you can specify a step (0 > 100)\n" "--volume=VOLUME\t\tSet the volume: 0 > 100\n" "\n" "--reload\t\tReload the songs from your music folder\n" "--quit, --plunge\tQuit Bluemindo" msgstr "" "Bluemindo Copyright (C) 2007-2009 Erwan Briand\n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to\n" "redistribute it under certain conditions.\n" "\n" "Utilisation : bluemindo[.py] [options]\n" "\n" "Options disponibles :\n" "--current\t\tAfficher l'artiste, le titre et l'album de la chanson en cours\n" "--current-cover\t\tAfficher le chemin vers la couverture de la chanson en " "cours\n" "--current-lyrics\tAfficher les paroles de la chanson en cours\n" "\n" "--playpause, --play, --pause\tLire ou mettre en pause une chanson\n" "--stop\t\t\tStopper une chanson\n" "--previous\t\tAller à la précédente chanson dans la liste de lecture\n" "--next\t\t\tAller à la suivante chanson dans la liste de lecture\n" "\n" "--volume-more [STEP]\tAugmenter le volume, vous pouvez spécifier un pas (0 " "> 100)\n" "--volume-less [STEP]\tDiminuer le volume, vous pouvez spécifier un pas (0 " "> 100)\n" "--volume=VOLUME\t\tRégler le volume : 0 > 100\n" "\n" "--reload\t\tRecharger les chansons de votre dossier musical\n" "--quit, --plunge\tQuitter Bluemindo" #: src/bluemindo.py:132 msgid "Bluemindo is already started!" msgstr "Bluemindo est déjà démarré !" #: src/bluemindo.py:133 msgid "" "The socket file located at /tmp/bluemindo already exists, so " "Bluemindo cannot start. If you still want to start Bluemindo, hit \"yes\" " "but part of the software might be broken." msgstr "" "Le fichier de socket placé dans /tmp/bluemindo existe déjà, donc " "Bluemindo ne peut pas démarrer. Si vous voulez toujours lancer Bluemindo, " "cliquez \"oui\" mais des parties du logiciel pourraient être cassées." #: src/plugins/notification/configuration.glade:51 #: src/modules/explorer/editsongwindow.glade:140 msgid "Title:" msgstr "Titre :" #: src/plugins/notification/configuration.glade:95 msgid "Text:" msgstr "Texte :" #: src/plugins/notification/configuration.glade:120 msgid "ms" msgstr "ms" #: src/plugins/notification/configuration.glade:142 msgid "Timeout:" msgstr "Minuteur :" #: src/plugins/jabber/configuration.glade:67 msgid "Jabber identifiant:" msgstr "Identifiant Jabber :" #: src/plugins/jabber/configuration.glade:80 #: src/plugins/audioscrobbler/configuration.glade:67 msgid "Password:" msgstr "Mot de passe :" #: src/plugins/audioscrobbler/configuration.glade:52 #: src/modules/lyrics/configuration.glade:60 msgid "Server:" msgstr "Serveur :" #: src/plugins/audioscrobbler/configuration.glade:83 msgid "Username:" msgstr "Nom d'utilisateur :" #: src/modules/explorer/configuration.glade:40 #: src/modules/explorer/configuration.glade:239 #: src/modules/player/configuration.glade:39 #: src/modules/player/configuration.glade:72 #: src/modules/player/configuration.glade:155 #: src/modules/lyrics/configuration.glade:39 msgid "Yes" msgstr "Oui" #: src/modules/explorer/configuration.glade:58 msgid "Fetch artworks:" msgstr "Récupérer les images :" #: src/modules/explorer/configuration.glade:74 msgid "View mode:" msgstr "Mode de vue :" #: src/modules/explorer/configuration.glade:201 msgid "Track" msgstr "Piste" #: src/modules/explorer/configuration.glade:221 msgid "Show columns:" msgstr "Colonnes affichées :" #: src/modules/explorer/configuration.glade:276 msgid "Folder:" msgstr "Dossier :" #: src/modules/explorer/configuration.glade:289 msgid "Scan at startup:" msgstr "Scanner au démarage :" #: src/modules/explorer/editsongwindow.glade:91 msgid "Year:" msgstr "Année :" #: src/modules/explorer/editsongwindow.glade:103 msgid "Track:" msgstr "Piste :" #: src/modules/explorer/editsongwindow.glade:128 msgid "Genre:" msgstr "Genre :" #: src/modules/explorer/editsongwindow.glade:148 msgid "Artist:" msgstr "Artiste :" #: src/modules/explorer/editsongwindow.glade:160 msgid "Album:" msgstr "Album :" #: src/modules/explorer/editsongwindow.glade:209 msgid "Comment:" msgstr "Commentaire :" #: src/modules/explorer/editsongwindow.glade:242 msgid "Edit a song" msgstr "Éditer une chanson" #: src/modules/player/configuration.glade:27 msgid "Change window title:" msgstr "Changer le titre de la fenêtre :" #: src/modules/player/configuration.glade:60 msgid "Show a popup with cover:" msgstr "Popup avec la couverture :" #: src/modules/player/configuration.glade:95 msgid "GStreamer playback:" msgstr "Lecture GStreamer :" #: src/modules/player/configuration.glade:108 msgid "Default" msgstr "Défaut" #: src/modules/player/configuration.glade:118 msgid "Gapless" msgstr "Sans pause" #: src/modules/player/configuration.glade:145 msgid "Start minimized:" msgstr "Démarrer minimisé :" #: src/modules/trayicon/traycontext.glade:40 msgid "Bluemindo" msgstr "Bluemindo" #: src/modules/lyrics/configuration.glade:27 msgid "Active lyrics retrieval:" msgstr "Téléchargement des paroles :" #: data/glade/playlistmenu.glade:11 msgid "Play now" msgstr "Lire maintenant" #: data/glade/playlistmenu.glade:30 msgid "Add to playlist" msgstr "Ajouter à la liste de lecture" #: data/glade/playlistmenu.glade:49 msgid "Remove from this playlist" msgstr "Supprimer de la liste de lecture" #: data/glade/playlistmenu.glade:68 msgid "Show lyrics" msgstr "Afficher les paroles" #: data/glade/playlistmenu.glade:81 msgid "Edit song's datas" msgstr "Editer les informations de la chanson" #: data/glade/playlistmenu.glade:96 msgid "Playlist" msgstr "Liste de lecture" #: data/glade/playlistmenu.glade:127 data/glade/mainwindow.glade:1406 msgid "Name:" msgstr "Nom :" #: data/glade/playlistmenu.glade:149 msgid "Create a new playlist" msgstr "Créer une nouvelle liste de lecture" #: data/glade/prefswindow.glade:69 msgid "Modules" msgstr "Modules" #: data/glade/prefswindow.glade:128 msgid "Plugins" msgstr "Plugins" #: data/glade/aboutdialog.glade:8 msgid "About Bluemindo" msgstr "À propos de Bluemindo" #: data/glade/aboutdialog.glade:16 msgid "A really simple but powerful audio player in Python/PyGTK." msgstr "Un lecteur audio simple mais puissant en Python/PyGTK." #: data/glade/mainwindow.glade:17 msgid "_File" msgstr "_Fichier" #: data/glade/mainwindow.glade:57 msgid "_Show" msgstr "Aff_ichage" #: data/glade/mainwindow.glade:65 msgid "_Lightweight" msgstr "_Léger" #: data/glade/mainwindow.glade:74 msgid "_Basic" msgstr "_Basique" #: data/glade/mainwindow.glade:84 msgid "_Normal" msgstr "_Normal" #: data/glade/mainwindow.glade:94 msgid "_Full" msgstr "_Complet" #: data/glade/mainwindow.glade:104 msgid "Alb_ums" msgstr "Alb_ums" #: data/glade/mainwindow.glade:114 msgid "_Playlists" msgstr "_Listes de lecture" #: data/glade/mainwindow.glade:124 msgid "_Webradios" msgstr "_Webradios" #: data/glade/mainwindow.glade:138 msgid "Fullscreen" msgstr "Plein écran" #: data/glade/mainwindow.glade:150 msgid "Small display (only player)" msgstr "Affichage discret (lecteur uniquement)" #: data/glade/mainwindow.glade:158 msgid "Display all" msgstr "Tout afficher" #: data/glade/mainwindow.glade:170 msgid "_Help" msgstr "_Aide" #: data/glade/mainwindow.glade:317 msgid "Add a new playlist" msgstr "Ajouter une nouvelle liste de lecture" #: data/glade/mainwindow.glade:327 data/glade/mainwindow.glade:1156 msgid "Delete" msgstr "Supprimer" #: data/glade/mainwindow.glade:345 msgid "Import" msgstr "Importer" #: data/glade/mainwindow.glade:355 msgid "Export" msgstr "Exporter" #: data/glade/mainwindow.glade:452 msgid "Group artists" msgstr "Grouper les artistes" #: data/glade/mainwindow.glade:486 data/glade/mainwindow.glade:1075 msgid "Delete filter" msgstr "Supprimer le filtre" #: data/glade/mainwindow.glade:751 msgid "Show song lyrics" msgstr "Afficher les paroles de la chanson" #: data/glade/mainwindow.glade:1013 msgid "Clean the playlist" msgstr "Effacer la liste de lecture" #: data/glade/mainwindow.glade:1023 msgid "Shuffle the playlist" msgstr "Mélanger la liste de lecture" #: data/glade/mainwindow.glade:1034 msgid "Repeat mode" msgstr "Mode de répétition" #: data/glade/mainwindow.glade:1076 msgid "Cancel filter" msgstr "Annuler le filtrage" #: data/glade/mainwindow.glade:1098 msgid "Reload lyrics for this song" msgstr "Recharger les paroles pour cette chanson" #: data/glade/mainwindow.glade:1108 msgid "" "Save lyrics for this song (this feature allows you to edit lyrics or add " "lyrics for a song)" msgstr "" "Enregistrer les paroles pour cette chanson (cette fonctionnalité vous permet " "d'éditer les paroles ou d'ajouter des paroles pour une chanson)" #: data/glade/mainwindow.glade:1146 msgid "Add" msgstr "Ajouter" #: data/glade/mainwindow.glade:1166 msgid "Refresh" msgstr "Raffraîchir" #: data/glade/mainwindow.glade:1372 msgid "Webradio" msgstr "Webradio" #: data/glade/mainwindow.glade:1436 msgid "URL:" msgstr "URL :" #: data/glade/mainwindow.glade:1464 msgid "Add a new radio" msgstr "Ajouter une nouvelle radio" #~ msgid "Edit a song - Bluemindo" #~ msgstr "Éditer une chanson - Bluemindo" #~ msgid "Audio tags written and database uploaded!" #~ msgstr "Informations audio écrites et base de données mise à jour !" #~ msgid "Information." #~ msgstr "Information." #~ msgid "Loading" #~ msgstr "Chargement" #~ msgid "Song #%s" #~ msgstr "Chanson #%s" #~ msgid "No songs found." #~ msgstr "Pas de chanson trouvée." #~ msgid "All your %u songs have been imported!" #~ msgstr "Toutes vos %u chansons ont été importées !" #~ msgid "Fetching artist %(id)u/%(n)u" #~ msgstr "Récupération de l'artiste %(id)u/%(n)u" #~ msgid "Playlists" #~ msgstr "Listes de lecture" #~ msgid "Artists" #~ msgstr "Artistes" #~ msgid "Change Gajim status with the current playing song." #~ msgstr "Changer le status de gajim avec la chanson en cours d'écoute." #~ msgid "Send current song to Last.fm profile" #~ msgstr "Envoyer la chanson en cours vers votre profil Last.fm" #~ msgid "Active artwork retrieval:" #~ msgstr "Téléchargement des pochettes :" #~ msgid "" #~ "You need to restart Bluemindo to see the changes for this module." #~ msgstr "" #~ "Vous devez relancer Bluemindo pour voir les changements de ce module." #~ msgid "Activated:" #~ msgstr "Activé" #~ msgid "Status:" #~ msgstr "Statuts :" #~ msgid "Status message:" #~ msgstr "Message de statut :" #~ msgid "" #~ "Current\n" #~ "Online\n" #~ "Free for chat\n" #~ "Away\n" #~ "Extended away\n" #~ "Do not disturb" #~ msgstr "" #~ "En cours\n" #~ "En ligne\n" #~ "Disponible pour discuter\n" #~ "Absent\n" #~ "Non disponible\n" #~ "Occupé" #~ msgid "Song:" #~ msgstr "Chanson :" #~ msgid "More" #~ msgstr "Plus" #~ msgid "Load a playlist" #~ msgstr "Charger une liste de lecture" #~ msgid "Import a playlist" #~ msgstr "Importer une liste de lecture" #~ msgid "Play" #~ msgstr "Lire" #~ msgid "New playlist" #~ msgstr "Nouvelle liste de lecture" bluemindo-0.3/locale/sv/LC_MESSAGES/bluemindo.po0000644000175000017500000004463011225155411021230 0ustar xbrightxbrightmsgid "" msgstr "" "Project-Id-Version: bluemindo-0.2.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-07-06 02:04+0400\n" "PO-Revision-Date: 2009-01-22 11:07+0100\n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Poedit-Language: Swedish\n" "X-Poedit-Country: SWEDEN\n" #: src/plugins/notification/__init__.py:37 #, fuzzy msgid "Show desktop notification" msgstr "Visar skrivbordsnotifiering" #: src/plugins/notification/config.py:87 #, fuzzy msgid "" "Available tags:\n" "{title}\t\tInsert the title\n" "{artist}\tInsert the artist\n" "{album}\t\tInsert the album\n" "{comment}\tInsert the comment\n" "{genre}\t\tInsert the genre\n" "{year}\t\tInsert the year\n" "{track}\t\tInsert the track number\n" "{length}\tInsert the length" msgstr "" "Tillgängliga taggar:\n" " {titel} - Infoga titeln\n" " {artist} - Infoga artisten\n" " {album} - Infoga albumet\n" " {spår} - Infoga spårnummret\n" " {speltid} - Infoga speltiden\n" " {genre} - Infoga genren" #: src/plugins/jabber/__init__.py:46 #, fuzzy msgid "Send PEP notifications" msgstr "Skicka PEP-notifieringar via ditt Jabber-konto." #: src/plugins/audioscrobbler/__init__.py:40 #, fuzzy msgid "Send song notifications" msgstr "Visar skrivbordsnotifiering" #: src/gui/extensionsconfig.py:45 msgid "Preferences" msgstr "Inställningar" #: src/gui/extensionsconfig.py:192 msgid "This extension is not configurable." msgstr "" #: src/gui/extensionsconfig.py:208 msgid "You should start the plugin before trying to configure it." msgstr "" #: src/modules/explorer/views/normal.py:118 #: src/modules/explorer/views/lightweight.py:49 #: src/modules/explorer/views/basic.py:82 #: src/modules/explorer/views/full.py:111 #: src/modules/explorer/views/albums.py:84 msgid "Opening folder…" msgstr "" #: src/modules/explorer/views/normal.py:147 #: src/modules/explorer/views/lightweight.py:75 #: src/modules/explorer/views/basic.py:109 #: src/modules/explorer/views/full.py:137 #: src/modules/explorer/views/albums.py:110 #, fuzzy msgid "Loading music…" msgstr "Läser in musik" #: src/modules/explorer/views/normal.py:155 #: src/modules/explorer/views/basic.py:117 #: src/modules/explorer/views/full.py:145 #: src/modules/explorer/views/albums.py:118 msgid "Loading artists…" msgstr "" #: src/modules/explorer/views/normal.py:174 #, python-format msgid "%(art)d artists (%(sng)d songs)" msgstr "" #: src/modules/explorer/views/normal.py:215 #: src/modules/explorer/views/normal.py:232 #, python-format msgid "%(alb)d albums (%(sng)d songs)" msgstr "" #: src/modules/explorer/views/__init__.py:89 msgid "" "Welcome in Bluemindo!\n" "\n" "First of all, you need to configure the explorer module in order to choose " "your root music directory. This is easy! Go in the File menu, click on " "Preferences and you will be able to configure all Bluemindo's modules." msgstr "" "Bluemindo hälsar dig välkommen!\n" "\n" "Till att börja med måste du konfigurera utforskarmodulen för att kunna välja " "din rootmusikkatalog. Detta är enkelt! Gå till Filmenyn, klicka på " "Inställningar och du kommer att kunna konfigurera alla Bluemindos moduler." #: src/modules/explorer/views/full.py:295 #, python-format msgid "%d albums" msgstr "" #: src/modules/explorer/views/full.py:296 #, fuzzy, python-format msgid "%d songs" msgstr "Redigera låttaggar" #: src/modules/explorer/views/full.py:301 #, python-format msgid "%d plays" msgstr "" #: src/modules/explorer/views/webradios.py:97 msgid "Bookmarks" msgstr "" #: src/modules/explorer/views/webradios.py:98 msgid "World Wide Web" msgstr "" #: src/modules/explorer/views/webradios.py:107 #: src/modules/explorer/views/webradios.py:205 #: src/modules/explorer/views/webradios.py:281 msgid "Downloading…" msgstr "" #: src/modules/explorer/views/webradios.py:129 #, fuzzy msgid "Name" msgstr "Namn:" #: src/modules/explorer/views/webradios.py:138 msgid "Listener count" msgstr "" #: src/modules/explorer/views/playlists.py:96 msgid "Top 50 songs" msgstr "" #: src/modules/explorer/views/playlists.py:97 msgid "Top 100 songs" msgstr "" #: src/modules/explorer/views/playlists.py:98 msgid "Top 10 albums" msgstr "" #: src/modules/explorer/views/playlists.py:99 msgid "Random 50 songs" msgstr "" #: src/modules/explorer/views/playlists.py:100 msgid "Random 100 songs" msgstr "" #: src/modules/explorer/views/playlists.py:238 #, fuzzy msgid "Delete the playlist?" msgstr "Töm spellistan" #: src/modules/explorer/views/playlists.py:247 #, python-format msgid "Do you really want to delete the %s playlist?" msgstr "" #: src/modules/explorer/views/playlists.py:264 #: src/modules/explorer/views/playlists.py:293 msgid "Winamp-like playlists" msgstr "Winamp-liknande spellistor" #: src/modules/explorer/views/playlists.py:266 msgid "Choose a playlist" msgstr "Välj en spellista" #: src/modules/explorer/views/playlists.py:295 #, fuzzy msgid "Export playlist as…" msgstr "Exportera en spellista" #: src/modules/explorer/playlist.py:100 src/modules/explorer/playlist.py:169 #: src/modules/explorer/configuration.glade:187 msgid "Title" msgstr "Titel" #: src/modules/explorer/playlist.py:101 src/modules/explorer/playlist.py:170 #: src/modules/explorer/configuration.glade:173 msgid "Artist" msgstr "Artist" #: src/modules/explorer/playlist.py:102 src/modules/explorer/playlist.py:171 #: src/modules/explorer/configuration.glade:143 msgid "Album" msgstr "Album" #: src/modules/explorer/playlist.py:168 msgid "#" msgstr "nr" #: src/modules/explorer/playlist.py:172 #: src/modules/explorer/configuration.glade:157 msgid "Genre" msgstr "Genre" #: src/modules/explorer/playlist.py:173 #: src/modules/explorer/configuration.glade:108 msgid "Comment" msgstr "" #: src/modules/explorer/playlist.py:174 #: src/modules/explorer/configuration.glade:94 msgid "Year" msgstr "" #: src/modules/explorer/playlist.py:175 #: src/modules/explorer/configuration.glade:124 msgid "Length" msgstr "Speltid" #: src/modules/explorer/playlist.py:417 #, fuzzy, python-format msgid "Added %(nbsongs)u songs (%(totalength)s)." msgstr "Lade till %(nbsongs)s låtar (%(totalength)s)." #: src/modules/explorer/musicdb.py:81 #, python-format msgid "Added %d songs" msgstr "" #: src/modules/explorer/musicdb.py:98 msgid "Update statistics…" msgstr "" #: src/modules/explorer/config.py:118 msgid "Lightweight" msgstr "Lättviktigt" #: src/modules/explorer/config.py:119 msgid "Basic" msgstr "Enkelt" #: src/modules/explorer/config.py:120 msgid "Normal" msgstr "Normalt" #: src/modules/explorer/config.py:121 msgid "Full" msgstr "Detaljerat" #: src/modules/explorer/config.py:122 #, fuzzy msgid "Albums" msgstr "Album" #: src/modules/explorer/config.py:123 #, fuzzy msgid "Playlists" msgstr "Spellista" #: src/modules/explorer/config.py:124 src/modules/player/player_gui.py:484 msgid "Webradios" msgstr "" #: src/modules/player/player_gui.py:116 msgid "Change album artwork" msgstr "Byt omslagsbild för album" #: src/modules/lyrics/__init__.py:119 #, fuzzy, python-format msgid "Downloading lyrics for %(title)s - %(artist)s…" msgstr "Låttext hittades inte (för %(title)s - %(artist)s)." #: src/modules/lyrics/__init__.py:124 #, fuzzy, python-format msgid "No lyrics found for %(title)s - %(artist)s!" msgstr "Låttext hittades inte (för %(title)s - %(artist)s)." #: src/modules/lyrics/lyricsdownloader.py:44 #, fuzzy msgid "Lyrics fetching is currently disabled." msgstr "Hämtning av låttext inaktiverad." #: src/bluemindo.py:46 #, fuzzy msgid "" "Bluemindo Copyright (C) 2007-2009 Erwan Briand\n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to\n" "redistribute it under certain conditions.\n" "\n" "Usage: bluemindo[.py] [options]\n" "\n" "Available options:\n" "--current\t\tShow the current playing song artist, title and album\n" "--current-cover\t\tShow the path to the cover of the current playing song\n" "--current-lyrics\tShow the lyrics for the current playing song\n" "\n" "--playpause, --play, --pause\tPlay or pause a song\n" "--stop\t\t\tStop a song\n" "--previous\t\tJump to the previous song in playlist\n" "--next\t\t\tJump to the next song in playlist\n" "\n" "--volume-more [STEP]\tIncrease the volume, you can specify a step (0 > 100)\n" "--volume-less [STEP]\tDecrease the volume, you can specify a step (0 > 100)\n" "--volume=VOLUME\t\tSet the volume: 0 > 100\n" "\n" "--reload\t\tReload the songs from your music folder\n" "--quit, --plunge\tQuit Bluemindo" msgstr "" "Bluemindo Copyright (C) 2007-2008 Erwan Briand\n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to\n" "redistribute it under certain conditions.\n" "\n" "Användning: bluemindo.py [options]\n" "\n" "Tillgängliga alternativ:\n" "--reload\t\t\tLäs om låtar ifrån din musikmapp\n" "--current\t\t\tShow the current playing song artist and title\n" "--playpause, --play, --pause\tSpela upp eller pausa en låt\n" "--stop\t\t\t\tStoppa en låt\n" "--previous\t\t\tHoppa till föregående låt i spellista\n" "--next\t\t\t\tHoppa till nästa låt i spellista\n" "--volume-more [STEP]\t\tÖka volymen, du kan ange en nivå (0 > 100)\n" "--volume-less [STEP]\t\tSänk volymen, du kan ange en nivå (0 > 100)\n" "--volume=VOLUME\t\t\tStäll in volymen: 0 > 100\n" "--quit, --plunge\t\tAvsluta Bluemindo" #: src/bluemindo.py:132 #, fuzzy msgid "Bluemindo is already started!" msgstr "Musikspelaren Bluemindo" #: src/bluemindo.py:133 msgid "" "The socket file located at /tmp/bluemindo already exists, so " "Bluemindo cannot start. If you still want to start Bluemindo, hit \"yes\" " "but part of the software might be broken." msgstr "" #: src/plugins/notification/configuration.glade:51 #: src/modules/explorer/editsongwindow.glade:140 msgid "Title:" msgstr "Titel:" #: src/plugins/notification/configuration.glade:95 msgid "Text:" msgstr "Text:" #: src/plugins/notification/configuration.glade:120 msgid "ms" msgstr "ms" #: src/plugins/notification/configuration.glade:142 msgid "Timeout:" msgstr "Tidsgräns:" #: src/plugins/jabber/configuration.glade:67 msgid "Jabber identifiant:" msgstr "" #: src/plugins/jabber/configuration.glade:80 #: src/plugins/audioscrobbler/configuration.glade:67 msgid "Password:" msgstr "Lösenord:" #: src/plugins/audioscrobbler/configuration.glade:52 #: src/modules/lyrics/configuration.glade:60 msgid "Server:" msgstr "Server:" #: src/plugins/audioscrobbler/configuration.glade:83 msgid "Username:" msgstr "Användarnamn:" #: src/modules/explorer/configuration.glade:40 #: src/modules/explorer/configuration.glade:239 #: src/modules/player/configuration.glade:39 #: src/modules/player/configuration.glade:72 #: src/modules/player/configuration.glade:155 #: src/modules/lyrics/configuration.glade:39 msgid "Yes" msgstr "Ja" #: src/modules/explorer/configuration.glade:58 msgid "Fetch artworks:" msgstr "" #: src/modules/explorer/configuration.glade:74 msgid "View mode:" msgstr "Visningsläge:" #: src/modules/explorer/configuration.glade:201 msgid "Track" msgstr "Spår" #: src/modules/explorer/configuration.glade:221 msgid "Show columns:" msgstr "Visa kolumner:" #: src/modules/explorer/configuration.glade:276 msgid "Folder:" msgstr "Mapp:" #: src/modules/explorer/configuration.glade:289 msgid "Scan at startup:" msgstr "Sök av vid uppstart:" #: src/modules/explorer/editsongwindow.glade:91 msgid "Year:" msgstr "" #: src/modules/explorer/editsongwindow.glade:103 #, fuzzy msgid "Track:" msgstr "Spår" #: src/modules/explorer/editsongwindow.glade:128 msgid "Genre:" msgstr "Genre:" #: src/modules/explorer/editsongwindow.glade:148 msgid "Artist:" msgstr "Artist:" #: src/modules/explorer/editsongwindow.glade:160 msgid "Album:" msgstr "Album:" #: src/modules/explorer/editsongwindow.glade:209 msgid "Comment:" msgstr "" #: src/modules/explorer/editsongwindow.glade:242 msgid "Edit a song" msgstr " Redigera en låt" #: src/modules/player/configuration.glade:27 msgid "Change window title:" msgstr "Byt fönstertitel:" #: src/modules/player/configuration.glade:60 msgid "Show a popup with cover:" msgstr "" #: src/modules/player/configuration.glade:95 msgid "GStreamer playback:" msgstr "" #: src/modules/player/configuration.glade:108 #, fuzzy msgid "Default" msgstr "Ta bort" #: src/modules/player/configuration.glade:118 msgid "Gapless" msgstr "" #: src/modules/player/configuration.glade:145 msgid "Start minimized:" msgstr "" #: src/modules/trayicon/traycontext.glade:40 msgid "Bluemindo" msgstr "Bluemindo" #: src/modules/lyrics/configuration.glade:27 msgid "Active lyrics retrieval:" msgstr "Aktiv hämtning av låttext:" #: data/glade/playlistmenu.glade:11 #, fuzzy msgid "Play now" msgstr "Spela upp denna nu" #: data/glade/playlistmenu.glade:30 #, fuzzy msgid "Add to playlist" msgstr "Lägg till i sparad spellista" #: data/glade/playlistmenu.glade:49 msgid "Remove from this playlist" msgstr "Ta bort denna från spellista" #: data/glade/playlistmenu.glade:68 #, fuzzy msgid "Show lyrics" msgstr "Visa låttext" #: data/glade/playlistmenu.glade:81 #, fuzzy msgid "Edit song's datas" msgstr "Redigera låttaggar" #: data/glade/playlistmenu.glade:96 msgid "Playlist" msgstr "Spellista" #: data/glade/playlistmenu.glade:127 data/glade/mainwindow.glade:1406 msgid "Name:" msgstr "Namn:" #: data/glade/playlistmenu.glade:149 msgid "Create a new playlist" msgstr "Skapa en ny spellista" #: data/glade/prefswindow.glade:69 msgid "Modules" msgstr "Moduler" #: data/glade/prefswindow.glade:128 msgid "Plugins" msgstr "Insticksmoduler" #: data/glade/aboutdialog.glade:8 msgid "About Bluemindo" msgstr "Om Bluemindo" #: data/glade/aboutdialog.glade:16 msgid "A really simple but powerful audio player in Python/PyGTK." msgstr "En riktigt simpel men kraftfull musikspelare skriven i Python/PyGTK." #: data/glade/mainwindow.glade:17 msgid "_File" msgstr "_Fil" #: data/glade/mainwindow.glade:57 msgid "_Show" msgstr "_Visa" #: data/glade/mainwindow.glade:65 #, fuzzy msgid "_Lightweight" msgstr "Lättviktigt" #: data/glade/mainwindow.glade:74 #, fuzzy msgid "_Basic" msgstr "Enkelt" #: data/glade/mainwindow.glade:84 #, fuzzy msgid "_Normal" msgstr "Normalt" #: data/glade/mainwindow.glade:94 #, fuzzy msgid "_Full" msgstr "Detaljerat" #: data/glade/mainwindow.glade:104 #, fuzzy msgid "Alb_ums" msgstr "Album" #: data/glade/mainwindow.glade:114 #, fuzzy msgid "_Playlists" msgstr "Spellista" #: data/glade/mainwindow.glade:124 msgid "_Webradios" msgstr "" #: data/glade/mainwindow.glade:138 msgid "Fullscreen" msgstr "Helskärmsläge" #: data/glade/mainwindow.glade:150 msgid "Small display (only player)" msgstr "Liten skärm (bara spelare)" #: data/glade/mainwindow.glade:158 msgid "Display all" msgstr "Visa allt" #: data/glade/mainwindow.glade:170 msgid "_Help" msgstr "_Hjälp" #: data/glade/mainwindow.glade:317 #, fuzzy msgid "Add a new playlist" msgstr "Lägg till i sparad spellista" #: data/glade/mainwindow.glade:327 data/glade/mainwindow.glade:1156 msgid "Delete" msgstr "Ta bort" #: data/glade/mainwindow.glade:345 #, fuzzy msgid "Import" msgstr "Exportera" #: data/glade/mainwindow.glade:355 msgid "Export" msgstr "Exportera" #: data/glade/mainwindow.glade:452 msgid "Group artists" msgstr "" #: data/glade/mainwindow.glade:486 data/glade/mainwindow.glade:1075 #, fuzzy msgid "Delete filter" msgstr "Avbryt filter" #: data/glade/mainwindow.glade:751 msgid "Show song lyrics" msgstr "Visa låttext" #: data/glade/mainwindow.glade:1013 msgid "Clean the playlist" msgstr "Töm spellistan" #: data/glade/mainwindow.glade:1023 msgid "Shuffle the playlist" msgstr "Blanda spellistan" #: data/glade/mainwindow.glade:1034 msgid "Repeat mode" msgstr "Upprepningsläge" #: data/glade/mainwindow.glade:1076 msgid "Cancel filter" msgstr "Avbryt filter" #: data/glade/mainwindow.glade:1098 msgid "Reload lyrics for this song" msgstr "Läs om låttext för denna låt" #: data/glade/mainwindow.glade:1108 msgid "" "Save lyrics for this song (this feature allows you to edit lyrics or add " "lyrics for a song)" msgstr "" "Spara låttext för denna låt (denna funktion tillåter dig\n" "att redigera låttexter eller lägga till låttext till en låt)" #: data/glade/mainwindow.glade:1146 msgid "Add" msgstr "" #: data/glade/mainwindow.glade:1166 msgid "Refresh" msgstr "" #: data/glade/mainwindow.glade:1372 msgid "Webradio" msgstr "" #: data/glade/mainwindow.glade:1436 msgid "URL:" msgstr "" #: data/glade/mainwindow.glade:1464 #, fuzzy msgid "Add a new radio" msgstr "Skapa en ny spellista" #~ msgid "Edit a song - Bluemindo" #~ msgstr "Redigera en låt - Bluemindo" #~ msgid "Audio tags written and database uploaded!" #~ msgstr "Ljudtaggar har skrivits och har skickats till databasen!" #~ msgid "Information." #~ msgstr "Information." #~ msgid "Loading" #~ msgstr "Läser in" #~ msgid "Song #%s" #~ msgstr "Låt #%s" #~ msgid "No songs found." #~ msgstr "Inga låtar hittades." #~ msgid "All your %u songs have been imported!" #~ msgstr "Alla dina %u låtar har importerats!" #~ msgid "Fetching artist %(id)u/%(n)u" #~ msgstr "Hämtar artist %(id)u/%(n)u" #~ msgid "Playlists" #~ msgstr "Spellistor" #~ msgid "Artists" #~ msgstr "Artister" #~ msgid "Change Gajim status with the current playing song." #~ msgstr "Byt Gajimstatus med den aktuella uppspelande låten." #~ msgid "Send current song to Last.fm profile" #~ msgstr "Skicka aktuell låt till Last.fm-profil" #~ msgid "Active artwork retrieval:" #~ msgstr "Aktiv hämtning av omslagsbild:" #~ msgid "" #~ "You need to restart Bluemindo to see the changes for this module." #~ msgstr "" #~ "Du måste starta om Bluemindo för att se ändringarna för denna modul." #~ msgid "Activated:" #~ msgstr "Aktiverat:" #~ msgid "Status:" #~ msgstr "Status:" #~ msgid "Status message:" #~ msgstr "Statusmeddelande:" #~ msgid "" #~ "Current\n" #~ "Online\n" #~ "Free for chat\n" #~ "Away\n" #~ "Extended away\n" #~ "Do not disturb" #~ msgstr "" #~ "Aktuell\n" #~ "Uppkopplad\n" #~ "Tid att chatta\n" #~ "Iväg\n" #~ "Extended away\n" #~ "Stör ej" #~ msgid "Song:" #~ msgstr "Låt:" #~ msgid "More" #~ msgstr "Mer" #~ msgid "Load a playlist" #~ msgstr "Läs in en spellista" #~ msgid "Import a playlist" #~ msgstr "Importera en spellista" #~ msgid "Play" #~ msgstr "Spela upp" #~ msgid "New playlist" #~ msgstr "Ny spellista" bluemindo-0.3/locale/it/LC_MESSAGES/bluemindo.po0000644000175000017500000004551511225155411021217 0ustar xbrightxbright# French translations for Bluemindo package. # Copyright (C) 2008 Erwan Briand # This file is distributed under the same license as the Bluemindo package. # xbright , 2008. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-07-06 02:04+0400\n" "PO-Revision-Date: 2008-05-27 02:22+0400\n" "Last-Translator: xbright \n" "Language-Team: French\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: src/plugins/notification/__init__.py:37 #, fuzzy msgid "Show desktop notification" msgstr "Visualizza le notifiche sulla Scrivania" #: src/plugins/notification/config.py:87 #, fuzzy msgid "" "Available tags:\n" "{title}\t\tInsert the title\n" "{artist}\tInsert the artist\n" "{album}\t\tInsert the album\n" "{comment}\tInsert the comment\n" "{genre}\t\tInsert the genre\n" "{year}\t\tInsert the year\n" "{track}\t\tInsert the track number\n" "{length}\tInsert the length" msgstr "" "Etichette disponibili :\n" " {title} - Inserisci il titolo\n" " {artist} - Inserisci l'artista\n" " {album} - Inserisci l'album\n" " {track} - Inserisci il numero della traccia\n" " {length} - Inserisci la durata\n" " {genre} - Inserisci il genere" #: src/plugins/jabber/__init__.py:46 #, fuzzy msgid "Send PEP notifications" msgstr "Invia le notifiche PEP al tuo account Jabber." #: src/plugins/audioscrobbler/__init__.py:40 #, fuzzy msgid "Send song notifications" msgstr "Visualizza le notifiche sulla Scrivania" #: src/gui/extensionsconfig.py:45 msgid "Preferences" msgstr "Preferenze" #: src/gui/extensionsconfig.py:192 msgid "This extension is not configurable." msgstr "" #: src/gui/extensionsconfig.py:208 msgid "You should start the plugin before trying to configure it." msgstr "" #: src/modules/explorer/views/normal.py:118 #: src/modules/explorer/views/lightweight.py:49 #: src/modules/explorer/views/basic.py:82 #: src/modules/explorer/views/full.py:111 #: src/modules/explorer/views/albums.py:84 msgid "Opening folder…" msgstr "" #: src/modules/explorer/views/normal.py:147 #: src/modules/explorer/views/lightweight.py:75 #: src/modules/explorer/views/basic.py:109 #: src/modules/explorer/views/full.py:137 #: src/modules/explorer/views/albums.py:110 #, fuzzy msgid "Loading music…" msgstr "Caricamento della musica" #: src/modules/explorer/views/normal.py:155 #: src/modules/explorer/views/basic.py:117 #: src/modules/explorer/views/full.py:145 #: src/modules/explorer/views/albums.py:118 msgid "Loading artists…" msgstr "" #: src/modules/explorer/views/normal.py:174 #, python-format msgid "%(art)d artists (%(sng)d songs)" msgstr "" #: src/modules/explorer/views/normal.py:215 #: src/modules/explorer/views/normal.py:232 #, python-format msgid "%(alb)d albums (%(sng)d songs)" msgstr "" #: src/modules/explorer/views/__init__.py:89 msgid "" "Welcome in Bluemindo!\n" "\n" "First of all, you need to configure the explorer module in order to choose " "your root music directory. This is easy! Go in the File menu, click on " "Preferences and you will be able to configure all Bluemindo's modules." msgstr "" "Benvenuti su Bluemindo!\n" "\n" "Prima di tutto, bisogna configurare i menù delle preferenzela vostra " "cartella della musica. E' facile! Andate nel menù File, cliccate su " "preferenze e potrete configurare tutte le impostazioni di Bluemindo." #: src/modules/explorer/views/full.py:295 #, python-format msgid "%d albums" msgstr "" #: src/modules/explorer/views/full.py:296 #, fuzzy, python-format msgid "%d songs" msgstr "Editare il testo della canzone" #: src/modules/explorer/views/full.py:301 #, python-format msgid "%d plays" msgstr "" #: src/modules/explorer/views/webradios.py:97 msgid "Bookmarks" msgstr "" #: src/modules/explorer/views/webradios.py:98 msgid "World Wide Web" msgstr "" #: src/modules/explorer/views/webradios.py:107 #: src/modules/explorer/views/webradios.py:205 #: src/modules/explorer/views/webradios.py:281 msgid "Downloading…" msgstr "" #: src/modules/explorer/views/webradios.py:129 #, fuzzy msgid "Name" msgstr "Nome:" #: src/modules/explorer/views/webradios.py:138 msgid "Listener count" msgstr "" #: src/modules/explorer/views/playlists.py:96 msgid "Top 50 songs" msgstr "" #: src/modules/explorer/views/playlists.py:97 msgid "Top 100 songs" msgstr "" #: src/modules/explorer/views/playlists.py:98 msgid "Top 10 albums" msgstr "" #: src/modules/explorer/views/playlists.py:99 msgid "Random 50 songs" msgstr "" #: src/modules/explorer/views/playlists.py:100 msgid "Random 100 songs" msgstr "" #: src/modules/explorer/views/playlists.py:238 #, fuzzy msgid "Delete the playlist?" msgstr "Pulizia della playlist" #: src/modules/explorer/views/playlists.py:247 #, python-format msgid "Do you really want to delete the %s playlist?" msgstr "" #: src/modules/explorer/views/playlists.py:264 #: src/modules/explorer/views/playlists.py:293 msgid "Winamp-like playlists" msgstr "Playlist simile a Winamp" #: src/modules/explorer/views/playlists.py:266 msgid "Choose a playlist" msgstr "Scegliere una playlist" #: src/modules/explorer/views/playlists.py:295 #, fuzzy msgid "Export playlist as…" msgstr "Esporta una playlist" #: src/modules/explorer/playlist.py:100 src/modules/explorer/playlist.py:169 #: src/modules/explorer/configuration.glade:187 msgid "Title" msgstr "Titolo" #: src/modules/explorer/playlist.py:101 src/modules/explorer/playlist.py:170 #: src/modules/explorer/configuration.glade:173 msgid "Artist" msgstr "Artista" #: src/modules/explorer/playlist.py:102 src/modules/explorer/playlist.py:171 #: src/modules/explorer/configuration.glade:143 msgid "Album" msgstr "Album" #: src/modules/explorer/playlist.py:168 msgid "#" msgstr "#" #: src/modules/explorer/playlist.py:172 #: src/modules/explorer/configuration.glade:157 msgid "Genre" msgstr "Genere" #: src/modules/explorer/playlist.py:173 #: src/modules/explorer/configuration.glade:108 msgid "Comment" msgstr "" #: src/modules/explorer/playlist.py:174 #: src/modules/explorer/configuration.glade:94 msgid "Year" msgstr "" #: src/modules/explorer/playlist.py:175 #: src/modules/explorer/configuration.glade:124 msgid "Length" msgstr "Durata" #: src/modules/explorer/playlist.py:417 #, fuzzy, python-format msgid "Added %(nbsongs)u songs (%(totalength)s)." msgstr "%(song)s Canzone/i aggiunta/e (%(length)s)." #: src/modules/explorer/musicdb.py:81 #, python-format msgid "Added %d songs" msgstr "" #: src/modules/explorer/musicdb.py:98 msgid "Update statistics…" msgstr "" #: src/modules/explorer/config.py:118 msgid "Lightweight" msgstr "Leggero" #: src/modules/explorer/config.py:119 msgid "Basic" msgstr "Base" #: src/modules/explorer/config.py:120 msgid "Normal" msgstr "Normale" #: src/modules/explorer/config.py:121 msgid "Full" msgstr "Completo" #: src/modules/explorer/config.py:122 #, fuzzy msgid "Albums" msgstr "Album" #: src/modules/explorer/config.py:123 #, fuzzy msgid "Playlists" msgstr "Playlist" #: src/modules/explorer/config.py:124 src/modules/player/player_gui.py:484 msgid "Webradios" msgstr "" #: src/modules/player/player_gui.py:116 msgid "Change album artwork" msgstr "Cambia la copertina dell'album" #: src/modules/lyrics/__init__.py:119 #, fuzzy, python-format msgid "Downloading lyrics for %(title)s - %(artist)s…" msgstr "Testo non trovato (per %(title)s - %(artist)s)." #: src/modules/lyrics/__init__.py:124 #, fuzzy, python-format msgid "No lyrics found for %(title)s - %(artist)s!" msgstr "Testo non trovato (per %(title)s - %(artist)s)." #: src/modules/lyrics/lyricsdownloader.py:44 #, fuzzy msgid "Lyrics fetching is currently disabled." msgstr "Recupero del testo disattivato" #: src/bluemindo.py:46 #, fuzzy msgid "" "Bluemindo Copyright (C) 2007-2009 Erwan Briand\n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to\n" "redistribute it under certain conditions.\n" "\n" "Usage: bluemindo[.py] [options]\n" "\n" "Available options:\n" "--current\t\tShow the current playing song artist, title and album\n" "--current-cover\t\tShow the path to the cover of the current playing song\n" "--current-lyrics\tShow the lyrics for the current playing song\n" "\n" "--playpause, --play, --pause\tPlay or pause a song\n" "--stop\t\t\tStop a song\n" "--previous\t\tJump to the previous song in playlist\n" "--next\t\t\tJump to the next song in playlist\n" "\n" "--volume-more [STEP]\tIncrease the volume, you can specify a step (0 > 100)\n" "--volume-less [STEP]\tDecrease the volume, you can specify a step (0 > 100)\n" "--volume=VOLUME\t\tSet the volume: 0 > 100\n" "\n" "--reload\t\tReload the songs from your music folder\n" "--quit, --plunge\tQuit Bluemindo" msgstr "" "Bluemindo Copyright (C) 2007-2008 Erwan Briand\n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to\n" "redistribute it under certain conditions.\n" "\n" "Utilisation : bluemindo.py [options]\n" "\n" "Options disponibles:\n" "--reload\t\t\tRecharger les chansons de votre dossier musical\n" "--current\t\t\tAfficher l'artiste et le titre de la chanson en cours\n" "--playpause, --play, --pause\tLire ou mettre en pause une chanson\n" "--stop\t\t\t\tStopper une chanson\n" "--previous\t\t\tAller à la précédente chanson dans la liste de lecture\n" "--next\t\t\t\tAller à la suivante chanson dans la liste de lecture\n" "--volume-more [STEP]\t\tAugmenter le volume, vous pouvez spécifier un pas (0 " "> 100)\n" "--volume-less [STEP]\t\tDiminuer le volume, vous pouvez spécifier un pas (0 " "> 100)\n" "--volume=VOLUME\t\t\tRégler le volume: 0 > 100\n" "--quit, --plunge\t\tQuitter Bluemindo" #: src/bluemindo.py:132 #, fuzzy msgid "Bluemindo is already started!" msgstr "Bluemindo player musicale" #: src/bluemindo.py:133 msgid "" "The socket file located at /tmp/bluemindo already exists, so " "Bluemindo cannot start. If you still want to start Bluemindo, hit \"yes\" " "but part of the software might be broken." msgstr "" #: src/plugins/notification/configuration.glade:51 #: src/modules/explorer/editsongwindow.glade:140 msgid "Title:" msgstr "Titolo:" #: src/plugins/notification/configuration.glade:95 msgid "Text:" msgstr "Testo:" #: src/plugins/notification/configuration.glade:120 msgid "ms" msgstr "ms" #: src/plugins/notification/configuration.glade:142 msgid "Timeout:" msgstr "Fine del tempo:" #: src/plugins/jabber/configuration.glade:67 msgid "Jabber identifiant:" msgstr "Identificazione Jabber:" #: src/plugins/jabber/configuration.glade:80 #: src/plugins/audioscrobbler/configuration.glade:67 msgid "Password:" msgstr "Password:" #: src/plugins/audioscrobbler/configuration.glade:52 #: src/modules/lyrics/configuration.glade:60 msgid "Server:" msgstr "Server:" #: src/plugins/audioscrobbler/configuration.glade:83 msgid "Username:" msgstr "Nome utente:" #: src/modules/explorer/configuration.glade:40 #: src/modules/explorer/configuration.glade:239 #: src/modules/player/configuration.glade:39 #: src/modules/player/configuration.glade:72 #: src/modules/player/configuration.glade:155 #: src/modules/lyrics/configuration.glade:39 msgid "Yes" msgstr "Sì" #: src/modules/explorer/configuration.glade:58 msgid "Fetch artworks:" msgstr "" #: src/modules/explorer/configuration.glade:74 msgid "View mode:" msgstr "Modalità vista:" #: src/modules/explorer/configuration.glade:201 msgid "Track" msgstr "Traccia" #: src/modules/explorer/configuration.glade:221 msgid "Show columns:" msgstr "Visualizza colonne:" #: src/modules/explorer/configuration.glade:276 msgid "Folder:" msgstr "Cartella:" #: src/modules/explorer/configuration.glade:289 msgid "Scan at startup:" msgstr "Fai una scansione all'avvio" #: src/modules/explorer/editsongwindow.glade:91 msgid "Year:" msgstr "" #: src/modules/explorer/editsongwindow.glade:103 #, fuzzy msgid "Track:" msgstr "Traccia" #: src/modules/explorer/editsongwindow.glade:128 msgid "Genre:" msgstr "Genere:" #: src/modules/explorer/editsongwindow.glade:148 msgid "Artist:" msgstr "Artista :" #: src/modules/explorer/editsongwindow.glade:160 msgid "Album:" msgstr "Album:" #: src/modules/explorer/editsongwindow.glade:209 msgid "Comment:" msgstr "" #: src/modules/explorer/editsongwindow.glade:242 msgid "Edit a song" msgstr "Editare una canzone" #: src/modules/player/configuration.glade:27 msgid "Change window title:" msgstr "Cambia il titolo della finestra:" #: src/modules/player/configuration.glade:60 msgid "Show a popup with cover:" msgstr "" #: src/modules/player/configuration.glade:95 msgid "GStreamer playback:" msgstr "" #: src/modules/player/configuration.glade:108 #, fuzzy msgid "Default" msgstr "Cancella" #: src/modules/player/configuration.glade:118 msgid "Gapless" msgstr "" #: src/modules/player/configuration.glade:145 msgid "Start minimized:" msgstr "" #: src/modules/trayicon/traycontext.glade:40 msgid "Bluemindo" msgstr "Bluemindo" #: src/modules/lyrics/configuration.glade:27 msgid "Active lyrics retrieval:" msgstr "Scarica il testo:" #: data/glade/playlistmenu.glade:11 #, fuzzy msgid "Play now" msgstr "Usalo ora" #: data/glade/playlistmenu.glade:30 #, fuzzy msgid "Add to playlist" msgstr "Aggiungi alla plyalist salvata" #: data/glade/playlistmenu.glade:49 msgid "Remove from this playlist" msgstr "Rimuovi da questa playlist" #: data/glade/playlistmenu.glade:68 #, fuzzy msgid "Show lyrics" msgstr "Visualizza il testo della canzone" #: data/glade/playlistmenu.glade:81 #, fuzzy msgid "Edit song's datas" msgstr "Editare il testo della canzone" #: data/glade/playlistmenu.glade:96 msgid "Playlist" msgstr "Playlist" #: data/glade/playlistmenu.glade:127 data/glade/mainwindow.glade:1406 msgid "Name:" msgstr "Nome:" #: data/glade/playlistmenu.glade:149 msgid "Create a new playlist" msgstr "Crea una nuova playlist" #: data/glade/prefswindow.glade:69 msgid "Modules" msgstr "Moduli" #: data/glade/prefswindow.glade:128 msgid "Plugins" msgstr "Plugins" #: data/glade/aboutdialog.glade:8 msgid "About Bluemindo" msgstr "a proposito di Bluemindo" #: data/glade/aboutdialog.glade:16 msgid "A really simple but powerful audio player in Python/PyGTK." msgstr "Un lettore semplice ma potente scritto in Python/PyGTK." #: data/glade/mainwindow.glade:17 msgid "_File" msgstr "_Fichier" #: data/glade/mainwindow.glade:57 msgid "_Show" msgstr "_Affichage" #: data/glade/mainwindow.glade:65 #, fuzzy msgid "_Lightweight" msgstr "Leggero" #: data/glade/mainwindow.glade:74 #, fuzzy msgid "_Basic" msgstr "Base" #: data/glade/mainwindow.glade:84 #, fuzzy msgid "_Normal" msgstr "Normale" #: data/glade/mainwindow.glade:94 #, fuzzy msgid "_Full" msgstr "Completo" #: data/glade/mainwindow.glade:104 #, fuzzy msgid "Alb_ums" msgstr "Album" #: data/glade/mainwindow.glade:114 #, fuzzy msgid "_Playlists" msgstr "Playlist" #: data/glade/mainwindow.glade:124 msgid "_Webradios" msgstr "" #: data/glade/mainwindow.glade:138 msgid "Fullscreen" msgstr "Schermo intero" #: data/glade/mainwindow.glade:150 msgid "Small display (only player)" msgstr "finestra piccola (solo lettore)" #: data/glade/mainwindow.glade:158 msgid "Display all" msgstr "Visualizza tutto" #: data/glade/mainwindow.glade:170 msgid "_Help" msgstr "_Aide" #: data/glade/mainwindow.glade:317 #, fuzzy msgid "Add a new playlist" msgstr "Aggiungi alla plyalist salvata" #: data/glade/mainwindow.glade:327 data/glade/mainwindow.glade:1156 msgid "Delete" msgstr "Cancella" #: data/glade/mainwindow.glade:345 #, fuzzy msgid "Import" msgstr "Esporta" #: data/glade/mainwindow.glade:355 msgid "Export" msgstr "Esporta" #: data/glade/mainwindow.glade:452 msgid "Group artists" msgstr "" #: data/glade/mainwindow.glade:486 data/glade/mainwindow.glade:1075 #, fuzzy msgid "Delete filter" msgstr "Annulla il filtro" #: data/glade/mainwindow.glade:751 msgid "Show song lyrics" msgstr "Visualizza il testo della canzone" #: data/glade/mainwindow.glade:1013 msgid "Clean the playlist" msgstr "Pulizia della playlist" #: data/glade/mainwindow.glade:1023 msgid "Shuffle the playlist" msgstr "Mischiare la playlist" #: data/glade/mainwindow.glade:1034 msgid "Repeat mode" msgstr "Modalità ripeti" #: data/glade/mainwindow.glade:1076 msgid "Cancel filter" msgstr "Annulla il filtro" #: data/glade/mainwindow.glade:1098 msgid "Reload lyrics for this song" msgstr "Ricarica il testo della canzone" #: data/glade/mainwindow.glade:1108 msgid "" "Save lyrics for this song (this feature allows you to edit lyrics or add " "lyrics for a song)" msgstr "" "Salva il testo di questa canzone (questa funzione ti autorizza ad editare il " "testo o di aggiungerlo)" #: data/glade/mainwindow.glade:1146 msgid "Add" msgstr "" #: data/glade/mainwindow.glade:1166 msgid "Refresh" msgstr "" #: data/glade/mainwindow.glade:1372 msgid "Webradio" msgstr "" #: data/glade/mainwindow.glade:1436 msgid "URL:" msgstr "" #: data/glade/mainwindow.glade:1464 #, fuzzy msgid "Add a new radio" msgstr "Crea una nuova playlist" #~ msgid "Edit a song - Bluemindo" #~ msgstr "Edita canzone - Bluemindo" #~ msgid "Audio tags written and database uploaded!" #~ msgstr "Etichetta audio scritta e database aggiornato!" #~ msgid "Information." #~ msgstr "Informazioni." #~ msgid "Loading" #~ msgstr "Caricamento" #~ msgid "Song #%s" #~ msgstr "Canzone #%s" #~ msgid "No songs found." #~ msgstr "Nessuna canzone trovata." #~ msgid "All your %u songs have been imported!" #~ msgstr "Tutte le tue %u canzoni sono state importate!" #~ msgid "Fetching artist %(id)u/%(n)u" #~ msgstr "Recupero dell'artista %(id)u/%(n)u" #~ msgid "Playlists" #~ msgstr "Playlist" #~ msgid "Artists" #~ msgstr "Artisti" #~ msgid "Change Gajim status with the current playing song." #~ msgstr "Cambia lo stato di Gajim con la canzone che stai ascoltando." #~ msgid "Send current song to Last.fm profile" #~ msgstr "Invia la canzone corrente sul profilo di Last.fm" #~ msgid "Active artwork retrieval:" #~ msgstr "Scarica copertina:" #~ msgid "" #~ "You need to restart Bluemindo to see the changes for this module." #~ msgstr "" #~ "Bisogna riavviare Bluemindo per attivare i cambiamenti a questo modulo." #~ "" #~ msgid "Activated:" #~ msgstr "Attivato" #~ msgid "Status:" #~ msgstr "Stato:" #~ msgid "Status message:" #~ msgstr "Messaggio di stato :" #~ msgid "" #~ "Current\n" #~ "Online\n" #~ "Free for chat\n" #~ "Away\n" #~ "Extended away\n" #~ "Do not disturb" #~ msgstr "" #~ "In corso\n" #~ "In linea\n" #~ "Disponibile per la chat\n" #~ "Assente\n" #~ "Non disponibile\n" #~ "Occupato" #~ msgid "Song:" #~ msgstr "Canzone:" #~ msgid "More" #~ msgstr "Più" #~ msgid "Load a playlist" #~ msgstr "Carica una playlist" #~ msgid "Import a playlist" #~ msgstr "Importare una playlist" #~ msgid "Play" #~ msgstr "Play" #~ msgid "New playlist" #~ msgstr "Nuova playlist" bluemindo-0.3/locale/sk/LC_MESSAGES/bluemindo.po0000644000175000017500000004326111225217611021215 0ustar xbrightxbrightmsgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-07-06 02:04+0400\n" "PO-Revision-Date: 2008-05-27 02:22+0400\n" "Last-Translator: LubomirR \n" "Language-Team: Slovak\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n >= 2 && n <= 4) ? 1 : 2;\n" #: src/plugins/notification/__init__.py:37 msgid "Show desktop notification" msgstr "Zobrazí oznámenia" #: src/plugins/notification/config.py:87 msgid "" "Available tags:\n" "{title}\t\tInsert the title\n" "{artist}\tInsert the artist\n" "{album}\t\tInsert the album\n" "{comment}\tInsert the comment\n" "{genre}\t\tInsert the genre\n" "{year}\t\tInsert the year\n" "{track}\t\tInsert the track number\n" "{length}\tInsert the length" msgstr "" "Dostupné tagy:\n" "{title}\t\tVložiť názov\n" "{artist}\tVložiť interpreta\n" "{album}\t\tVložiť album\n" "{comment}\tVložiť komentár\n" "{genre}\t\tVložiť žáner\n" "{year}\t\tVložiť rok\n" "{track}\t\tVložiť číslo stopy\n" "{length}\tVložiť dĺžku" #: src/plugins/jabber/__init__.py:46 msgid "Send PEP notifications" msgstr "Poslať PEP oznámenia" #: src/plugins/audioscrobbler/__init__.py:40 msgid "Send song notifications" msgstr "Posielať oznámenia o skladbách" #: src/gui/extensionsconfig.py:45 msgid "Preferences" msgstr "Možnosti" #: src/gui/extensionsconfig.py:192 msgid "This extension is not configurable." msgstr "Toto rozšírenie neumožňuje konfiguráciu." #: src/gui/extensionsconfig.py:208 msgid "You should start the plugin before trying to configure it." msgstr "Tento plugin musíte zapnúť skôr, než sa budete pokúšať ho nastaviť." #: src/modules/explorer/views/normal.py:118 #: src/modules/explorer/views/lightweight.py:49 #: src/modules/explorer/views/basic.py:82 #: src/modules/explorer/views/full.py:111 #: src/modules/explorer/views/albums.py:84 msgid "Opening folder…" msgstr "Otváram adresár…" #: src/modules/explorer/views/normal.py:147 #: src/modules/explorer/views/lightweight.py:75 #: src/modules/explorer/views/basic.py:109 #: src/modules/explorer/views/full.py:137 #: src/modules/explorer/views/albums.py:110 msgid "Loading music…" msgstr "Načítavam hudbu…" #: src/modules/explorer/views/normal.py:155 #: src/modules/explorer/views/basic.py:117 #: src/modules/explorer/views/full.py:145 #: src/modules/explorer/views/albums.py:118 msgid "Loading artists…" msgstr "Načítavam interpretov…" #: src/modules/explorer/views/normal.py:174 #, fuzzy, python-format msgid "%(art)d artists (%(sng)d songs)" msgstr "%{art}d interpretov (%(sng)d skladieb)" #: src/modules/explorer/views/normal.py:215 #: src/modules/explorer/views/normal.py:232 #, python-format msgid "%(alb)d albums (%(sng)d songs)" msgstr "%(alb)d albumov (%(sng)d skladieb)" #: src/modules/explorer/views/__init__.py:89 msgid "" "Welcome in Bluemindo!\n" "\n" "First of all, you need to configure the explorer module in order to choose " "your root music directory. This is easy! Go in the File menu, click on " "Preferences and you will be able to configure all Bluemindo's modules." msgstr "" "Vitajte v Blueminde!\n" "\n" "Najprv potrebujete nastaviť modul prieskumníka, aby ste si mohli vybrať váš " "hudobný priečinok. Je to jednoduché! Choďte do menu Súbor, kliknite na " "Možnosti a budete si môcť nastaviť všetky moduly v Blueminde." #: src/modules/explorer/views/full.py:295 #, python-format msgid "%d albums" msgstr "%d albumov" #: src/modules/explorer/views/full.py:296 #, python-format msgid "%d songs" msgstr "%d skladieb" #: src/modules/explorer/views/full.py:301 #, python-format msgid "%d plays" msgstr "%d prehratí" #: src/modules/explorer/views/webradios.py:97 msgid "Bookmarks" msgstr "Záložky" #: src/modules/explorer/views/webradios.py:98 msgid "World Wide Web" msgstr "Celosvetová sieť" #: src/modules/explorer/views/webradios.py:107 #: src/modules/explorer/views/webradios.py:205 #: src/modules/explorer/views/webradios.py:281 msgid "Downloading…" msgstr "Sťahujem…" #: src/modules/explorer/views/webradios.py:129 msgid "Name" msgstr "Meno" #: src/modules/explorer/views/webradios.py:138 msgid "Listener count" msgstr "Počet poslucháčov" #: src/modules/explorer/views/playlists.py:96 msgid "Top 50 songs" msgstr "Top 50 skladieb" #: src/modules/explorer/views/playlists.py:97 msgid "Top 100 songs" msgstr "Top 100 skladieb" #: src/modules/explorer/views/playlists.py:98 msgid "Top 10 albums" msgstr "Top 10 albumov" #: src/modules/explorer/views/playlists.py:99 msgid "Random 50 songs" msgstr "50 náhodných skladieb" #: src/modules/explorer/views/playlists.py:100 msgid "Random 100 songs" msgstr "100 náhodných skladieb" #: src/modules/explorer/views/playlists.py:238 msgid "Delete the playlist?" msgstr "Zmazať zoznam skladieb?" #: src/modules/explorer/views/playlists.py:247 #, python-format msgid "Do you really want to delete the %s playlist?" msgstr "Naozaj chcete zmazať zoznam skladieb %s?" #: src/modules/explorer/views/playlists.py:264 #: src/modules/explorer/views/playlists.py:293 msgid "Winamp-like playlists" msgstr "Zoznamy skladieb ako vo Winampe" #: src/modules/explorer/views/playlists.py:266 msgid "Choose a playlist" msgstr "Vybrať zoznam skladieb" #: src/modules/explorer/views/playlists.py:295 msgid "Export playlist as…" msgstr "Exportovať zoznam skladieb ako…" #: src/modules/explorer/playlist.py:100 src/modules/explorer/playlist.py:169 #: src/modules/explorer/configuration.glade:187 msgid "Title" msgstr "Názov" #: src/modules/explorer/playlist.py:101 src/modules/explorer/playlist.py:170 #: src/modules/explorer/configuration.glade:173 msgid "Artist" msgstr "Interpret" #: src/modules/explorer/playlist.py:102 src/modules/explorer/playlist.py:171 #: src/modules/explorer/configuration.glade:143 msgid "Album" msgstr "Album" #: src/modules/explorer/playlist.py:168 msgid "#" msgstr "#" #: src/modules/explorer/playlist.py:172 #: src/modules/explorer/configuration.glade:157 msgid "Genre" msgstr "Žáner" #: src/modules/explorer/playlist.py:173 #: src/modules/explorer/configuration.glade:108 msgid "Comment" msgstr "Komentár" #: src/modules/explorer/playlist.py:174 #: src/modules/explorer/configuration.glade:94 msgid "Year" msgstr "Rok" #: src/modules/explorer/playlist.py:175 #: src/modules/explorer/configuration.glade:124 msgid "Length" msgstr "Dĺžka" #: src/modules/explorer/playlist.py:417 #, python-format msgid "Added %(nbsongs)u songs (%(totalength)s)." msgstr "%(nbsongs)u skladieb pridaných (%(totalength)s)." #: src/modules/explorer/musicdb.py:81 #, python-format msgid "Added %d songs" msgstr "Pridaných %d skladieb" #: src/modules/explorer/musicdb.py:98 msgid "Update statistics…" msgstr "Aktualizovať štatistiku…" #: src/modules/explorer/config.py:118 msgid "Lightweight" msgstr "Ľahký" #: src/modules/explorer/config.py:119 msgid "Basic" msgstr "Základný" #: src/modules/explorer/config.py:120 msgid "Normal" msgstr "Normálny" #: src/modules/explorer/config.py:121 msgid "Full" msgstr "Plný" #: src/modules/explorer/config.py:122 msgid "Albums" msgstr "Albumy" #: src/modules/explorer/config.py:123 msgid "Playlists" msgstr "Zoznamy skladieb" #: src/modules/explorer/config.py:124 src/modules/player/player_gui.py:484 msgid "Webradios" msgstr "Webové rádia" #: src/modules/player/player_gui.py:116 msgid "Change album artwork" msgstr "Zmeniť obrázok albumu" #: src/modules/lyrics/__init__.py:119 #, python-format msgid "Downloading lyrics for %(title)s - %(artist)s…" msgstr "Sťahujem text skladby pre %(title)s - %(artist)s…" #: src/modules/lyrics/__init__.py:124 #, python-format msgid "No lyrics found for %(title)s - %(artist)s!" msgstr "Text skladby pre %(title)s - %(artist)s nenájdený!" #: src/modules/lyrics/lyricsdownloader.py:44 msgid "Lyrics fetching is currently disabled." msgstr "Získavanie textu skladieb je momentálne zakázané." #: src/bluemindo.py:46 msgid "" "Bluemindo Copyright (C) 2007-2009 Erwan Briand\n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to\n" "redistribute it under certain conditions.\n" "\n" "Usage: bluemindo[.py] [options]\n" "\n" "Available options:\n" "--current\t\tShow the current playing song artist, title and album\n" "--current-cover\t\tShow the path to the cover of the current playing song\n" "--current-lyrics\tShow the lyrics for the current playing song\n" "\n" "--playpause, --play, --pause\tPlay or pause a song\n" "--stop\t\t\tStop a song\n" "--previous\t\tJump to the previous song in playlist\n" "--next\t\t\tJump to the next song in playlist\n" "\n" "--volume-more [STEP]\tIncrease the volume, you can specify a step (0 > 100)\n" "--volume-less [STEP]\tDecrease the volume, you can specify a step (0 > 100)\n" "--volume=VOLUME\t\tSet the volume: 0 > 100\n" "\n" "--reload\t\tReload the songs from your music folder\n" "--quit, --plunge\tQuit Bluemindo" msgstr "" "Bluemindo Copyright (C) 2007-2009 Erwan Briand\n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to\n" "redistribute it under certain conditions.\n" "\n" "Použitie: bluemindo.py [možnosti]\n" "\n" "Dostupné možnosti:\n" "--current\t\tZobraziť interpreta, názov a album práve prehrávanej skladby\n" "--current-cover\t\tZobraziť cestu k obalu práve prehrávanej skladby\n" "--current-lyrics\tZobraziť text práve prehrávanej skladby\n" "\n" "--playpause, --play, --pause\tPrehrať aleb pozastaviť skladbu\n" "--stop\t\t\tZastaviť skladbu\n" "--previous\t\tSkočiť na predchádzajúcu skladbu v zozname\n" "--next\t\t\tSkočiť na nasledujúcu skladbu v zozname\n" "\n" "--volume-more [SKOK]\tZvýšiť hlasitosť, môžete určiť skok (0 > 100)\n" "--volume-less [SKOK]\tZnížiť hlasitosť, môžete určiť skok (0 > 100)\n" "--volume=VOLUME\t\tNastaviť hlasitosť: 0 > 100\n" "\n" "--reload\t\tZnovu načítať skladby z hudobného adresára\n" "--quit, --plunge\tUkončiť Bluemindo" #: src/bluemindo.py:132 msgid "Bluemindo is already started!" msgstr "Bluemindo už beží!" #: src/bluemindo.py:133 msgid "" "The socket file located at /tmp/bluemindo already exists, so " "Bluemindo cannot start. If you still want to start Bluemindo, hit \"yes\" " "but part of the software might be broken." msgstr "" "Socket súbor nachádzajúci sa v /tmp/bluemindo už existuje, takže " "Bluemindo sa nemôže zapnúť. Ak stále chcete zapnúť Bluemindo, kliknite na " "\"yes\", ale časť programu môže byť pokazená." #: src/plugins/notification/configuration.glade:51 #: src/modules/explorer/editsongwindow.glade:140 msgid "Title:" msgstr "Titulok:" #: src/plugins/notification/configuration.glade:95 msgid "Text:" msgstr "Text:" #: src/plugins/notification/configuration.glade:120 msgid "ms" msgstr "ms" #: src/plugins/notification/configuration.glade:142 msgid "Timeout:" msgstr "Časový limit:" #: src/plugins/jabber/configuration.glade:67 msgid "Jabber identifiant:" msgstr "Jabber identifikátor:" #: src/plugins/jabber/configuration.glade:80 #: src/plugins/audioscrobbler/configuration.glade:67 msgid "Password:" msgstr "Heslo:" #: src/plugins/audioscrobbler/configuration.glade:52 #: src/modules/lyrics/configuration.glade:60 msgid "Server:" msgstr "Server:" #: src/plugins/audioscrobbler/configuration.glade:83 msgid "Username:" msgstr "Užívateľské meno:" #: src/modules/explorer/configuration.glade:40 #: src/modules/explorer/configuration.glade:239 #: src/modules/player/configuration.glade:39 #: src/modules/player/configuration.glade:72 #: src/modules/player/configuration.glade:155 #: src/modules/lyrics/configuration.glade:39 msgid "Yes" msgstr "Áno" #: src/modules/explorer/configuration.glade:58 msgid "Fetch artworks:" msgstr "Získavať obrázky albumov:" #: src/modules/explorer/configuration.glade:74 msgid "View mode:" msgstr "Zobraziť mód:" #: src/modules/explorer/configuration.glade:201 msgid "Track" msgstr "Stopa" #: src/modules/explorer/configuration.glade:221 msgid "Show columns:" msgstr "Zobraziť stĺpce:" #: src/modules/explorer/configuration.glade:276 msgid "Folder:" msgstr "Adresár:" #: src/modules/explorer/configuration.glade:289 msgid "Scan at startup:" msgstr "Preskúmať pri štarte:" #: src/modules/explorer/editsongwindow.glade:91 msgid "Year:" msgstr "Rok:" #: src/modules/explorer/editsongwindow.glade:103 msgid "Track:" msgstr "Stopa:" #: src/modules/explorer/editsongwindow.glade:128 msgid "Genre:" msgstr "Žáner:" #: src/modules/explorer/editsongwindow.glade:148 msgid "Artist:" msgstr "Interpret:" #: src/modules/explorer/editsongwindow.glade:160 msgid "Album:" msgstr "Album:" #: src/modules/explorer/editsongwindow.glade:209 msgid "Comment:" msgstr "Komentár:" #: src/modules/explorer/editsongwindow.glade:242 msgid "Edit a song" msgstr "Upraviť skladbu" #: src/modules/player/configuration.glade:27 msgid "Change window title:" msgstr "Zmeniť titulok okna:" #: src/modules/player/configuration.glade:60 msgid "Show a popup with cover:" msgstr "Zobraziť vyskakovacie okno s obalom:" #: src/modules/player/configuration.glade:95 msgid "GStreamer playback:" msgstr "" #: src/modules/player/configuration.glade:108 msgid "Default" msgstr "Základné nastavenia" #: src/modules/player/configuration.glade:118 msgid "Gapless" msgstr "Prehrávanie bez medzier" #: src/modules/player/configuration.glade:145 msgid "Start minimized:" msgstr "Na začiatku minimalizované:" #: src/modules/trayicon/traycontext.glade:40 msgid "Bluemindo" msgstr "Bluemindo" #: src/modules/lyrics/configuration.glade:27 msgid "Active lyrics retrieval:" msgstr "Aktívne získavanie textu:" #: data/glade/playlistmenu.glade:11 msgid "Play now" msgstr "Prehrať teraz" #: data/glade/playlistmenu.glade:30 msgid "Add to playlist" msgstr "Pridať do zoznamu skladieb" #: data/glade/playlistmenu.glade:49 msgid "Remove from this playlist" msgstr "Odstrániť z tohto zoznamu" #: data/glade/playlistmenu.glade:68 msgid "Show lyrics" msgstr "Zobraziť text skladby" #: data/glade/playlistmenu.glade:81 msgid "Edit song's datas" msgstr "Upraviť dáta o skladbe" #: data/glade/playlistmenu.glade:96 msgid "Playlist" msgstr "Zoznam skladieb" #: data/glade/playlistmenu.glade:127 data/glade/mainwindow.glade:1406 msgid "Name:" msgstr "Meno:" #: data/glade/playlistmenu.glade:149 msgid "Create a new playlist" msgstr "Vytvoriť nový zoznam skladieb" #: data/glade/prefswindow.glade:69 msgid "Modules" msgstr "Moduly" #: data/glade/prefswindow.glade:128 msgid "Plugins" msgstr "Pluginy" #: data/glade/aboutdialog.glade:8 msgid "About Bluemindo" msgstr "O Blueminde" #: data/glade/aboutdialog.glade:16 msgid "A really simple but powerful audio player in Python/PyGTK." msgstr "Skutočne jednoduchý a výkonný prehrávač hudby v Pythone/PyGTK." #: data/glade/mainwindow.glade:17 msgid "_File" msgstr "_Súbor" #: data/glade/mainwindow.glade:57 msgid "_Show" msgstr "_Zobraziť" #: data/glade/mainwindow.glade:65 msgid "_Lightweight" msgstr "Ľ_ahký" #: data/glade/mainwindow.glade:74 msgid "_Basic" msgstr "_Základný" #: data/glade/mainwindow.glade:84 msgid "_Normal" msgstr "_Normálny" #: data/glade/mainwindow.glade:94 msgid "_Full" msgstr "_Plný" #: data/glade/mainwindow.glade:104 msgid "Alb_ums" msgstr "Alb_umy" #: data/glade/mainwindow.glade:114 msgid "_Playlists" msgstr "Z_oznamy skladieb" #: data/glade/mainwindow.glade:124 msgid "_Webradios" msgstr "_Webové rádia" #: data/glade/mainwindow.glade:138 msgid "Fullscreen" msgstr "Celá obrazovka" #: data/glade/mainwindow.glade:150 msgid "Small display (only player)" msgstr "Malé zobrazenie (len prehrávač)" #: data/glade/mainwindow.glade:158 msgid "Display all" msgstr "Zobraziť všetko" #: data/glade/mainwindow.glade:170 msgid "_Help" msgstr "_Pomoc" #: data/glade/mainwindow.glade:317 msgid "Add a new playlist" msgstr "Pridať nový zoznam skladieb" #: data/glade/mainwindow.glade:327 data/glade/mainwindow.glade:1156 msgid "Delete" msgstr "Odstrániť" #: data/glade/mainwindow.glade:345 msgid "Import" msgstr "Importovať" #: data/glade/mainwindow.glade:355 msgid "Export" msgstr "Exportovať" #: data/glade/mainwindow.glade:452 msgid "Group artists" msgstr "Zoskupiť interpretov" #: data/glade/mainwindow.glade:486 data/glade/mainwindow.glade:1075 msgid "Delete filter" msgstr "Odstrániť filter" #: data/glade/mainwindow.glade:751 msgid "Show song lyrics" msgstr "Zobraziť text skladby" #: data/glade/mainwindow.glade:1013 msgid "Clean the playlist" msgstr "Zmazať zoznam skladieb" #: data/glade/mainwindow.glade:1023 msgid "Shuffle the playlist" msgstr "Zamiešať zoznam skladieb" #: data/glade/mainwindow.glade:1034 msgid "Repeat mode" msgstr "Opakovať" #: data/glade/mainwindow.glade:1076 msgid "Cancel filter" msgstr "Zrušiť filter" #: data/glade/mainwindow.glade:1098 msgid "Reload lyrics for this song" msgstr "Znovu načítať text pre túto pesničku" #: data/glade/mainwindow.glade:1108 msgid "" "Save lyrics for this song (this feature allows you to edit lyrics or add " "lyrics for a song)" msgstr "" "Uložiť text tejto skladby (táto funkcia vám umožňuje upraviť alebo pridať " "text pre skladby)" #: data/glade/mainwindow.glade:1146 msgid "Add" msgstr "Pridať" #: data/glade/mainwindow.glade:1166 msgid "Refresh" msgstr "Obnoviť" #: data/glade/mainwindow.glade:1372 msgid "Webradio" msgstr "Webové rádio" #: data/glade/mainwindow.glade:1436 msgid "URL:" msgstr "Adresa:" #: data/glade/mainwindow.glade:1464 msgid "Add a new radio" msgstr "Pridať nové rádio" bluemindo-0.3/debian/control0000644000175000017500000000215311241603412016052 0ustar xbrightxbrightSource: bluemindo Section: sound Priority: extra Maintainer: Thibaut GIRKA Build-Depends: cdbs (>= 0.4.49), debhelper (>= 5), patchutils Build-Depends-Indep: python-support (>= 0.5.3), imagemagick Standards-Version: 3.8.2 Homepage: http://bluemindo.codingteam.net/ Package: bluemindo Architecture: all XB-Python-Version: ${python:Versions} Depends: ${misc:Depends}, ${python:Depends}, python, python-gst0.10, python-gtk2, python-glade2, python-tagpy, gstreamer0.10-plugins-base Recommends: python-dbus, python-notify, python-eggtrayicon Suggests: python-xmpp Description: simple yet powerful audio player Bluemindo is a really simple but powerful audio player in Python/PyGTK, using GStreamer. . With Bluemindo you can: * automatically download lyrics, album-covers, or a picture of the artist, for the current playing song; * choose between five different view modes (lightweight, basic, normal, full or albums); * use plugins; * get desktop notifications (requires python-notify); * send music to your Jabber account (requires python-xmpp) or to your Last.fm profile. bluemindo-0.3/debian/links0000644000175000017500000000007711045403215015516 0ustar xbrightxbright/usr/share/common-licenses/GPL-3 /usr/share/bluemindo/COPYING bluemindo-0.3/debian/dirs0000644000175000017500000000006111045403215015330 0ustar xbrightxbrightusr/bin usr/share/applications usr/share/pixmaps bluemindo-0.3/debian/compat0000644000175000017500000000000211045403215015645 0ustar xbrightxbright5 bluemindo-0.3/debian/menu0000644000175000017500000000023311045403215015334 0ustar xbrightxbright?package(bluemindo):needs="x11" section="Applications/Sound"\ title="bluemindo" command="/usr/bin/bluemindo" \ icon="/usr/share/pixmaps/bluemindo.xpm" bluemindo-0.3/debian/install0000644000175000017500000000005111045403215016034 0ustar xbrightxbrightdebian/bluemindo.xpm /usr/share/pixmaps/ bluemindo-0.3/debian/watch0000644000175000017500000000016411241603412015500 0ustar xbrightxbrightversion=3 http://codingteam.net/project/bluemindo/download \ project/bluemindo/download/file/bluemindo-(.*).tar.gzbluemindo-0.3/debian/changelog0000644000175000017500000000076111241603412016324 0ustar xbrightxbrightbluemindo (0.2.1-2) unstable; urgency=low * Fixed the watch file * Calls idle_add and add_logo the right way. (LP: #261817) * Patches now follow DEP3 * Bumped Standards-Version to 3.8.2 * Changed priority to extra * Improved package description -- Thibaut GIRKA Thu, 02 Jul 2009 14:23:33 +0200 bluemindo (0.2.1-1) unstable; urgency=low * Initial release (Closes: #487572) -- Thibaut GIRKA Sun, 22 Jun 2008 21:36:29 +0200 bluemindo-0.3/debian/copyright0000644000175000017500000000565611241603412016415 0ustar xbrightxbrightThis package was downloaded from http://www.codingteam.net/bluemindo-down.html Upstream Author: Erwan Briand Copyright Holders: Erwan Briand Thomas Julien (for files in data/image) Andy Theyers (for src/libs/audioscrobbler.py) Ľubomír Remák (for locale/sk/LC_MESSAGES/bluemindo.po) Salvatore Tomarchio (for locale/it/LC_MESSAGES/bluemindo.po) Shang Yuanchun <05281253@bjtu.edu.cn> (for locale/zh_CN/LC_MESSAGES/bluemindo.po) Bruno Conde (for locale/pt_BR/LC_MESSAGES/bluemindo.po) Niklas Grahn (for locale/sv/LC_MESSAGES/bluemindo.po) License: This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 3 of the License. This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA The Debian packaging is © 2008, Thibaut GIRKA and is licensed under the terms of the GNU General Public License. On Debian systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL-3'. Format-Specification: http://wiki.debian.org/Proposals/CopyrightFormat Original-Source: http://codingteam.net/project/bluemindo/download Files: debian/* Copyright: © 2008 Thibaut GIRKA License: GPL-3 The Debian packaging information is under the GPL, version 3 Files: * Copyright: © 2007-2009 Erwan Briand License: GPL-3 Files: data/image/* Copyright: © 2008 Thomas Julien License: GPL-3 Files: locale/sk/LC_MESSAGES/bluemindo.po Copyright: © 2008-2009 Ľubomír Remák Files: locale/it/LC_MESSAGES/bluemindo.po Copyright: © 2008 Salvatore Tomarchio Files: locale/zh_CN/LC_MESSAGES/bluemindo.po Copyright: © 2008-2009 Shang Yuanchun <05281253@bjtu.edu.cn> Files: locale/pt_BR/LC_MESSAGES/bluemindo.po Copyright: © 2008 Bruno Conde Files: locale/sv/LC_MESSAGES/bluemindo.po Copyright: © 2008 Niklas Grahn Files: src/plugins/lastfm/audioscrobbler.py Copyright: © Andy Theyers License: GPL-3 It was originally taken from Sonata: http://svn.berlios.de/wsvn/sonata/trunk/audioscrobbler.py bluemindo-0.3/debian/docs0000644000175000017500000000000711045403215015317 0ustar xbrightxbrightREADME bluemindo-0.3/debian/rules0000644000175000017500000000076311121436015015531 0ustar xbrightxbright#!/usr/bin/make -f include /usr/share/cdbs/1/rules/debhelper.mk include /usr/share/cdbs/1/rules/simple-patchsys.mk include /usr/share/cdbs/1/class/makefile.mk DEB_MAKE_INSTALL_TARGET := install DESTDIR="$(DEB_DESTDIR)" DEB_INSTALL_CHANGELOGS_ALL := CHANGELOG cleanbuilddir/bluemindo:: rm -rf locale/*/LC_MESSAGES/bluemindo.mo rm -f debian/bluemindo.xpm build/bluemindo:: convert data/misc/bluemindo.png -scale 32x32 debian/bluemindo.xpm binary-install/bluemindo:: dh_pysupport -pbluemindo bluemindo-0.3/debian/README.source0000644000175000017500000000104411045403215016625 0ustar xbrightxbrightThis package contains bluemindo, along with debian patches. These patches are stored in debian/patches and applied in alphabetical order. To produce the full source of the package in a form that is ready for editing, please run ./debian/rules patch To remove patches that have already been applied, run ./debian/rules reverse-patches To make a new patch, you can use cdbs-edit-patch (in 'cdbs' package): cdbs-edit-patch patch To disable a patch, rename it in a way that does not match the following shell pattern: .{diff,patch}{,.gz,.bz2,.uu} bluemindo-0.3/src/media/gstreamer.py0000644000175000017500000001163411241536535017455 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from urllib import pathname2url from os.path import exists import gst class GStreamer(object): ref = None ref2 = None def __new__(cls, *args, **kws): # Singleton if cls.ref is None: cls.ref = super(GStreamer, cls).__new__(cls, args, kws) return cls.ref def __init__(self): if GStreamer.ref2 is None: GStreamer.ref2 = 42 self.nowplaying = None self.status = 'NULL' self.player = None def set_playback(self, playback): # Gstreamer initialization self.playback = playback try: vol = self.player.get_property('volume') force_volume = True except AttributeError: force_volume = False if self.player is None: self.player = gst.element_factory_make('playbin2', 'bluemindo') bus = self.player.get_bus() bus.add_signal_watch() bus.connect('message', self.on_message) if force_volume: self.player.set_property('volume', vol) def playpause(self, song): # We want to pause the actual song if song == None and self.status == 'PLAYING': self.player.set_state(gst.STATE_PAUSED) self.status = 'PAUSED' return self.status # We want to play the actual song elif song == None and self.status == 'PAUSED': self.player.set_state(gst.STATE_PLAYING) self.status = 'PLAYING' return self.status # Nothing have been done elif song == None and self.status == 'NULL': self.status = 'NULL' return self.status # Huh, we can't do anything elif song == None and self.status == 'STOP': return 42 else: # Launch this song if self.nowplaying == None: self.launch(song) return 'PLAYING' else: # The sended song is already playing: pause it if song == self.nowplaying and self.status == 'PLAYING': self.player.set_state(gst.STATE_PAUSED) self.status = 'PAUSED' return self.status # The sended song is already pausing: play it elif song == self.nowplaying and self.status == 'PAUSED': self.player.set_state(gst.STATE_PLAYING) self.status = 'PLAYING' return self.status # Launch this song else: self.stop() self.launch(song) return 'STOPPED' def stop(self): # Stop listening self.player.set_state(gst.STATE_NULL) self.nowplaying = None self.status = 'STOP' def launch(self, song): self.nowplaying = song # Launch a song by URI if song is not None and exists(song): song = pathname2url(song) self.player.set_property('uri', 'file://' + song) elif song is not None: self.player.set_property('uri', song) self.player.set_state(gst.STATE_PLAYING) self.status = 'PLAYING' def getnow(self): return self.nowplaying def getstatus(self): return self.status def getplayer(self): return self.player def getposition(self): # Return the position in the song return self.player.query_position(gst.FORMAT_TIME)[0] def seek(self, seconds): # Go to a position in the song value = int(gst.SECOND * seconds) self.player.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, value) def on_message(self, bus, message): # Handle Gstreamer messages if self.playback == 'gapless': return _type = message.type if _type == gst.MESSAGE_EOS: self.player.set_state(gst.STATE_NULL) self.status = 'NULL' elif _type == gst.MESSAGE_ERROR: self.player.set_state(gst.STATE_NULL) self.nowplaying = None self.status = 'NULL' err, debug = message.parse_error() print 'Error: %s' % err, debugbluemindo-0.3/src/media/__init__.py0000644000175000017500000000000010723263165017204 0ustar xbrightxbrightbluemindo-0.3/src/mainapplication.py0000644000175000017500000000653611130403236017546 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gobject import io_add_watch, idle_add from socket import (socket, AF_UNIX, SOCK_DGRAM, SOCK_DGRAM, error as socket_error) from gtk.gdk import INPUT_READ from pickle import loads from extensionsloader import ExtensionsLoader from common.config import ConfigLoader from gui.mainwindow import MainWindow extensions = ExtensionsLoader() config = ConfigLoader() class MainApplication(object): def __init__(self): print 'The dolphin reaches the surface!' SOCKET_NAME = '/tmp/bluemindo' self.classes = [] def handle_connection(source, condition): """This is the UNIX socket server.""" datagram = server.recv(1024) argv = loads(datagram) usercommand = argv[1] # PlayPause, Play or Pause if usercommand in ('--playpause', '--play', '--pause'): idle_add(extensions.load_event, 'OnPlayPressed') # Stop elif usercommand == '--stop': idle_add(extensions.load_event, 'OnStopPressed') # Next elif usercommand == '--next': idle_add(extensions.load_event, 'OnNextPressed') # Previous elif usercommand == '--previous': idle_add(extensions.load_event, 'OnPreviousPressed') # More volume elif usercommand.startswith('--volume-more'): self.classes[1].volume_more(usercommand) # Less volume elif usercommand.startswith('--volume-less'): self.classes[1].volume_less(usercommand) # Set the volume elif usercommand.startswith('--volume='): self.classes[1].volume_set(usercommand.split('=')[1]) # Reload songs list elif usercommand == '--reload': idle_add(extensions.load_event, 'OnMenuRefreshPressed', self.widgets) # Quit Bluemindo elif usercommand in ('--quit', '--plunge'): idle_add(self.classes[0].on_menu_quit, 'socket') # The command isn't handled by any action else: print 'Receive unknown event `%s`!' % usercommand return True # Create the UNIX socket server server = socket(AF_UNIX, SOCK_DGRAM) server.bind(SOCKET_NAME) io_add_watch(server, INPUT_READ, handle_connection) # Launch extensions and start user interface extensions.load() gui = MainWindow(extensions) self.classes = gui.get_classes() # Nothing can be done after that gui.start_thread()bluemindo-0.3/src/handlers/__init__.py0000644000175000017500000000000010723263165017725 0ustar xbrightxbrightbluemindo-0.3/src/handlers/main.py0000644000175000017500000000535311145020176017122 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gui.aboutdialog import AboutDialog from gui.extensionsconfig import ExtensionsConfig from common.functions import Functions from modules.explorer.views import Views from modules.explorer import Explorer class MainHandlers: def __init__(self, extensions, config, widgets, gst): self.extensions = extensions self.config = config self.widgets = widgets self.gst = gst self.functions = Functions() self.fullscreen = False def on_menu_fullscreen(self, widget): widget.set_active(not self.fullscreen) if self.fullscreen: self.widgets.get_widget('window1').unfullscreen() else: self.widgets.get_widget('window1').fullscreen() self.fullscreen = not self.fullscreen def on_menu_quit(self, widget): self.extensions.load_event('OnBluemindoQuitted') self.functions.close_bluemindo(self.widgets.get_widget('window1')) def on_window_close(self, widget, event): self.extensions.load_event('OnBluemindoWindowClosed') return True def on_tool_previous(self, widget): self.extensions.load_event('OnPreviousPressed') def on_tool_stop(self, widget): self.extensions.load_event('OnStopPressed') def on_tool_play(self, widget): self.extensions.load_event('OnPlayPressed') def on_tool_next(self, widget): self.extensions.load_event('OnNextPressed') def on_tool_lyrics(self, widget): self.extensions.load_event('OnToolLyricsPressed') def on_tool_reloadlyrics(self, widget): self.extensions.load_event('OnToolReloadLyricsPressed') def on_tool_savelyrics(self, widget): self.extensions.load_event('OnToolSaveLyricsPressed') def on_menu_prefs(self, widget): ExtensionsConfig(self.extensions) def on_menu_refresh(self, widget): config = Explorer(self.extensions).get_configuration() views = Views(glade_file=self.widgets, conf=config, force_reload=True) def on_menu_about(self, widget): AboutDialog()bluemindo-0.3/src/plugins/audioscrobbler/audioscrobbler.png0000644000175000017500000000136511224131121024177 0ustar xbrightxbrightPNG  IHDRw=bKGD pHYs  tIME <IDATH]Ueh:OZh' ;/L!HcD"ETлF ,1Bݢb"'K I=ެ'Q;p{o[R**x <OŹ$ߺ;@L 4p>ɧ @#x4p {N%~g9_lCW{ `>8[c%Q*F5 ~ðjQj?y/vt`&>xC%NobzӨE%CKѶHTl`{=׌QCFcG`7.5,/q ד|[ŏPɋtħ([Õ>В~!v >U؊JR12g g0/%y7»A_1诱z\&7!IDkB0* KE~QKŠR17?h}Z(O t*^[`>֕'f"dz(xoǂRvW=cj8y KEO01% 6`84WXǡz`j}X ч"cmΎ'\ocL}18Y0ۂn}_XXg|Ѱ;YIENDB`bluemindo-0.3/src/plugins/audioscrobbler/configuration.glade0000644000175000017500000001520211224131121024332 0ustar xbrightxbright 400 130 True window1 False True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 3 2 5 True True True True 1 2 2 3 150 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Server: 2 3 GTK_FILL GTK_FILL 150 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Password: 1 2 GTK_FILL GTK_FILL 150 20 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Username: GTK_FILL GTK_FILL True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 2 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False 1 2 1 2 False bluemindo-0.3/src/plugins/audioscrobbler/__init__.py0000755000175000017500000001440511241521161022613 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from threading import Thread from ConfigParser import ConfigParser, NoSectionError from time import time from os.path import join from thread import start_new_thread from time import sleep from common.config import ConfigLoader from plugins.audioscrobbler.audioscrobbler import AudioScrobbler from plugins.audioscrobbler.config import Config class Audioscrobbler: def __init__(self, extensionsloader): self.extensions = extensionsloader self.plugin = {'name': 'Audioscrobbler', 'version': 0.1, 'logo': 'plugins/audioscrobbler/audioscrobbler.png', 'configurable': True, 'authors': 'Erwan Briand ', 'license': 'GNU General Public License 3', 'description': _('Send song notifications')} def start_plugin(self): # Start configuration name = self.plugin['name'].lower() + '.cfg' self.config = ConfigLoader() self.config_file = join(self.config.confdir, 'plugins', name) self.config_gui = Config(self.plugin, self.config_file, self.get_configuration) # Start Last.fm scobbler agent conf = self.get_configuration() self.song_to_scrobble = None self.scrob = audioscrobbler.AudioScrobbler() self.scrob.audioscrobbler_post_host = conf['server'] self.scrob_post = self.scrob.post(conf['username'], conf['password']) def authenticate(): print '[Audioscrobbler] Trying to authenticate account…' try: self.scrob_post.auth() print '[Audioscrobbler] Ok, session opened.' except Exception, error: print '[Audioscrobbler] Error authenticating:\n%s' % str(error) self.scrob_post = None thread = Thread(group=None, target=authenticate, name='scrobblerauth', args=tuple()) thread.start() def loadconfig(args): self.config_gui.configuration(args) def saveconfig(args): self.config_gui.save_config(args) def handler_play_new_song(song=None): """Scrobble the previous song.""" # Get the timestamp timestamp = int(str(time()).split('.')[0]) if self.song_to_scrobble is not None: # Don't send if the song length is inferior to 30 secs real_duration = int(timestamp - self.song_to_scrobble[1]) song_length = self.song_to_scrobble[0][7] if real_duration > (song_length / 2) and real_duration > 30: # Check if user is authentified if self.scrob_post is not None: title = self.song_to_scrobble[0][0] artist =self.song_to_scrobble[0][1] album = self.song_to_scrobble[0][2] track = str(self.song_to_scrobble[0][7]) start = self.song_to_scrobble[1] # Send the song to Last.fm self.scrob_post.addtrack(artist, title, song_length, start, track, album) start_new_thread(self.scrob_post.post, tuple()) # Wait one second if we have to send a now-playing if song is not None: sleep(1) # Add the new song to the queue if song is not None: # If user authentified, we send the now-playing notification if self.scrob_post is not None: title = song[0] artist = song[1] album = song[2] track = str(song[6]) length = str(song[7]) start = str(time()).split('.')[0] start_new_thread(self.scrob_post.nowplaying, (artist, title, length, track, album, start)) # Save the song to be scrobbled in the future self.song_to_scrobble = [song, timestamp] else: self.song_to_scrobble = None # Connect to Bluemindo's signals self.extensions.connect('OnPlayNewSong', handler_play_new_song) self.extensions.connect('OnStopPressed', handler_play_new_song) self.extensions.connect('OnBluemindoQuitted', handler_play_new_song) self.extensions.connect('OnModuleConfiguration', loadconfig) self.extensions.connect('OnModuleConfigurationSave', saveconfig) def stop_plugin(self): self.scrob_post = None def get_configuration(self): configparser = ConfigParser() configparser.read(self.config_file) config = {} try: for item in configparser.items(self.plugin['name']): try: value = int(item[1]) except ValueError: value = str(item[1]) config[item[0]] = value except NoSectionError: config['username'] = '' config['password'] = '' config['server'] = 'post.audioscrobbler.com' return configbluemindo-0.3/src/plugins/audioscrobbler/audioscrobbler.py0000644000175000017500000007017411140506342024057 0ustar xbrightxbright# $Id$ """ ~~~~~~~~~~~ PyScrobbler ~~~~~~~~~~~ Python Classes for AudioScrobbler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A set of python classes for interacting with the Last.fm AudioScrobbler services. AudioScrobblerQuery AudioScrobblerQuery is used to consume the web services that provide similar artists, charts, tags and so on AudioScrobblerPost AudioScrobblerPost is used to update your personal play information for those of you (like me) who've been stoopid enough to write your own player. AudioScrobblerQuery: AudioScrobbler Web Service Consumption ============================================================= A set of classes for consuming the AudioScrobbler web services. Currently we consume protocol 1.0, as defined at: http://www.audioscrobbler.net/data/webservices Basic usage ----------- >>> lips = AudioScrobblerQuery(artist='The Flaming Lips') >>> for artist in lips.similar(): ... print artist.name, artist.mbid ... or >>> for i in AudioScrobblerQuery(user='offmessage').topalbums() ... print i.name, i.image.small ... A certain number of the services take a querystring (for example a user's weekly artist chart takes a start and end date). These can be passed as follows: >>> offmessage = AudioScrobblerQuery(user='offmessage') >>> for i in offmessage.weeklyartistchart(_from=1114965332, to=1115570132): ... print i.name, i.chartposition ... Note that we have had to prefix the ``from`` keyword with an underscore (``_``). This is because ``from`` is a reserved word in Python and can't be used as a parameter name. Any parameter passed with a leading underscore will have that underscore stripped (so ``_from`` is passed as ``from``, as ``_foo`` would be passed as ``foo``). If an element has an attribute (for example the artist has an ``mbid`` in /user/.../topalbums.xml) these can be accessed as a dictionary key of that element: >>> offmessage = AudioScrobblerQuery(user='offmessage') >>> for i in offmessage.topalbums(): ... print i.name, i.artist, i.artist['mbid'] ... if you're not sure if an element has an attribute they can be accessed instead using the ``get()`` method: >>> offmessage = AudioScrobblerQuery(user='offmessage') >>> for i in offmessage.topalbums(): ... print i.name, i.artist, i.artist.get('mbid', '') ... More control ------------ There are 2 additional keyword arguments that you can offer at the point of instantiation of the AudioScrobblerQuery object: host If the lovely people at Last.fm change their server name, or if you set up some kind of local proxy of their services you can set the host from which you get the XML. Usage: >>> lips = AudioScrobblerQuery(artist='The Flaming Lips', ... host='audioscrobbler.isotoma.com') Default is ``'ws.audioscrobbler.com'`` version If another version of the web services is released, and this code still holds up you can set the protocol version at instantiation. This is inserted into the request URL at the correct point (i.e. ws.audioscrobbler.com/**1.0**/artist/The+Flaming+Lips). Usage: >>> lips = AudioScrobberRequest(artist='The Flaming Lips', version='1.1') Default is ``'1.0'`` Caching ------- Each instance of the ``AudioScrobblerQuery`` maintains its own internal cache of requests, which are checked against the server's response. If the server returns a ``304`` (content not modified) we return the cached value, otherwise we return the result that the server gave us. This is all well and good, however at the time of writing the ``ws.audioscrobbler.com`` server does not support the ``304`` response for all pages, so you may find yourself making requests that you are sure should be cached, but in fact aren't. For example: http://ws.audioscrobbler.com/1.0/user/offmessage/topalbums.xml returns a ``304`` http://ws.audioscrobbler.com/1.0/artist/The+Flaming+Lips/similar.xml does not I have assumed that this is for a reason, and as such honour this behaviour. If a page does not return a ``Last-Modified`` header we do not cache it. Any page that does return such a header (but not the ``Pragma: no-cache`` header) gets cached. Any page that does return a ``Last-Modified`` header gets exactly what it sent to us sent back to it as the ``If-Modified-Since``. This is a fit of pragmatism based on the fact that calculating local dates and avoiding clock disparaties is not worth the effort. Also, note that the AudioScrobbler servers do not return the ``ETag`` header and so we do not look for it or refer to it. A last note ----------- These classes are for consuming the AudioScrobbler web services. As such each of the classes here have no ``set``, ``__setitem__`` or ``__setattr__`` methods. They are read only. If you want to actually manipulate the resulting XML you should access the underlying ElementTree_ instances directly using either the ``raw()`` or ``element()`` methods provided. You can then make use of the innate power of those classes directly: ``raw()`` returns an ElementTree: >>> tree = AudioScrobblerQuery(user='offmessage').topalbums().raw() while ``element()`` returns an Element at the point within the tree that you currently are: >>> lips = AudioScrobblerQuery(artist='The Flaming Lips') >>> elements = [ x.name.element() for x in lips.similar() ] which is equivalent to: >>> lips = AudioScrobberRequest(artist='The Flaming Lips') >>> elements = lips.similar().raw().findall('*/name') As an alternative you could also look at XMLTramp_, which is another XML manipulator designed primarily for consuming web services (and used in the FlickrClient_ script that gave me much inspiration for these classes). Last but not least a mention should go to BeautifulSoup_, just because it is the most powerful and flexible of this type of application that I've used. It specialises in badly formed XML/HTML though, and luckily is not required in this instance. That said, it's a powerful beast and you should have a look at it the minute your requirements become more complex than this code supports. .. _ElementTree: http://effbot.org/zone/element.htm .. _XMLTramp: http://www.aaronsw.com/2002/xmltramp/ .. _BeautifulSoup: http://www.crummy.com/software/BeautifulSoup/ AudioScrobblerPost: Update Your Personal Play History ===================================================== A set of classes for posting playlist information to your Last.fm account. Please note that you will need your own account to use these classes. Updated to work with version 1.2 of the Audioscrobbler protocol. Some stuff about the time management and internal cache should go here. Basic usage (outdated) ----------- >>> track = dict(artist_name="The Flaming Lips", song_title="The Yeah Yeah Yeah Song", length=291, date_played="2006-04-29 04:20:00", album="At War With The Mystics", mbid="" ) >>> post = AudioScrobblerPost(user='offmessage', password='mypasswd') >>> post(**track) >>> More control ------------ There are a number of keywords that can be passed at the time of instantiation of the ``AudioScrobblerPost`` class: verbose Boolean. If you pass ``verbose=True`` every log line is also printed. Useful for debugging and in interactive scripts. client_name By default pyscrobbler uses the ``tst`` client name. If you deploy these classes in an application with a wide user base it seems to be advisable to apply for your own client name. If lots of people start to use these classes as is I will apply for a unique name for them. If you have your own, pass it in here (as a string) client_version Currently we use ``1.0``. This is because we are using the ``tst`` client name (see above) and anything lower seems to raise an ``UPDATE`` message from the post server at handshake. protocol_version By default we use ``1.1``. These classes are not compatible with Protocol 1.0. It's unlikely they will be compatible with any later versions. The changes between 1.0 and 1.1 were pretty major; I can't see they'd be any less major for the next version. host By default we use ``post.audioscrobbler.com``. If the server name changes (or you want to run tests) you can pass a different host name here. In fact, the url that we authenticate against is built as ``"http://%s/?%s" % (host, params)`` so as long as your host string makes a valid url in that format, go for it. Note the trailing slash between host and the query string. This seems to be a requirement of urllib2. This means that your test auth script should be able to be called that way. Putting them to use in real life ================================ Some advice on using these classes. ``timeout`` ----------- I've not done anything about the socket timeout on the running system. It's not really this code's place to do anything about it, but it is something that probably needs sorting out, otherwise your scripts will hang for as long as your timeout setting if there is a problem with the Last.fm servers (which there appears to be fairly frequently with the submissions servers). Generally, if you are on a slow link or one with poor latency you can up the timeout so that your requests don't keep failing unnecessarily. However, if you're on a quick link you may want to lower it as low as 10 seconds, as the wait can be agonising if you're making a few requests and the server is not responding. Thanks ====== I took a considerable amount of inspiration from Michael Campeotto's FlickrClient_ script for AudioScrobblerQuery I've taken even greater amounts of inspiration from Mike Bleyer's iPodScrobbler_ script for AudioScrobblerPost. .. _FlickrClient: http://micampe.it/things/flickrclient .. _iPodScrobbler: http://www.hoc.net/mike/source/iPodScrobbler/ TODO ==== Mad leet reStructuredText epydoc mashup Get that sorted. Example query script Actually write some example query scripts :) Document use of the factory class AudioScrobbler """ __author__ = "Andy Theyers " __version__ = "$Revision$"[11:-2] __docformat__ = "restructuredtext" import datetime, locale, hashlib, site, sys, time, urllib, urllib2, ConfigParser, os from xml.etree import ElementTree # This is lifted in the most part from iPodScrobbler (see docs above) # Get the base local encoding enc = 'ascii' try: enc = site.encoding except: pass if enc == 'ascii' and locale.getpreferredencoding(): enc = locale.getpreferredencoding() # if we are on MacOSX, we default to UTF8 # because apples python reports 'ISO8859-1' as locale, but MacOSX uses utf8 if sys.platform=='darwin': enc = 'utf8' # AudioScrobblerQuery configuration settings audioscrobbler_request_version = '1.0' audioscrobbler_request_host = 'ws.audioscrobbler.com' # AudioScrobblerPost configuration settings audioscrobbler_post_version = u'1.2' #updated audioscrobbler_post_host = 'post.audioscrobbler.com' pyscrobbler_version = u'1.0' # Little update for Bluemindo # Please don't use that for your software, but tst as client name and 1.0 as # client version (only for tests purpose: you have to contact Last.fm to obtain # your own client id). client_name = u'blu' client_version = u'trunk' class AudioScrobblerError(Exception): """ The base AudioScrobbler error from which all our other exceptions derive """ def __init__(self, message): """ Create a new AudioScrobblerError. :Parameters: - `message`: The message that will be displayed to the user """ self.message = message def __repr__(self): msg = "%s: %s" return msg % (self.__class__.__name__, self.message,) def __str__(self): return self.__repr__() class AudioScrobblerConnectionError(AudioScrobblerError): """ The base network error, raise by invalid HTTP responses or failures to connect to the server specified (DNS, timeouts, etc.) """ def __init__(self, type, code, message): self.type = type self.code = code self.message = message def __repr__(self): msg = "AudioScrobblerConnectionError: %s: %s %s" return msg % (self.type.upper(), self.code, self.message,) class AudioScrobblerTypeError(AudioScrobblerError): """ If we would normally raise a TypeError we raise this instead """ pass class AudioScrobblerPostUpdate(AudioScrobblerError): """ If the POST server returns an ``UPDATE`` message we raise this exception. This is the only case when this exception is raised, allowing you to happily ignore it. """ def __repr__(self): msg = "An update to your AudioScrobbler client is available at %s" return msg % (self.message,) class AudioScrobblerPostFailed(AudioScrobblerError): """ If the POST server returns an ``FAILED`` message we raise this exception. """ def __repr__(self): msg = "Posting track to AudioScrobbler failed. Reason given: %s" return msg % (self.message,) class AudioScrobblerHandshakeError(AudioScrobblerError): """ If we fail the handshake this is raised. If you're running in a long running process you should pass on this error, as the system keeps a cache which ``flushcache()`` will clear for you when the server is back. """ pass class AudioScrobbler: """ Factory for Queries and Posts. Holds configuration for the session """ def __init__(self, audioscrobbler_request_version=audioscrobbler_request_version, audioscrobbler_post_version=audioscrobbler_post_version, audioscrobbler_request_host=audioscrobbler_request_host, audioscrobbler_post_host=audioscrobbler_post_host, client_name=client_name, client_version=pyscrobbler_version): self.audioscrobbler_request_version=audioscrobbler_request_version self.audioscrobbler_post_version=audioscrobbler_post_version self.audioscrobbler_request_host=audioscrobbler_request_host self.audioscrobbler_post_host=audioscrobbler_post_host self.client_name=client_name self.client_version=pyscrobbler_version def query(self, **kwargs): """ Create a new AudioScrobblerQuery """ if len(kwargs) != 1: raise TypeError("__init__() takes exactly 1 argument, %s " "given" % (len(kwargs),)) ret = AudioScrobblerQuery(version=self.audioscrobbler_request_version, host=self.audioscrobbler_request_host, **kwargs) return ret def post(self, username, md5_password, verbose=False): """ Create a new AudioScrobblerPost """ ret = AudioScrobblerPost(username=username.encode('utf8'), md5_password=md5_password.encode('utf8'), host=self.audioscrobbler_post_host, protocol_version=self.audioscrobbler_post_version, client_name=self.client_name, client_version=self.client_version, verbose=verbose) return ret class AudioScrobblerCache: def __init__(self, elemtree, last): self.elemtree = elemtree self.requestdate = last def created(self): return self.requestdate def gettree(self): return self.elemtree class AudioScrobblerQuery: def __init__(self, version=audioscrobbler_request_version, host=audioscrobbler_request_host, **kwargs): if len(kwargs) != 1: raise TypeError("__init__() takes exactly 1 audioscrobbler " "request argument, %s given" % str(len(kwargs)) ) self.type = kwargs.keys()[0] self.param = str(kwargs[self.type]) self.baseurl = 'http://%s/%s/%s/%s' % (host, version, urllib.quote(self.type), urllib.quote(self.param), ) self._cache = {} def __getattr__(self, name): def method(_self=self, name=name, **params): # Build the URL url = '%s/%s.xml' % (_self.baseurl, urllib.quote(name)) if len(params) != 0: for key in params.keys(): # This little mess is required to get round the fact that # 'from' is a reserved word and can't be passed as a # parameter name. if key.startswith('_'): params[key[1:]] = params[key] del params[key] url = '%s?%s' % (url, urllib.urlencode(params)) req = urllib2.Request(url) # Check the cache cache = _self._cache if url in cache and cache[url].created() is not None: req.add_header('If-Modified-Since', cache[url].created()) # Open the URL and test the response try: response = urllib2.urlopen(url) except urllib2.HTTPError, error: if error.code == 304: return AudioScrobblerItem(cache[url].getroot(), _self, url) if error.code == 400: raise AudioScrobblerConnectionError('ws', 400, error.fp.read()) raise AudioScrobblerConnectionError('http', error.code, error.msg) except urllib2.URLError, error: code = error.reason.args[0] message = error.reason.args[1] raise AudioScrobblerConnectionError('network', code, message) elemtree = ElementTree.ElementTree(file=response) if response.headers.get('pragma', None) != 'no-cache': last_modified = response.headers.get('last-modified', None) else: last_modified = None _self._cache[url] = AudioScrobblerCache(elemtree, last_modified) return AudioScrobblerItem(elemtree.getroot(), _self, url) return method def __repr__(self): return "AudioScrobblerQuery(%s='%s')" % (self.type, self.param) def __str__(self): return self.__repr__() class AudioScrobblerItem: def __init__(self, element, parent, url=None): self._element = element self._parent = parent self.tag = element.tag self.text = element.text self._url = url if self._url is None: self._url = self._parent._url def __repr__(self): if isinstance(self._parent, AudioScrobblerQuery): return "" % (self._url,) return "" % (self.tag, id(self)) def __str__(self): if self.text is None: return '' text = self.text try: retval = text.encode(enc) except AttributeError: retval = text return retval def __getattr__(self, name): result = self._element.findall(name) if len(result) == 0: raise AttributeError("AudioScrobbler %s element has no " "subelement '%s'" % (self.tag, name) ) ret = [ AudioScrobblerItem(i, self) for i in result ] if len(ret) == 1: return ret[0] return ret def __iter__(self): for i in self._element: yield AudioScrobblerItem(i, self) def __getitem__(self, i): return self._element.attrib[i] def get(self, i, default): if i in self._element.attrib: return self._element.attrib[i] else: return default def __getslice__(self, i, j): return [ AudioScrobblerItem(x, self) for x in self._element[i:j] ] def raw(self): def getparent(obj): if isinstance(obj._parent, AudioScrobblerQuery): return obj._parent return getparent(obj._parent) return getparent(self)._cache[self._url].gettree() def element(self): return self._element class AudioScrobblerPost: """ Provide the ability to post tracks played to a user's Last.fm account """ #Updated to protocol version 1.2 def __init__(self, username=u'', md5_password=u'', client_name=client_name, client_version=pyscrobbler_version, protocol_version=audioscrobbler_post_version, host=audioscrobbler_post_host, verbose=False): # Capture the information passed for future use self.params = dict(username=username, md5_password=md5_password, client_name=client_name, client_version=client_version, protocol_version=protocol_version, host=host) self.verbose = verbose self.auth_details = {} self.last_post = None self.interval = 0 self.cache = [] self.loglines = [] self.last_shake = None self.authenticated = False self.updateurl = None self.posturl = None self.npurl = None def auth(self): """ Authenticate against the server """ if self.authenticated: return True timestamp = str(int(time.time())) md5_password = self.params['md5_password'] auth_token = hashlib.md5(md5_password + timestamp).hexdigest() p = {} p['hs'] = 'true' p['p'] = self.params['protocol_version'] p['c'] = self.params['client_name'] p['v'] = self.params['client_version'] p['u'] = self.params['username'] p['t'] = timestamp p['a'] = auth_token plist = [(k, urllib.quote_plus(v.encode('utf8'))) for k, v in p.items()] authparams = urllib.urlencode(plist) url = 'http://%s/?%s' % (self.params['host'], authparams) req = urllib2.Request(url) try: url_handle = urllib2.urlopen(req) except urllib2.HTTPError, error: self.authenticated = False raise AudioScrobblerConnectionError('http', error.code, error.msg) except urllib2.URLError, error: self.authenticated = False code = error.reason#.args[0] message = error.reason#.args[1] raise AudioScrobblerConnectionError('network', code, message) response = url_handle.readlines() if len(response) == 0: raise AudioScrobblerHandshakeError('Got nothing back from the server') username = self.params['username'] md5_password = self.params['md5_password'] # First we test the best and most likely case if response[0].startswith('OK'): answer = response[1].strip() self.auth_details['s'] = answer self.npurl = response[2].strip() self.posturl = response[3].strip() self.authenticated = True # Then the various failure states.... elif response[0].startswith('BANNED'): self.authenticated = False msg = "this client version has been banned from the server." raise AudioScrobblerHandshakeError(msg) elif response[0].startswith('BADAUTH'): self.authenticated = False msg = "The authentication details provided were incorrect." raise AudioScrobblerHandshakeError(msg) elif response[0].startswith('BADTIME'): self.authenticated = False msg = "The timestamp provided was not close enough to the current time." raise AudioScrobblerHandshakeError(msg) elif response[0].startswith('FAILED'): self.authenticated = False reason = response[0][6:] try: msg = reason.decode('utf8').encode(enc) except: msg = reason raise AudioScrobblerHandshakeError(msg) else: self.authenticated = False msg = "Unknown response from server: %r" % (response,) raise AudioScrobblerHandshakeError(msg) def posttrack(self, artist_name, song_title, length, date_played, tracknumber=u'', album=u'', mbid=u'', source=u'P'): """ Add the track to the local cache, and try to post it to the server """ self.addtrack(artist_name=artist_name, song_title=song_title, length=length, date_played=date_played, tracknumber=tracknumber, album=album, mbid=mbid, source=source) self.post() __call__ = posttrack def addtrack(self, artist_name, song_title, length, date_played, tracknumber=u'', album=u'', mbid=u'', source=u'P'): """ Add a track to the local cache """ # Quick sanity check on track length if type(length) == type(0): sane_length = length elif type(length) == type('') and length.isdigit(): sane_length = int(length) else: sane_length = -1 # If the track length is less than 30 move on if sane_length < 30: msg = ("Track '%s' by '%s' only %s seconds in length, so " "not added" % (song_title, artist_name, sane_length)) self.log(msg) # Otherwise build the track dictionary and add it to the local cache else: try: track = {'a[%s]': artist_name.encode('utf8'), 't[%s]': song_title.encode('utf8'), 'l[%s]': str(sane_length), 'i[%s]': date_played, 'b[%s]': album.encode('utf8'), 'm[%s]': mbid.encode('utf8'), 'r[%s]': u'', 'n[%s]': tracknumber.encode('utf8'), 'o[%s]': source.encode('utf8'), } except: track = {'a[%s]': artist_name, 't[%s]': song_title, 'l[%s]': str(sane_length), 'i[%s]': date_played, 'b[%s]': album, 'm[%s]': mbid.encode('utf8'), 'r[%s]': u'', 'n[%s]': tracknumber.encode('utf8'), 'o[%s]': source.encode('utf8'), } self.cache.append(track) def nowplaying(self, artist_name, song_title, length=u'', tracknumber=u'', album=u'', mbid=u''): self.auth() p = {} p['s'] = self.auth_details['s'] try: p['a'] = artist_name.encode('utf8') p['t'] = song_title.encode('utf8') p['b'] = album.encode('utf8') p['l'] = length.encode('utf8') p['n'] = tracknumber.encode('utf8') p['m'] = mbid.encode('utf8') except: p['a'] = artist_name p['t'] = song_title p['b'] = album p['l'] = length.encode('utf8') p['n'] = tracknumber.encode('utf8') p['m'] = mbid.encode('utf8') npdata = urllib.urlencode(p) req = urllib2.Request(url=self.npurl, data=npdata) try: url_handle = urllib2.urlopen(req) except urllib2.HTTPError, error: self.authenticated = False print AudioScrobblerConnectionError('http', error.code, error.msg) return except: code = '000' message = sys.exc_info()[1] print AudioScrobblerConnectionError('network', code, message) return response = url_handle.readlines() if response[0].startswith('OK'): self.log("Now playing track updated ('%s' by '%s')." % (p['t'], p['a'])) elif response[0].startswith('BADSESSION'): self.authenticated = False def post(self): """ Post the tracks by popping the first ten off the cache and attempting to post them. """ if len(self.cache) == 0: return if len(self.cache) > 10: number = 10 else: number = len(self.cache) params = {} count = 0 for track in self.cache[:number]: for k in track.keys(): params[k % (count,)] = track[k] count += 1 self.auth() params.update(self.auth_details) postdata = urllib.urlencode(params) req = urllib2.Request(url=self.posturl, data=postdata) now = datetime.datetime.utcnow() try: url_handle = urllib2.urlopen(req) except urllib2.HTTPError, error: raise AudioScrobblerConnectionError('http', error.code, error.msg) except urllib2.URLError, error: args = getattr(error.reason, 'args', None) code = '000' message = str(error) if args is not None: if len(args) == 1: message = error.reason.args[0] elif len(args) == 2: code = error.reason.args[0] message = error.reason.args[1] raise AudioScrobblerConnectionError('network', code, message) except: code = '000' message = sys.exc_info()[1] raise AudioScrobblerConnectionError('network', code, message) self.last_post = now response = url_handle.readlines() # Test the various responses possibilities: if response[0].startswith('OK'): for song in self.cache[:number]: self.log("Uploaded track successfully ('%s' by '%s')." % (song['t[%s]'], song['a[%s]'])) del self.cache[:number] elif response[0].startswith('BADSESSION'): self.log("Got BADSESSION.") self.authenticated = False elif response[0].startswith('FAILED'): reason = response[0][6:].strip() raise AudioScrobblerPostFailed(reason) else: msg = "Server returned something unexpected." raise AudioScrobblerPostFailed(msg) def flushcache(self): """ Post all the tracks in the cache """ while len(self.cache) > 0: self.post() def savecache(self, filename): """ Save all tracks in the cache to a file """ conf = ConfigParser.ConfigParser() # Save each track in cache: count = 0 for track in self.cache: conf.add_section('Track ' + str(count)) for k in track.keys(): conf.set('Track ' + str(count), k, track[k]) count += 1 conf.write(file(filename, 'w')) def retrievecache(self, filename): """ Retrieve all cached tracks from a file so that cached tracks are preserved across client restarts """ if not os.path.isfile(filename): return conf = ConfigParser.ConfigParser() conf.read(filename) # Retrieve each cached track from file: count = 0 while conf.has_section('Track ' + str(count)): track = {} for key in ['a','t','l','i','b','m','r','n','o']: if conf.has_option('Track ' + str(count), key + '[%s]'): track[key + '[%s]'] = conf.get('Track ' + str(count), key + '[%s]') self.cache.append(track) count += 1 # Cached tracks retrieved, delete file: os.remove(filename) def log(self, msg): """ Add a line to the log, print it if verbose """ time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") self.loglines.append("%s: %s" % (time, msg)) if self.verbose: print self.loglines[-1] def getlog(self, clear=False): """ Return the entire log, clear it if requested """ if clear: retval = self.loglines self.loglines = [] return retval return self.loglines bluemindo-0.3/src/plugins/audioscrobbler/config.py0000644000175000017500000000763511224131121022317 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from gtk.glade import XML as glade_XML from gtk import ListStore from ConfigParser import ConfigParser, DuplicateSectionError from md5 import md5 from os.path import join from os import getcwd class Config: def __init__(self, plugin, config_file, get_configuration): self.plugin = plugin self.config_file = config_file self.get_configuration = get_configuration def save_config(self, name): if name == self.plugin['name']: entry_username = self.conf_widgets.get_widget('entry1') entry_password = self.conf_widgets.get_widget('entry2') combobox = self.conf_widgets.get_widget('comboboxentry1') username = entry_username.get_text() password = entry_password.get_text() md5_password = md5(password).hexdigest() it = combobox.get_active_iter() md = combobox.get_model() try: server = md[it][1] except TypeError: server = combobox.get_active_text() configparser = ConfigParser() configparser.read(self.config_file) try: configparser.add_section(self.plugin['name']) except DuplicateSectionError: pass configparser.set(self.plugin['name'], 'username', username) # Change the password only if the user set a password if password != '': configparser.set(self.plugin['name'], 'password', md5_password) configparser.set(self.plugin['name'], 'server', server) configparser.write(open(self.config_file, 'w')) def configuration(self, args): (name, widgets) = args if name == self.plugin['name']: # Load the glade and put the vertical box in the module's # configuration one config = self.get_configuration() self.conf_widgets = glade_XML(join(getcwd(), 'plugins', 'audioscrobbler', 'configuration.glade'), 'vbox1', domain='bluemindo') hbox = widgets.get_widget('hbox2') try: kids = hbox.get_children() hbox.remove(kids[2]) except: pass hbox.add(self.conf_widgets.get_widget('vbox1')) entry_username = self.conf_widgets.get_widget('entry1') entry_password = self.conf_widgets.get_widget('entry2') entry_username.set_text(config['username']) # We can't show the password because we hashed it in md5. ;-) combobox = self.conf_widgets.get_widget('comboboxentry1') lstore = ListStore(str, str) known_servers = ['post.audioscrobbler.com', 'turtle.libre.fm'] if config['server'] not in known_servers: known_servers.append(config['server']) iter_servers = {} for server in known_servers: it = lstore.append(('as', server)) iter_servers[server] = it combobox.set_model(lstore) combobox.set_text_column(1) combobox.set_active_iter(iter_servers[config['server']])bluemindo-0.3/src/plugins/jabber/jabber_small.png0000644000175000017500000000246611020337174022060 0ustar xbrightxbrightPNG  IHDRw=sRGB pHYs  tIME ( IDATHǍ{hUwiӦ/"AQ7W "wU|"XL!"Q&ʄZG'Uj>/l6;{#[\sD=}~gTԼ[J7sXH[&?)Laz0mǧS'%U`7MV\JQBWSUv81T;fVQ'H$^ox<,T S12>TӖ[*eg loow\Rlv_"ĢVspYMȢ\!woV|9|8G z|mB_/Ò=4*a`%k'D:7;NFrB&p cUN3P4&UXy U;U:E\Hc(BLAs ZSC#b B<aE`+ L+GAmÏ Ld#t`C'$#FQFpЀ"njV"W08=W 'uV]uKeRt(Pm{L^εϪSh }7™u-ūX;[O:F@6qc& ĩ|Ҫw t+Wy4UU'qT|&]B86LSOʈZP#؛UL7ݱq* xasn*Uk-9kCz9 H? w׽&o% }}8B@?bI{I>GaY.*,h,z*pEEw mgz6OGN܀PmEKy1-adm yj=+՘ )eVN9cb{1 BnM4]zʜ BBQ36O3f*Th$ijZ6?o_Q*衧n4 vL*d2~7-&4pK;6oׇ3kj 5LAq\*xoRA`V/AiwNOj ~҆ wwxڡ]2iofk*G!&` 400 130 True window1 False True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 2 5 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False 1 2 1 2 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 2 150 20 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Jabber identifiant: GTK_FILL GTK_FILL 150 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Password: 1 2 GTK_FILL GTK_FILL False bluemindo-0.3/src/plugins/jabber/__init__.py0000755000175000017500000002104611224133427021045 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from gtk import STOCK_DIALOG_INFO from ConfigParser import ConfigParser, NoSectionError from base64 import b64decode from thread import start_new_thread from os.path import join from time import sleep try: from xmpp import NS_PUBSUB, NS_PRIVACY, Node, Iq, Client, Presence NS_TUNE = 'http://jabber.org/protocol/tune' xmpp_imported = True except ImportError: xmpp_imported = False from common.config import ConfigLoader from plugins.jabber.config import Config class Jabber: def __init__(self, extensionsloader): self.extensions = extensionsloader self.plugin = {'name': 'Jabber', 'version': 0.2, 'logo': 'plugins/jabber/jabber_small.png', 'configurable': True, 'authors': 'Erwan Briand ', 'license': 'GNU General Public License 3', 'description': _('Send PEP notifications')} def start_plugin(self): # python-xmpp is an optional depends if not xmpp_imported: self.stop_plugin() return # Start configuration name = self.plugin['name'].lower() + '.cfg' self.config = ConfigLoader() self.config_file = join(self.config.confdir, 'plugins', name) self.config_gui = Config(self.plugin, self.config_file, self.get_configuration) # Start a new Jabber client config = self.get_configuration() # Get Jabber account self.is_jabber_account = False if config['username'] != '' and config['password'] != '': self.jid = config['username'] pwd = b64decode(config['password']) try: user = self.jid.split('@')[0] server = self.jid.split('@')[1] except IndexError: return # Start the connection to the Jabber server self.connection = Client(server, debug=[]) self.connection.RegisterDisconnectHandler( self.connection.reconnectAndReauth) def connect_to_account(server, user, pwd): """Connect to the Jabber account of the user.""" if not self.connection.connect(): raise Exception('Unable to connect to %s' % server) if not self.connection.auth(user=user, password=pwd, resource='Bluemindo'): raise Exception('Unable to authenticate as %s' % self.jid) self.is_jabber_account = True # Use an invisible list (Omega, I love you <3) if not self.check_invisible_list(): self.create_invisible_list() self.activate_invisible_list() self.connection.send(Presence(priority=-42)) while 1: try: self.connection.Process(5) except: exit(0) # Thread the conection start_new_thread(connect_to_account, (server, user, pwd)) def loadconfig(args): self.config_gui.configuration(args) def saveconfig(args): self.config_gui.save_config(args) def load_new_song(song): title = song[0] artist = song[1] album = song[2] track = song[6] length = song[7] if self.is_jabber_account: # Test if connection is active, if not, reconnect and reauth if self.connection.isConnected() is None: self.connection.reconnectAndReauth() # Create the PEP stanza item = Node('tune', {'xmlns': NS_TUNE}) itemchild = item.addChild('artist') itemchild.addData(artist) itemchild = item.addChild('source') itemchild.addData(album) itemchild = item.addChild('title') itemchild.addData(title) itemchild = item.addChild('track') itemchild.addData(track) itemchild = item.addChild('length') itemchild.addData(length) query = Iq('set', to=self.jid) querychild = query.addChild('pubsub', namespace=NS_PUBSUB) querychild = querychild.addChild('publish', {'node': NS_TUNE}) querychild = querychild.addChild('item', {}, [item]) # Send the PEP notification self.connection.send(query) # Connect to Bluemindo's signals self.extensions.connect('OnPlayNewSong', load_new_song) self.extensions.connect('OnStopPressed', self.stop_publish_anything) self.extensions.connect('OnBluemindoQuitted', self.quit_jabber_account) self.extensions.connect('OnModuleConfiguration', loadconfig) self.extensions.connect('OnModuleConfigurationSave', saveconfig) def stop_publish_anything(self): """Send empty PEP notification.""" iq = Iq(typ='set') iq.pubsub = iq.addChild('pubsub', namespace=NS_PUBSUB) iq.pubsub.publish = iq.pubsub.addChild('publish', attrs={'node': NS_TUNE}) iq.pubsub.publish.item = iq.pubsub.publish.addChild('item', attrs={'id': 'current'}) tune = iq.pubsub.publish.item.addChild('tune') tune.setNamespace(NS_TUNE) self.connection.send(iq) def quit_jabber_account(self): """Quit Bluemindo, so disconnect from Jabber network.""" if self.is_jabber_account: self.is_jabber_account = False self.stop_publish_anything() self.connection.send(Presence(frm=self.jid, typ='unavailable')) sleep(1) del self.connection def check_invisible_list(self): """Return TRUE if this account has a privacy list for invisibility.""" has_privacy = False request = Iq(typ='get') request.addChild('query', namespace=NS_PRIVACY) query = self.connection.SendAndWaitForResponse(request, timeout=25) children = query.getQueryChildren() if children is not None: for child in children: if (child.getName() == 'list' and child.getAttr('name') == 'bluemindo'): has_privacy = True return has_privacy def create_invisible_list(self): """Create an invisible list.""" iq = Iq(typ='set') iq.query = iq.addChild('query', namespace=NS_PRIVACY) iq.query.list = iq.query.addChild('list', attrs={'name': 'bluemindo'}) iq.query.list.item = iq.query.list.addChild('item', attrs={ 'action': 'deny', 'order': '1'}) iq.query.list.item.addChild('presence-out') self.connection.send(iq) def activate_invisible_list(self): """Activate the invisible list.""" iq = Iq(typ='set') iq.query = iq.addChild('query', namespace=NS_PRIVACY) iq.query.addChild('active', attrs={'name': 'bluemindo'}) self.connection.send(iq) def stop_plugin(self): self.quit_jabber_account() def get_configuration(self): configparser = ConfigParser() configparser.read(self.config_file) config = {} try: for item in configparser.items(self.plugin['name']): try: value = int(item[1]) except ValueError: value = str(item[1]) config[item[0]] = value except NoSectionError: config['username'] = '' config['password'] = '' return configbluemindo-0.3/src/plugins/jabber/config.py0000644000175000017500000000545111140535673020560 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from gtk.glade import XML as glade_XML from ConfigParser import ConfigParser, DuplicateSectionError from base64 import b64encode, b64decode from os.path import join from os import getcwd class Config: def __init__(self, plugin, config_file, get_configuration): self.plugin = plugin self.config_file = config_file self.get_configuration = get_configuration def save_config(self, name): if name == self.plugin['name']: entry_username = self.conf_widgets.get_widget('entry1') entry_password =self.conf_widgets.get_widget('entry2') username = entry_username.get_text() password = b64encode(entry_password.get_text()) configparser = ConfigParser() configparser.read(self.config_file) try: configparser.add_section(self.plugin['name']) except DuplicateSectionError: pass configparser.set(self.plugin['name'], 'username', username) configparser.set(self.plugin['name'], 'password', password) configparser.write(open(self.config_file, 'w')) def configuration(self, args): (name, widgets) = args if name == self.plugin['name']: # Load the glade and put the vertical box in the module's # configuration one config = self.get_configuration() self.conf_widgets = glade_XML(join(getcwd(), 'plugins', 'jabber', 'configuration.glade'), 'vbox1', domain='bluemindo') hbox = widgets.get_widget('hbox2') try: kids = hbox.get_children() hbox.remove(kids[2]) except: pass hbox.add(self.conf_widgets.get_widget('vbox1')) entry_username = self.conf_widgets.get_widget('entry1') entry_password =self.conf_widgets.get_widget('entry2') entry_username.set_text(config['username']) entry_password.set_text(b64decode(config['password']))bluemindo-0.3/src/plugins/notification/notify.py0000644000175000017500000001045211225217611022052 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gobject import markup_escape_text as escape from gtk.gdk import pixbuf_new_from_file, INTERP_BILINEAR from gtk import STOCK_DIALOG_INFO from os.path import isfile, join try: from pynotify import init as pynotify_init, URGENCY_LOW, Notification pynotify_imported = True except ImportError: pynotify_imported = False from common.functions import Functions functions = Functions() hlength = functions.human_length class Notify: def __init__(self, config): self.config = config # pynotify is an optional depends if not pynotify_imported: self.is_activated = False print '[Notify plugin] You need python-notify.' return # Start pynotify self.notif = None self.tray = None pynotify_init('bluemindo') # Fake stopped if plugin is desactivated during execution self.is_activated = True def _reload_conf(self, newconf): """Reload the configuration of the plugin.""" del self.config self.config = newconf def _stop_plugin(self): """Fake the unload of the plugin.""" self.is_activated = False def _create_notification(self, title, text, icon): """Create or update a notification and show it.""" # Quit if plugin has been desactivated if not self.is_activated: return if self.notif: if icon: self.notif.update(title, text) else: self.notif.update(title, text, STOCK_DIALOG_INFO) else: if icon: self.notif = Notification(title, text, None) else: self.notif = Notification(title, text, STOCK_DIALOG_INFO) if self.tray is not None: try: self.notif.attach_to_widget(self.tray) except TypeError: self.notif.attach_to_status_icon(self.tray) self.notif.set_urgency(URGENCY_LOW) self.notif.set_timeout(self.config['timeout']) if icon: self.notif.set_icon_from_pixbuf(icon) self.notif.show() def _get_tray(self, tray): """Get the trayicon.""" self.tray = tray def load_new_song(self, song): """Handler for a new playing song.""" title = song[0] artist = song[1] album = song[2] comment = song[3] genre = song[4] year = song[5] track = song[6] length = song[7] def parse(string): string = string.replace('{title}', title) string = string.replace('{artist}', escape(artist)) string = string.replace('{album}', escape(album)) string = string.replace('{comment}', escape(comment)) string = string.replace('{genre}', escape(genre)) string = string.replace('{year}', str(year)) string = string.replace('{track}', str(track)) string = string.replace('{length}', hlength(length)) return string album_file = join(self.config['__player-data'], 'covers', functions.get_hash(album, artist)) if isfile(album_file): icon = pixbuf_new_from_file(album_file) icon = icon.scale_simple(50, 50, INTERP_BILINEAR) else: icon = None notif_title = parse(self.config['title']) notif_text = parse(self.config['text']) self._create_notification(notif_title, notif_text, icon) def show(self): """Force notification.""" if self.notif is not None: self.notif.show()bluemindo-0.3/src/plugins/notification/configuration.glade0000644000175000017500000002274111131115224024032 0ustar xbrightxbright 400 300 True window1 False True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 3 2 5 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 2 150 20 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Title: GTK_FILL GTK_FILL True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 50 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK GTK_POLICY_NEVER GTK_POLICY_AUTOMATIC GTK_SHADOW_IN True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 2 1 2 150 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 0 Text: 1 2 GTK_FILL GTK_FILL True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True 0 0 20000 1000 0 0 40 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <i>ms</i> True False 1 1 2 2 3 150 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Timeout: 2 3 GTK_FILL GTK_FILL True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 label True 5 1 False bluemindo-0.3/src/plugins/notification/__init__.py0000644000175000017500000000634411223756731022317 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from gtk import STOCK_DIALOG_INFO from ConfigParser import ConfigParser, NoSectionError from os.path import join from common.config import ConfigLoader from plugins.notification.notify import Notify from plugins.notification.config import Config class Notification: def __init__(self, extensionsloader): self.extensions = extensionsloader self.plugin = {'name': 'Notification', 'version': 0.2, 'logo': STOCK_DIALOG_INFO, 'configurable': True, 'authors': 'Erwan Briand ', 'license': 'GNU General Public License 3', 'description': _('Show desktop notification')} def start_plugin(self): # Start configuration name = self.plugin['name'].lower() + '.cfg' self.config = ConfigLoader() self.config_file = join(self.config.confdir, 'plugins', name) self.player_dir = join(self.config.datadir, 'modules', 'player') # Start notification agent config = self.get_configuration() self.notify = Notify(config) self.config_gui = Config(self.plugin, self.notify, self.config_file,self.get_configuration) def loadconfig(args): self.config_gui.configuration(args) def saveconfig(args): self.config_gui.save_config(args) # Connect to Bluemindo's signals self.extensions.connect('OnTrayIconAnswer', self.notify._get_tray) self.extensions.connect('OnPlayNewSong', self.notify.load_new_song) self.extensions.connect('OnNotificationRequest', self.notify.show) self.extensions.connect('OnModuleConfiguration', loadconfig) self.extensions.connect('OnModuleConfigurationSave', saveconfig) def stop_plugin(self): self.notify._stop_plugin() def get_configuration(self): configparser = ConfigParser() configparser.read(self.config_file) config = {} try: for item in configparser.items(self.plugin['name']): try: value = int(item[1]) except ValueError: value = str(item[1]) config[item[0]] = value except NoSectionError: config['title'] = '{title}' config['text'] = '{artist}\n{album}' config['timeout'] = 10000 config['__player-data'] = self.player_dir return configbluemindo-0.3/src/plugins/notification/config.py0000644000175000017500000000760611131115224022007 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from gtk.glade import XML as glade_XML from gtk import WRAP_WORD from ConfigParser import ConfigParser, DuplicateSectionError from os.path import join from os import getcwd class Config: def __init__(self, plugin, notify, config_file, get_configuration): self.plugin = plugin self.notify = notify self.config_file = config_file self.get_configuration = get_configuration def save_config(self, name): if name == self.plugin['name']: entry = self.conf_widgets.get_widget('entry1') tview = self.conf_widgets.get_widget('textview1') spbutton = self.conf_widgets.get_widget('spinbutton1') title = entry.get_text() tbuff = tview.get_buffer() text = tbuff.get_text(tbuff.get_start_iter(), tbuff.get_end_iter()) timeout = int(spbutton.get_value()) configparser = ConfigParser() configparser.read(self.config_file) try: configparser.add_section(self.plugin['name']) except DuplicateSectionError: pass configparser.set(self.plugin['name'], 'title', title) configparser.set(self.plugin['name'], 'text', text) configparser.set(self.plugin['name'], 'timeout', timeout) configparser.write(open(self.config_file, 'w')) # Reload configuration newconf = self.get_configuration() self.notify._reload_conf(newconf) def configuration(self, args): (name, widgets) = args if name == self.plugin['name']: # Load the glade and put the vertical box in the module's # configuration one config = self.get_configuration() self.conf_widgets = glade_XML(join(getcwd(), 'plugins', 'notification', 'configuration.glade'), 'vbox1', domain='bluemindo') hbox = widgets.get_widget('hbox2') try: kids = hbox.get_children() hbox.remove(kids[2]) except: pass hbox.add(self.conf_widgets.get_widget('vbox1')) entry = self.conf_widgets.get_widget('entry1') tview = self.conf_widgets.get_widget('textview1') spbutton = self.conf_widgets.get_widget('spinbutton1') label = self.conf_widgets.get_widget('label6') label.set_markup(_('Available tags:\n' '{title}\t\tInsert the title\n' '{artist}\tInsert the artist\n' '{album}\t\tInsert the album\n' '{comment}\tInsert the comment\n' '{genre}\t\tInsert the genre\n' '{year}\t\tInsert the year\n' '{track}\t\tInsert the track number\n' '{length}\tInsert the length')) spbutton.set_value(float(config['timeout'])) entry.set_text(config['title']) tview.set_wrap_mode(WRAP_WORD) tbuff = tview.get_buffer() tbuff.set_text(config['text'])bluemindo-0.3/src/plugins/__init__.py0000644000175000017500000000000011020337174017577 0ustar xbrightxbrightbluemindo-0.3/src/gui/aboutdialog.py0000644000175000017500000000301211141436747017455 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from webbrowser import open as open_ from gtk.glade import XML as glade_XML from gtk import about_dialog_set_url_hook from gtk.gdk import pixbuf_new_from_file from common.functions import Functions functions = Functions() class AboutDialog: def __init__(self): about_dialog_set_url_hook(self.on_aboutdialog_link, None) xml = glade_XML(functions.datadir + '/glade/aboutdialog.glade', 'aboutdialog1', domain='bluemindo') aboutdialog = xml.get_widget('aboutdialog1') bluemindologo = pixbuf_new_from_file( functions.datadir + '/image/bluemindo.png') aboutdialog.set_logo(bluemindologo) aboutdialog.run() aboutdialog.destroy() def on_aboutdialog_link(self, widget, link, _): open_(link)bluemindo-0.3/src/gui/gnomemultimediakeys.py0000644000175000017500000000572611141436747021255 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from os.path import isfile, join try: from dbus import SessionBus as dbus_session, Interface as dbus_interface from dbus.exceptions import DBusException dbus_imported = True except: dbus_imported = False class GnomeMultimediaKeys(object): def __init__(self, extensions): gnome_keybinding = join('usr', 'bin', 'gnome-keybinding-properties') # Start only if dbus is properly imported and gnome-keybinding detected if dbus_imported and isfile(gnome_keybinding): try: bus = dbus_session() application = 'bluemindo' def on_dbus_key(app, key): if app == application: if key in ('Play', 'PlayPause'): extensions.load_event('OnPlayPressed') elif key == 'Next': extensions.load_event('OnNextPressed') elif key == 'Previous': extensions.load_event('OnPreviousPressed') elif key == 'Stop': extensions.load_event('OnStopPressed') # Old GNOME API try: obj = bus.get_object('org.g.SettingsDaemon', '/org/gnome/SettingsDaemon') dbi = dbus_interface(obj, 'org.gnome.SettingsDaemon') dbi.GrabMediaPlayerKeys(application, 0) dbi.connect_to_signal('MediaPlayerKeyPressed', on_dbus_key) # Newer GNOME API except DBusException: obj = bus.get_object('org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys') dbi = dbus_interface(obj, 'org.gnome.SettingsDaemon.MediaKeys') dbi.GrabMediaPlayerKeys(application, 0) dbi.connect_to_signal('MediaPlayerKeyPressed', on_dbus_key) except DBusException, error: print ('\nDBus error!\nThis error is related to GNOME ' 'multimedia keys and can be ignored if you do not want ' 'to use them.\n-----------\n%s' % error)bluemindo-0.3/src/gui/mainwindow.py0000644000175000017500000001350711150355570017343 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from common.functions import Functions functions = Functions() from gtk.glade import bindtextdomain as gtk_glade_bindtextdomain gtk_glade_bindtextdomain('bluemindo', functions.localedir) from gobject import threads_init from gtk.gdk import pixbuf_new_from_file from gtk.glade import XML as glade_XML from gtk import (main as gtk_main, window_set_default_icon_list, pygtk_version, gtk_version, image_new_from_file) from os.path import join from os import getcwd # Raise if GTK is not up-to-date if pygtk_version < (2, 12) or gtk_version < (2, 12): raise ImportError, 'Bluemindo needs GTK >= 2.12' from media.gstreamer import GStreamer from common.config import ConfigLoader from handlers import main as hmain from gui.volumemanagement import VolumeManagement from gui.gnomemultimediakeys import GnomeMultimediaKeys config = ConfigLoader() gst = GStreamer() class MainWindow(object): def __init__(self, extensions): # Start threads threads_init() self.extensions = extensions # GUI initialization glade_file = join(functions.datadir, 'glade', 'mainwindow.glade') self.widgets = glade_XML(glade_file, 'window1', domain='bluemindo') self.handler = hmain.MainHandlers(self.extensions, config, self.widgets, gst) window = self.widgets.get_widget('window1') functions.open_bluemindo(window) window.connect('delete_event', self.handler.on_window_close) # Add the pretty logo to all windows icon_file = join(functions.datadir, 'image', 'logo_head_small.png') pixbuf = pixbuf_new_from_file(icon_file) window_set_default_icon_list(pixbuf) window.set_icon(pixbuf) window.set_title('Bluemindo') # Registering a few GTK handlers tool_previous = self.widgets.get_widget('tool-previous') tool_previous.connect('clicked', self.handler.on_tool_previous) tool_stop = self.widgets.get_widget('tool-stop') tool_stop.connect('clicked', self.handler.on_tool_stop) tool_play = self.widgets.get_widget('tool-play') tool_play.connect('clicked', self.handler.on_tool_play) tool_next = self.widgets.get_widget('tool-next') tool_next.connect('clicked', self.handler.on_tool_next) tool_lyrics = self.widgets.get_widget('tool-lyrics') tool_lyrics.connect('clicked', self.handler.on_tool_lyrics) tool_reloadlyrics = self.widgets.get_widget('tool-reloadlyrics') tool_reloadlyrics.connect('clicked', self.handler.on_tool_reloadlyrics) tool_savelyrics = self.widgets.get_widget('tool-savelyrics') tool_savelyrics.connect('clicked', self.handler.on_tool_savelyrics) tool_show_fullscreen = self.widgets.get_widget('menu-show-fullscreen') tool_show_fullscreen.connect('activate', self.handler.on_menu_fullscreen) tool_quit = self.widgets.get_widget('menu-quit') tool_quit.connect('activate', self.handler.on_menu_quit) tool_prefs = self.widgets.get_widget('menu-prefs') tool_prefs.connect('activate', self.handler.on_menu_prefs) tool_refresh = self.widgets.get_widget('menu-refresh') tool_refresh.connect('activate', self.handler.on_menu_refresh) tool_about = self.widgets.get_widget('menu-about') tool_about.connect('activate', self.handler.on_menu_about) # GNOME multimedia keys GnomeMultimediaKeys(extensions) # Only-player functionnality self.has_left_panel = False radio_player = self.widgets.get_widget('view-player') radio_all = self.widgets.get_widget('view-all') radio_player.set_group(radio_all) radio_player.set_active(False) radio_player.connect('activate', self.change_view, 'player') radio_all.set_active(True) radio_all.connect('activate', self.change_view, 'all') # Volume management self.volume = VolumeManagement(self.widgets) def change_view(self, widget, view): """ This function selects the view (player or all).""" vbox = self.widgets.get_widget('vbox2') if view == 'player': if vbox.get_property('visible'): vbox.hide() self.has_left_panel = True self.widgets.get_widget('vpaned1').hide() self.widgets.get_widget('toolbar4').hide() window = self.widgets.get_widget('window1') width = window.get_size()[0] window.resize(width, 120) elif view == 'all': if self.has_left_panel: vbox.show() self.widgets.get_widget('vpaned1').show() self.widgets.get_widget('toolbar4').show() window = self.widgets.get_widget('window1') width = window.get_size()[0] window.resize(width, 240) def get_classes(self): return [self.handler, self.volume] def start_thread(self): # Tell extensions that Bluemindo is now launched self.extensions.load_event('OnBluemindoStarted', self.widgets) # Start the GTK main thread gtk_main()bluemindo-0.3/src/gui/__init__.py0000644000175000017500000000000010723263165016711 0ustar xbrightxbrightbluemindo-0.3/src/gui/boxes.py0000644000175000017500000000314711154031467016306 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gtk.glade import XML as glade_XML from os.path import join from common.functions import Functions functions = Functions() class ErrorBox(object): def __init__(self, title='Error.', text=''): glade_file = join(functions.datadir, 'glade', 'boxes.glade') box = glade_XML(glade_file, 'errorbox') box_wdg = box.get_widget('errorbox') box_wdg.set_title(title) box_wdg.set_markup(text) box_wdg.run() box_wdg.destroy() class DialogBox(object): def __init__(self, title='Question.', text=''): glade_file = join(functions.datadir, 'glade', 'boxes.glade') box = glade_XML(glade_file, 'dialogbox') box_wdg = box.get_widget('dialogbox') box_wdg.set_title(title) box.get_widget('label1').set_markup(text) self.val = box_wdg.run() box_wdg.destroy() def get_answer(self): return self.valbluemindo-0.3/src/gui/volumemanagement.py0000644000175000017500000001047611146400610020524 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gtk.glade import XML as glade_XML from gtk import ICON_SIZE_LARGE_TOOLBAR, VolumeButton from ConfigParser import ConfigParser from os.path import join, exists from common.config import ConfigLoader from common.functions import Functions from media.gstreamer import GStreamer config = ConfigLoader() functions = Functions() gst = GStreamer() class VolumeManagement(object): ref = None ref2 = None def __new__(cls, *args, **kws): # Singleton if cls.ref is None: cls.ref = super(VolumeManagement, cls).__new__(cls, args, kws) return cls.ref def __init__(self, widgets): if VolumeManagement.ref2 is None: VolumeManagement.ref2 = 42 self.launch(widgets) def launch(self, widgets): self.widgets = widgets self.show_volume = False tool_wdg = self.widgets.get_widget('tool') self.tool_volume = VolumeButton() tool_wdg.add(self.tool_volume) self.tool_volume.show() self.tool_volume.connect('value-changed', self.user_set_volume) configparser = ConfigParser() configparser.read(join(config.confdir, 'Bluemindo.cfg')) try: volume = float(configparser.get('Audio', 'volume')) except ConfigParser.NoSectionError: volume = 1.0 self.volume = volume def volume_more(self, datagram): """This function handles the user's command to increase the volume.""" is_step = datagram.split('--volume-more ') if len(is_step) == 2: step = float(is_step[1]) / 100. else: step = 0.1 volume = self.volume + step self.change_volume(volume) def volume_less(self, datagram): """This function handles the user's command to decrease the volume.""" is_step = datagram.split('--volume-less ') if len(is_step) == 2: step = float(is_step[1]) / 100. else: step = 0.1 volume = self.volume - step self.change_volume(volume) def volume_set(self, volume): """This function handles the user's command to change the volume.""" self.change_volume((float(volume) / 100.)) def user_set_volume(self, widget, volume): self.change_volume(volume) def change_volume(self, volume): """This function changes the volume.""" # Verify the volume if volume < 0.0: volume = 0.0 elif volume > 1.0: volume = 1.0 self.volume = volume # Send the new volume to Gstreamer player = gst.getplayer() player.set_property('volume', volume) # Update gtk.VolumeButton self.tool_volume.set_value(volume) # Save the new volume level configparser = ConfigParser() configfile = join(config.confdir, 'Bluemindo.cfg') if exists(configfile): configparser.read(configfile) if configparser.has_section('Window'): width = configparser.get('Window', 'width') height = configparser.get('Window', 'height') x = configparser.get('Window', 'x') y = configparser.get('Window', 'y') configparser.set('Window', 'width', width) configparser.set('Window', 'height', height) configparser.set('Window', 'x', x) configparser.set('Window', 'y', y) configparser.set('Audio', 'volume', volume) else: configparser.add_section('Audio') configparser.set('Audio', 'volume', volume) configparser.write(open(configfile, 'w'))bluemindo-0.3/src/gui/extensionsconfig.py0000644000175000017500000002702011147654433020555 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from gtk.gdk import Pixbuf as gPixbuf, pixbuf_new_from_file from gobject import TYPE_STRING as gString, TYPE_BOOLEAN as gBoolean from gtk import (WIN_POS_CENTER_ALWAYS, ListStore, CellRendererText, CellRendererPixbuf, CellRendererToggle, TreeViewColumn, ICON_SIZE_LARGE_TOOLBAR, Label) from gtk.glade import XML as glade_XML from os.path import join, isfile from pickle import dump from gui.aboutdialog import AboutDialog from common.functions import Functions from common.config import ConfigLoader config = ConfigLoader() functions = Functions() class ExtensionsConfig(object): def __init__(self, extensions): self.extensions = extensions self.is_in_config = False # Create the configuration GUI glade_file = join(functions.datadir, 'glade', 'prefswindow.glade') self.widgets = glade_XML(glade_file, 'window1', domain='bluemindo') window = self.widgets.get_widget('window1') window.set_position(WIN_POS_CENTER_ALWAYS) window.set_title(_('Preferences')) window.connect('delete_event', self.on_window_close) validate_button = self.widgets.get_widget('button-validate') validate_button.connect('clicked', self.close, window) self.widgets.get_widget('button-about').connect('clicked', self.about) # Show modules and plugins found_extensions = self.extensions.get_extensions() activated_plugins = self.extensions.get_actived_plugins() # Create treeviews modules_tree = self.widgets.get_widget('treeview1') plugins_tree = self.widgets.get_widget('treeview2') render_pixbuf = CellRendererPixbuf() render_text = CellRendererText() render_bool = CellRendererToggle() render_bool.set_property('activatable', True) render_bool.set_property('inconsistent', False) modules_list = ListStore(gPixbuf, gString, gString) modules_tree.set_model(modules_list) modules_tree.set_rules_hint(True) modules_column = TreeViewColumn() modules_column.pack_start(render_pixbuf, expand=False) modules_column.add_attribute(render_pixbuf, 'pixbuf', 0) modules_column.pack_start(render_text, expand=True) modules_column.add_attribute(render_text, 'markup', 1) modules_tree.append_column(modules_column) plugins_list = ListStore(gPixbuf, gString, gBoolean, gString) plugins_tree.set_model(plugins_list) plugins_tree.set_rules_hint(True) plugins_column0 = TreeViewColumn() plugins_column0.pack_start(render_pixbuf, expand=False) plugins_column0.add_attribute(render_pixbuf, 'pixbuf', 0) plugins_column0.set_expand(True) plugins_column0.pack_start(render_text, expand=True) plugins_column0.add_attribute(render_text, 'markup', 1) plugins_tree.append_column(plugins_column0) plugins_column1 = TreeViewColumn() plugins_column1.pack_start(render_bool, expand=False) plugins_column1.add_attribute(render_bool, 'active', 2) plugins_tree.append_column(plugins_column1) # GTK signals render_bool.connect_object('toggled', self.on_toggle, plugins_list) m = 'modules' p = 'plugins' modules_tree.get_selection().connect('changed', self.openconf, m) plugins_tree.get_selection().connect('changed', self.openconf, p) for module in found_extensions['modules']: if isfile(module['logo']): icon = pixbuf_new_from_file(module['logo']) else: icon = plugins_tree.render_icon(stock_id=module['logo'], size=ICON_SIZE_LARGE_TOOLBAR, detail=None) modules_list.append((icon, '%s' % module['name'], module['name'])) for plugin in found_extensions['plugins']: if plugin['name'].lower() in activated_plugins: act = True else: act = False if isfile(plugin['logo']): icon = pixbuf_new_from_file(plugin['logo']) else: icon = plugins_tree.render_icon(stock_id=plugin['logo'], size=ICON_SIZE_LARGE_TOOLBAR, detail=None) plugins_list.append((icon, '%s\n%s' % (plugin['name'], plugin['description']), act, plugin['name'])) def on_window_close(self, widget, event): """Save configuration after the window close.""" # Configure current extension if self.is_in_config: self.extensions.load_event('OnModuleConfigurationSave', self.is_in_config) self.is_in_config = False def close(self, widget, window): """Save configuration on button click.""" # Configure current extension if self.is_in_config: self.extensions.load_event('OnModuleConfigurationSave', self.is_in_config) self.is_in_config = False window.destroy() def openconf(self, selection, exttype): """Open the configuration for a given extension.""" # Configure current extension if self.is_in_config: self.extensions.load_event('OnModuleConfigurationSave', self.is_in_config) self.is_in_config = False # Start new configuration (mod, iter_) = selection.get_selected() if iter_: pname = mod.get_value(iter_, 1).split('\n') name = functions.clear_html(pname[0]) extensions = self.extensions.get_extensions() actived = self.extensions.get_actived_plugins() if exttype != 'modules': exttype = 'plugins' for ext in extensions[exttype]: if ext['name'] == name: if not ext['configurable']: # This extension is not configurable self.is_in_config = False if exttype == 'modules': hbox = self.widgets.get_widget('hbox1') else: hbox = self.widgets.get_widget('hbox2') try: kids = hbox.get_children() hbox.remove(kids[2]) except IndexError: pass lbl = Label('%s' % _('This extension is not ' 'configurable.')) lbl.set_use_markup(True) hbox.add(lbl) lbl.show() elif name.lower() not in actived and exttype == 'plugins': # This plugin is deactivated self.is_in_config = False hbox = self.widgets.get_widget('hbox2') try: kids = hbox.get_children() hbox.remove(kids[2]) except IndexError: pass lbl = Label('%s' % _('You should start the ' 'plugin before trying to ' 'configure it.')) lbl.set_use_markup(True) hbox.add(lbl) lbl.show() else: # Open the configuration self.extensions.load_event('OnModuleConfiguration', (name, self.widgets)) self.is_in_config = name return def about(self, widget): """Show an AboutDialog for the selected extension.""" notepad = self.widgets.get_widget('notebook1') current = notepad.get_current_page() if current == 0: # If the extension is a module, show the Bluemindo's AboutDialog # instead of a specialized one AboutDialog() elif current == 1: # We've got a plugin now! treeview = self.widgets.get_widget('treeview2') (mod, iter_) = treeview.get_selection().get_selected() if iter_: # Get plugin's informations pname = mod.get_value(iter_, 1).split('\n') name = functions.clear_html(pname[0]) extensions = self.extensions.get_extensions() for plugin in extensions['plugins']: if plugin['name'] == name: if isfile(plugin['logo']): logopixbuf = pixbuf_new_from_file(plugin['logo']) else: logopixbuf = None # Construct the AboutDialog about_widgets = glade_XML(join(functions.datadir, 'glade', 'prefswindow.glade'), 'aboutdialog1', domain='bluemindo') about_dialog = about_widgets.get_widget('aboutdialog1') # Add datas about_dialog.set_name(plugin['name']) about_dialog.set_version(str(plugin['version'])) about_dialog.set_comments(plugin['description']) about_dialog.set_logo(logopixbuf) about_dialog.set_copyright('%s\n%s' % ( plugin['authors'], plugin['license'])) # Show the AboutDialog and exit about_dialog.run() about_dialog.destroy() return def on_toggle(self, liststore, pid): value = liststore[pid][2] if value: liststore[pid][2] = False status = False else: liststore[pid][2] = True status = True nm = liststore[pid][1].split('\n') name = functions.clear_html(nm[0]).lower() # Active or de-active a plugin actived = self.extensions.get_actived_plugins() if status and name not in actived: actived.append(name) self.extensions.activate_plugin(name) elif not status and name in actived: actived.remove(name) self.extensions.shutdown_plugin(name) # Write the new Plugins.cfg file dump(actived, open(join(config.confdir, 'Plugins.cfg'), 'w'))bluemindo-0.3/src/common/functions.py0000644000175000017500000001134111241603412017665 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gobject import idle_add from gtk import main_quit as gtk_main_quit from md5 import new as md5_new from os.path import join, isfile, isdir from os import remove import ConfigParser from common.config import ConfigLoader class Functions(object): ref = None ref2 = None def __new__(cls, *args, **kws): # Singleton if cls.ref is None: cls.ref = super(Functions, cls).__new__(cls, args, kws) return cls.ref def __init__(self): if Functions.ref2 is None: Functions.ref2 = 42 # Just set the data and the locale dir if isdir('../data') and isfile('../locale/bluemindo.pot'): self.datadir = '../data' self.localedir = '../locale' else: self.datadir = '/usr/share/bluemindo' self.localedir = '/usr/share/locale' def get_hash(self, str1, str2): """Just return the hash for given strings""" md5 = md5_new() md5.update(str1) md5.update(str2) return str(md5.hexdigest()) def human_length(self, length): """Return the length in a human-readable way""" lg0 = length / 60 lg1 = length % 60 if lg0 >= 0 and lg0 < 10: lg0 = '0' + str(lg0) if lg1 >= 0 and lg1 < 10: lg1 = '0' + str(lg1) lg = str(lg0) + ':' + str(lg1) return lg def clear_html(self, text, only_bold=False): """Return the text without html""" if text.startswith('') and text.endswith(''): text = text[3:-4] if not only_bold: if text.startswith(''): text = text[19:-7] return text def open_bluemindo(self, window): """Handle the Bluemindo's window open and change width, height and position on the screen.""" config = ConfigLoader() parser = ConfigParser.ConfigParser() configfile = join(config.confdir, 'Bluemindo.cfg') if isfile(configfile): conf_ = parser.read(configfile) try: width = parser.get('Window', 'width') height = parser.get('Window', 'height') x = parser.get('Window', 'x') y = parser.get('Window', 'y') window.resize(int(width), int(height)) window.move(int(x), int(y)) except ConfigParser.NoSectionError: pass else: # Create the default config file parser.add_section('Audio') parser.set('Audio', 'volume', 1.0) parser.write(open(configfile, 'w')) def close_bluemindo(self, window, quit=True): """This function is called when the Bluemindo's main window is closed.""" config = ConfigLoader() # Backup window width, height and position if window.get_properties('visible')[0]: configfile = ConfigParser.ConfigParser() _file_ = join(config.confdir, 'Bluemindo.cfg') configfile.read(_file_) width = window.get_size()[0] height = window.get_size()[1] x = window.get_position()[0] y = window.get_position()[1] if not configfile.has_section('Window'): configfile.add_section('Window') vol = configfile.get('Audio', 'volume') configfile.set('Audio', 'volume', vol) configfile.set('Window', 'width', width) configfile.set('Window', 'height', height) configfile.set('Window', 'x', x) configfile.set('Window', 'y', y) configfile.write(open(_file_, 'w')) # Delete the socket file and quit GTK if quit: SOCKET_NAME = '/tmp/bluemindo' remove(SOCKET_NAME) current_playing = join(config.datadir, 'current-playing') if isfile(current_playing): remove(current_playing) print 'The dolphin has plunge!' idle_add(gtk_main_quit) bluemindo-0.3/src/common/statistics.py0000644000175000017500000000722111130673251020056 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from common.sqlite import SQLite class Statistics(object): """A class to gets and updates statistics.""" def _get_value(self, params): sql = SQLite() if params.has_key('file'): sqlsyntax = 'select * from stats_songs where filename=:file' elif params.has_key('artist'): sqlsyntax = 'select * from stats_artists where artist=:artist' elif params.has_key('album'): sqlsyntax = 'select * from stats_albums where album=:album' cur_nb = sql.execute(sqlsyntax, params) song = cur_nb.fetchone() if song is not None: song_tracks = song[1] else: song_tracks = 0 sql.close() return song_tracks def _set_value(self, params): sql = SQLite() if params.has_key('file'): i_param = [params['file'], params['value']] i = 'insert into stats_songs (filename, tracks) values (?, ?)' u = 'update stats_songs set tracks=:value where filename=:file' elif params.has_key('artist'): i_param = [params['artist'], params['value']] i = 'insert into stats_artists (artist, tracks) values (?, ?)' u = 'update stats_artists set tracks=:value where artist=:artist' elif params.has_key('album'): i_param = [params['album'], params['value']] i = 'insert into stats_albums (album, tracks) values (?, ?)' u = 'update stats_albums set tracks=:value where album=:album' value = self._get_value(params) if params['value'] is None: params['value'] = int(value + 1) i_param[1] = int(value + 1) if value == 0: sql.execute(i, i_param) else: sql.execute(u, params) sql.close() def get_stats_for_song(self, filename): value = self._get_value({'file': filename}) return value def get_stats_for_artist(self, artist): value = self._get_value({'artist': artist}) return value def get_stats_for_album(self, album): value = self._get_value({'album':album}) return value def set_stats_for_song(self, filename, value): self._set_value({'file': filename, 'value': value}) def set_stats_for_artist(self, artist, value): self._set_value({'artist': artist, 'value': value}) def set_stats_for_album(self, album, value): self._set_value({'album': album, 'value': value}) def get_top(self, by='filename', limit=50): if by == 'filename': tbl = 'stats_songs' elif by == 'artist': tbl = 'stats_artists' elif by == 'album': tbl = 'stats_albums' result = [] txt = ('select * from %s order by tracks desc ' 'limit %u' % (tbl, limit)) sql = SQLite() cur = sql.execute(txt) for sg in cur: result.append(sg) sql.close() return resultbluemindo-0.3/src/common/__init__.py0000644000175000017500000000000010723263165017415 0ustar xbrightxbrightbluemindo-0.3/src/common/sqlite.py0000644000175000017500000000542511241603412017164 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from os.path import join, exists try: # Load sqlite3 (python 2.5) import sqlite3 as sqlite except ImportError: try: # Load pysqlite2 (python =< 2.4) from pysqlite2 import dbapi2 as sqlite except ImportError: raise SystemExit('You need python-pysqlite2 (see the INSTALL file).') from common.config import ConfigLoader config = ConfigLoader() class SQLite(object): def __init__(self): sqlfile = join(config.datadir, 'songs.db') if not exists(sqlfile): database_exist = False else: database_exist = True self.cx = sqlite.connect(sqlfile) self.cur = self.cx.cursor() self.cx.text_factory = str if not database_exist: self.execute('create table songs ( ' 'title text, ' 'artist text, ' 'album text, ' 'comment text, ' 'genre text, ' 'year text, ' 'track integer, ' 'length integer, ' 'filename text ' ')') self.execute('create table stats_songs ( ' 'filename text, tracks integer )') self.execute('create table stats_albums ( ' 'album text, tracks integer )') self.execute('create table stats_artists ( ' 'artist text, tracks integer )') self.execute('create table radios ( ' 'name text, url text )') def execute(self, sql, param=None): if param is not None: self.cur.execute(sql, param) else: self.cur.execute(sql) self.cx.commit() return self.cur def executemany(self, sql, param): self.cur.executemany(sql, param) self.cx.commit() return self.cur def fetchall(self, cur): return cur.fetchall() def close(self): self.cur.close() self.cx.close()bluemindo-0.3/src/common/webservices.py0000644000175000017500000001646111224222634020212 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from StringIO import StringIO from ConfigParser import ConfigParser from os.path import join, exists from os import makedirs, remove from re import compile as re_compile from base64 import b64decode from urllib2 import urlopen, Request, quote as urllib_quote, HTTPError from urllib import urlretrieve from httplib import BadStatusLine from threading import Lock from time import time, sleep try: # Python >= 2.5 import xml.etree.cElementTree as ElementTree except ImportError: try: # Python < 2.5 from elementtree import ElementTree except ImportError: raise SystemExit('You need python-elementtree (see the INSTALL file).') from common.config import ConfigLoader from common.functions import Functions class WebServices(object): """A class to handles many WebServices.""" config = ConfigLoader() functions = Functions() lock = Lock() def get_xml(self, url): """This function downloads a file and returns an ElementTree object.""" req = Request(url) req_file = urlopen(req) content = req_file.read() return ElementTree.fromstring(content) def get_html(self, url): """This function downloads a file and returns its content.""" req = Request(url) req_file = urlopen(req) content = req_file.read() return content class LastFm(WebServices): """A class for Last.fm.""" def __init__(self): self.api_key = b64decode(self.config.lastfm_key) self.api_url = 'http://ws.audioscrobbler.com/2.0/' def get_pictures(self, artists): """Get a picture for all albums.""" for artist in artists: self.get_artist_picture(artist) sleep(2) def get_artist_picture(self, artist_name): """Get a picture for an artist.""" url = (self.api_url + '?method=artist.getinfo&artist=%s&api_key=%s' % (urllib_quote(str(artist_name)), self.api_key)) datadir = self.config.datadir hash_a = self.functions.get_hash(artist_name, 'picture') pictures_dir = join(datadir, 'modules', 'explorer', 'artists') artist_file = join(pictures_dir, hash_a) self.lock.acquire() try: if not exists(pictures_dir): makedirs(pictures_dir) finally: self.lock.release() if not exists(artist_file): try: tree = self.get_xml(url) artist = tree.find('artist') images = artist.getiterator('image') for img in images: if img.attrib['size'] == 'large': artist_image = img.text if artist_image is not None: urlretrieve(artist_image, artist_file) return artist_file except (BadStatusLine, HTTPError): # Service is currently unavailable pass class Amazon(WebServices): """A class for Amazon.""" def __init__(self): self.api_key = b64decode(self.config.amazon_key) self.api_url = ('http://ecs.amazonaws.com/onca/xml' '?Service=AWSECommerceService' '&SearchIndex=Music&AWSAccessKeyId=') self.api_ns = ('{http://webservices.amazon.com/AWSECommerceService/' '2005-10-05}') self.api_reg = re_compile('') def get_pictures(self, albums): """Get a picture for all albums.""" for album in albums: self.get_album_picture(album[0], album[1]) sleep(2) def get_album_picture(self, artist_name, album_name): """Get a picture for an album.""" datadir = self.config.datadir hash_a = self.functions.get_hash(album_name, artist_name) pictures_dir = join(datadir, 'modules', 'player', 'covers') album_file = join(pictures_dir, hash_a) self.lock.acquire() try: if not exists(pictures_dir): makedirs(pictures_dir) finally: self.lock.release() search_keywords = ('%s %s' % (album_name, artist_name)) url = (self.api_url + '%s&Operation=ItemSearch&Keywords=%s' % ( self.api_key, urllib_quote(str(search_keywords)))) if not exists(album_file): try: tree = self.get_xml(url) results = tree.find('%sItems' % self.api_ns) items = results.getiterator('%sItem' % self.api_ns) for item in items: album_id = item.find('%sASIN' % self.api_ns).text url = ('http://www.amazon.com/gp/product/images' '/%s' % album_id) html = self.get_html(url) for img in self.api_reg.findall(html): urlretrieve(img, album_file) return img except HTTPError: # Service is currently unavailable pass class Shoutcast(WebServices): """A class for Shoutcast Radio.""" def __init__(self): self.api_url = 'http://www.shoutcast.com/sbin/' def get_categories(self): """Get the list of all categories.""" tree = self.get_xml(self.api_url + 'newxml.phtml') cat = [] categories = tree.getiterator('genre') for category in categories: cat.append(category.attrib['name']) return cat def get_radios_by_category(self, category): """Get the list of radios for one category.""" url = '%snewxml.phtml?genre=%s' % (self.api_url, category) tree = self.get_xml(url) rad = [] radios = tree.getiterator('station') for radio in radios: rad.append((radio.attrib['name'], radio.attrib['id'], radio.attrib['lc'])) return rad def get_radio_url(self, radio_id): """Construct the URL of a webradio with its identifier.""" url = ('%sshoutcast-playlist.pls?rn=%d&file=filename.pls' % ( self.api_url, int(radio_id))) return url def get_file_in_pls(self, pls): """Get the first file to load in the shoutcast playlist.""" content = self.get_html(pls) output = StringIO(content) # Parse the content configparser = ConfigParser() configparser.readfp(output, None) file_url = configparser.get('playlist', 'File1') output.close() return file_urlbluemindo-0.3/src/common/playlists.py0000644000175000017500000001053411146400610017703 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from os.path import join, isdir, isfile, basename from os import makedirs, listdir, remove from common.config import ConfigLoader config = ConfigLoader() class Playlists: """This class can create, delete and manager m3u-like playlists. Documentation: http://en.wikipedia.org/wiki/M3U This class handles playlists in unicode: m3u8""" def __init__(self): self.datadir = join(config.datadir, 'modules', 'explorer', 'playlists') if not isdir(self.datadir): makedirs(self.datadir) # This function returns the list of all Bluemindo's saved playlists def get_playlists_list(self): dir_list = listdir(self.datadir) files = [] for filename in dir_list: if filename.endswith('.m3u8'): pretty = filename.split('.m3u8') files.append(pretty[0]) return files # This function exports a playlist def export_playlist(self, playlist, new_playlist): songs = self.load_playlist(playlist) new_file = open(new_playlist, 'w') for song in songs: if isfile(song): new_file.write(song.encode('utf-8') + "\n") new_file.close() # This function imports a playlist and store it in the playlists folder def import_playlist(self, filename): # Get the real filename file_name = basename(filename) if file_name.endswith('.m3u'): file_name = file_name.split('.m3u') file_name = file_name[0] elif file_name.endswith('.m3u8'): file_name = file_name.split('.m3u8') file_name = file_name[0] # Create the playlist self.create_new_playlist(file_name) # Get all songs import_file = open(filename, 'r') import_songs = import_file.readlines() import_file.close() # Add the songs for song in import_songs: if isfile(song[:-1]): self.add_item_to_playlist(song[:-1], file_name) # Return the playlist name return file_name # This function creates a new playlist def create_new_playlist(self, name): playlist_file = open(join(self.datadir, str(name) + '.m3u8'), 'w') playlist_file.close() # This function deletes a playlist def delete_playlist(self, name): playlist_file = join(self.datadir, name + '.m3u8') if isfile(playlist_file): remove(playlist_file) # This function adds an item to a playlist def add_item_to_playlist(self, filename, name): playlist_file = open(join(self.datadir, name + '.m3u8'), 'a') playlist_file.write(filename.encode('utf-8') + '\n') playlist_file.close() # This function removes an item from the playlist def remove_item_from_playlist(self, filename, name): playlist_file = open(join(self.datadir, name + '.m3u8'), 'r') songs_list = playlist_file.readlines() playlist_file.close() songs = [] for song in songs_list: if song[:-1] != filename: songs.append(song[:-1]) remove(join(self.datadir, name + '.m3u8')) playlist_file = open(join(self.datadir, name + '.m3u8'), 'w') for song in songs: playlist_file.write(song.encode('utf-8') + '\n') playlist_file.close() # This function loads a playlist def load_playlist(self, name): playlist_file = open(join(self.datadir, name + '.m3u8'), 'r') songs = playlist_file.readlines() playlist_file.close() clean_songs = [] for song in songs: clean_songs.append(song.replace('\n', '')) return clean_songsbluemindo-0.3/src/common/config.py0000644000175000017500000000645711241603412017136 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from os.path import join, isdir, isfile, expanduser from os import listdir, makedirs, environ, removedirs from pickle import dump import ConfigParser class ConfigLoader(object): ref = None ref2 = None def __new__(cls, *args, **kws): # Singleton if cls.ref is None: cls.ref = super(ConfigLoader, cls).__new__(cls, args, kws) return cls.ref def __init__(self): if ConfigLoader.ref2 is None: ConfigLoader.ref2 = 42 self.launch() def launch(self): self.config = ConfigParser.ConfigParser() # Set the configuration directory to XDG_CONFIG_HOME if exists # If not, set it to $HOME/.config/bluemindo # http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html if environ.get('XDG_CONFIG_HOME'): self.confdir = join(environ.get('XDG_CONFIG_HOME'), 'bluemindo') else: self.confdir = join(expanduser('~'), '.config', 'bluemindo') if not isdir(self.confdir): makedirs(self.confdir) # Set the data directory to XDG_DATA_HOME if exists # If not, set it to $HOME/.local/share/bluemindo # http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html if environ.get('XDG_DATA_HOME'): self.datadir = join(environ.get('XDG_DATA_HOME'), 'bluemindo') else: self.datadir = join(expanduser('~'), '.local', 'share', 'bluemindo') if not isdir(self.datadir): makedirs(self.datadir) # Create directories for extensions config files if not isdir(join(self.confdir, 'modules')): makedirs(join(self.confdir, 'modules')) if not isdir(join(self.confdir, 'plugins')): makedirs(join(self.confdir, 'plugins')) # Create the plugin configuration file if not isfile(join(self.confdir, 'Plugins.cfg')): default_plugins = ['notification'] dump(default_plugins, open(join(self.confdir, 'Plugins.cfg'), 'w')) # Create the unofficial plugins directory self.unofficial_plugins = join(self.datadir, 'unofficial_plugins') if not isdir(self.unofficial_plugins): makedirs(self.unofficial_plugins) # Store the Bluemindo key for the Last.fm API # Don't use it, create your own here: http://www.lastfm.fr/api/account self.lastfm_key = 'MjZjOTgyYzk4NTVkZjcxMTIwMTgzY2UzZmJiNmI5ODA=' # Store the Bluemindo key for the Amazon API # Don't use it, create your own here: http://aws.amazon.com/ self.amazon_key = 'MFQxQ0M4REUxSFFHUldFTUJHODI='bluemindo-0.3/src/bluemindo.py0000755000175000017500000001535311241603412016355 0ustar xbrightxbright#!/usr/bin/env python # -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from os.path import isfile, exists from gettext import (bindtextdomain, textdomain, bind_textdomain_codeset, gettext as _) if isfile('../locale/bluemindo.pot'): localedir = '../locale' else: localedir = '/usr/share/locale' bindtextdomain('bluemindo', localedir) textdomain('bluemindo') bind_textdomain_codeset('bluemindo', 'UTF-8') from socket import socket, AF_UNIX, SOCK_DGRAM, error as socket_error from pickle import dumps from sys import argv from os import remove from os.path import join from gui.boxes import DialogBox from common.config import ConfigLoader from common.functions import Functions functions = Functions() config = ConfigLoader() class Bluemindo(object): def __init__(self): SOCKET_NAME = '/tmp/bluemindo' if len(argv) > 1 and argv[1] in ('-h', '--help'): # Show the help print _("Bluemindo Copyright (C) 2007-2009 Erwan Briand\n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to\n" "redistribute it under certain conditions.\n\n" "Usage: bluemindo[.py] [options]\n\n" "Available options:\n" "--current\t\t" "Show the current playing song artist, title and album\n" "--current-cover\t\t" "Show the path to the cover of the current playing song\n" "--current-lyrics\t" "Show the lyrics for the current playing song\n\n" "--playpause, --play, --pause\t" "Play or pause a song\n" "--stop\t\t\t" "Stop a song\n" "--previous\t\t" "Jump to the previous song in playlist\n" "--next\t\t\t" "Jump to the next song in playlist\n\n" "--volume-more [STEP]\t" "Increase the volume, you can specify a step (0 > 100)\n" "--volume-less [STEP]\t" "Decrease the volume, you can specify a step (0 > 100)\n" "--volume=VOLUME\t\t" "Set the volume: 0 > 100\n\n" "--reload\t\t" "Reload the songs from your music folder\n" "--quit, --plunge\t" "Quit Bluemindo") elif len(argv) > 1 and argv[1].startswith('--current'): # Get the current song current_playing = join(config.datadir, 'current-playing') if exists(current_playing): file_ = open(current_playing) csong = file_.read() csong_ = csong.split(' (from: ') calbum = csong_[1][:-1] csong_ = csong_[0].split(' - ') cartist = csong_[0] ctitle = csong_[1] file_.close() # Send the current playing song album cover if argv[1].endswith('-cover'): file_ = join(config.datadir, 'modules', 'player', functions.get_hash(calbum, cartist) + '.covers') if isfile(file_): print file_ else: print 'File not found.' # Send the current playing song lyrics elif argv[1].endswith('-lyrics'): file_ = join(config.datadir, 'modules', 'lyrics', functions.get_hash(ctitle, cartist) + '.lyrics') if isfile(file_): lyric = open(file_) print lyric.read() lyric.close() else: print 'File not found.' # Send the current playing song artist and title else: print csong elif len(argv) > 1 and exists(SOCKET_NAME): # Create a client and connect to the UNIX socket in # order to send the user's request try: client = socket(AF_UNIX, SOCK_DGRAM) client.connect_ex(SOCKET_NAME) client.send(dumps(argv)) client.close() except socket_error: print 'Socket error.' elif len(argv) > 1 and not exists(SOCKET_NAME): # We cannot do anything here print 'Socket not found.' elif len(argv) == 1 and exists(SOCKET_NAME): # The socket exists but we want to start Bluemindo: it fails db = DialogBox(title=_('Bluemindo is already started!'), text=_('The socket file located at ' '/tmp/bluemindo already exists, so ' 'Bluemindo cannot start. If you still want ' 'to start Bluemindo, hit "yes" but part of ' 'the software might be broken.')) if db.get_answer() == 0: # Remove the existing socket remove(SOCKET_NAME) if isfile(join(config.datadir, 'lock')): remove(join(config.datadir, 'lock')) # Yes I know it's not pretty to do an import here but pygst # is doing crazy things with --help and I'm tired of that. :'( from mainapplication import MainApplication MainApplication() else: # Start Bluemindo if isfile(join(config.datadir, 'lock')): remove(join(config.datadir, 'lock')) # Yes I know it's not pretty to do an import here but pygst # is doing crazy things with --help and I'm tired of that. :'( from mainapplication import MainApplication # Launch Bluemindo MainApplication() def main(): bluemindo = Bluemindo() if __name__ == "__main__": main()bluemindo-0.3/src/modules/explorer/views/normal.py0000644000175000017500000002462311224661214022336 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from threading import Thread from os.path import join, isfile from gobject import TYPE_STRING as gString, markup_escape_text, timeout_add from gtk.gdk import (Pixbuf as gPixbuf, pixbuf_new_from_file, INTERP_BILINEAR, threads_enter, threads_leave) from gtk import (CellRendererPixbuf, CellRendererText, ListStore, TreeViewColumn, TREE_VIEW_COLUMN_FIXED) from modules.explorer.playlist import Playlist from modules.explorer.musicdb import MusicDatabase from modules.explorer import viewscommon unescape = viewscommon.unescape insensitive_sort = viewscommon.insensitive_sort from common.functions import Functions class NormalView(object): ref = None ref2 = None def __new__(cls, *args, **kws): # Singleton if cls.ref is None: cls.ref = super(NormalView, cls).__new__(cls, args, kws) return cls.ref def __init__(self, glade_file, config, userconf): self.playlist = Playlist(glade_file, config, userconf) self.glade_file = glade_file self.config = config self.userconf = userconf self.player_data = join(self.userconf.datadir, 'modules', 'player') self.functions = Functions() # Show widget self.glade_file.get_widget('hbox6').show() self.glade_file.get_widget('scrolledwindow4').show() self.glade_file.get_widget('scrolledwindow5').show() self.glade_file.get_widget('frame2').hide() if NormalView.ref2 is None: NormalView.ref2 = 42 self.launch() def launch(self): render_pixbuf_ = CellRendererPixbuf() render_text_ = CellRendererText() # Album treestore_ = ListStore(gPixbuf, gString, gString) albumview = self.glade_file.get_widget('treeview4') albumview.set_model(treestore_) albumview.set_rules_hint(True) albumview.connect('row-activated', self.load_album) column1 = TreeViewColumn() column1.pack_start(render_pixbuf_, expand=False) column1.add_attribute(render_pixbuf_, 'pixbuf', 0) column1.pack_start(render_text_, expand=True) column1.add_attribute(render_text_, 'markup', 1) column1.set_min_width(250) column1.set_expand(True) column1.set_sizing(TREE_VIEW_COLUMN_FIXED) albumview.append_column(column1) column2 = TreeViewColumn() column2.pack_start(render_text_, expand=False) column2.add_attribute(render_text_, 'markup', 2) albumview.append_column(column2) # Artist treestore = ListStore(gString, gString) artistview = self.glade_file.get_widget('treeview3') artistview.set_model(treestore) artistview.set_rules_hint(True) artistview.connect('row-activated', self.load_artist) artselect = artistview.get_selection() artselect.connect('changed', self.browse_artist, treestore_, column1) column0_ = TreeViewColumn() column0_.pack_start(render_text_, expand=True) column0_.add_attribute(render_text_, 'markup', 0) column0_.set_min_width(280) column0_.set_expand(True) column0_.set_sizing(TREE_VIEW_COLUMN_FIXED) artistview.append_column(column0_) column1_ = TreeViewColumn() column1_.pack_start(render_text_, expand=False) column1_.add_attribute(render_text_, 'markup', 1) artistview.append_column(column1_) # Add artists self.vinfos = [treestore, treestore_, column1, column0_] self.reload_database() def reload_database(self, force_scan=False): pbar = self.glade_file.get_widget('progressbar1') pbar.set_text(_('Opening folder…')) pbar.show() thread = Thread(group=None, target=self.insert_artists, name='artists', args=(pbar, self.vinfos[0], self.vinfos[1], self.vinfos[2], self.vinfos[3], force_scan)) self.thread_name = thread thread.start() def timeout_progress(self, pbar): if self.thread_name.isAlive(): pbar.pulse() return True else: pbar.hide() def insert_artists(self, pbar, treestore, treestore_, column1, column0, force_scan): """This function is called at the start-up and add artists.""" threads_enter() # Call database musicdb = MusicDatabase(self.config, pbar) if self.config['scan'] or force_scan: musicdb.scan() pbar.set_text(_('Loading music…')) self.database = musicdb.load() timeout_add(100, self.timeout_progress, pbar) self.songs_tree = viewscommon.get_representation(self.database, self.config['artwork']) self.continue_to_pulse = False pbar.set_text(_('Loading artists…')) items = self.songs_tree.items() items.sort(insensitive_sort) items_sort = [key for key, value in items] treestore_.clear() treestore.clear() ni = -1 for artist in items_sort: nb = 0 for alb in self.songs_tree[artist]: nb = nb + len(self.songs_tree[artist][alb]) treestore.append(('%s' % markup_escape_text(unicode(artist)), '%s' % nb)) ni = ni + nb lensongs = len(self.songs_tree) column0.set_title(_('%(art)d artists (%(sng)d songs)') % {'art': lensongs, 'sng': ni}) self.browse_artist(None, treestore_, column1) threads_leave() def browse_artist(self, selection, treestore_, column): """This function show albums by artist.""" if selection: (mod, iter_) = selection.get_selected() if iter_: artist = unescape(mod.get_value(iter_, 0)) try: treestore_.clear() items = self.songs_tree[artist].items() items.sort(insensitive_sort) items_sort = [key for key, value in items] except UnboundLocalError: return nb = 0 for album_ in items_sort: nb = nb + len(self.songs_tree[artist][album_]) _file = join(self.player_data, 'covers', self.functions.get_hash(album_, artist)) if isfile(_file): icon = pixbuf_new_from_file(_file) else: icon = pixbuf_new_from_file(join(self.functions.datadir, 'image', 'logo_head_big.png')) icon = icon.scale_simple(32, 32, INTERP_BILINEAR) treestore_.append((icon, '%s' % markup_escape_text(unicode(album_)), ('%s' % len(self.songs_tree[artist][album_])) )) column.set_title(_('%(alb)d albums (%(sng)d songs)') % {'alb': len(self.songs_tree[artist]), 'sng': nb}) else: albums = [] i = 0 n = -1 for art in self.songs_tree: for alb in self.songs_tree[art]: albums.append({'album': alb, 'artist': art}) i = i + 1 n = n + len(self.songs_tree[art][alb]) albums.sort() column.set_title(_('%(alb)d albums (%(sng)d songs)') % {'alb': i, 'sng': n}) for alb in albums: album_ = alb['album'] artist = alb['artist'] _file = join(self.player_data, 'covers', self.functions.get_hash(album_, artist)) if isfile(_file): icon = pixbuf_new_from_file(_file) else: icon = pixbuf_new_from_file(join(self.functions.datadir, 'image', 'logo_head_big.png')) icon = icon.scale_simple(32, 32, INTERP_BILINEAR) treestore_.append((icon, '%s' % markup_escape_text(unicode(album_)), ('%s' % len(self.songs_tree[artist][album_]) ))) def load_artist(self, tview, path, column): """This function adds artist's songs to playlist.""" (mod, iter_) = tview.get_selection().get_selected() artist = unescape(mod.get_value(iter_, 0)) songs = [] for alb in self.songs_tree[artist]: for sg in self.songs_tree[artist][alb]: songs.append(sg) self.playlist.add_songs(songs) def load_album(self, tview, path, column): """This function adds album's songs to playlist.""" try: tart = self.glade_file.get_widget('treeview3') (mod, iter_) = tart.get_selection().get_selected() artist = unescape(mod.get_value(iter_, 0)) by_artist = True except TypeError: by_artist = False (mod, iter_) = tview.get_selection().get_selected() album = unescape(mod.get_value(iter_, 1)) songs = [] if by_artist: for sg in self.songs_tree[artist][album]: songs.append(sg) else: for art in self.songs_tree: for alb in self.songs_tree[art]: if alb == album: for sg in self.songs_tree[art][alb]: songs.append(sg) if len(songs) > 0: self.playlist.add_songs(songs)bluemindo-0.3/src/modules/explorer/views/__init__.py0000644000175000017500000001250611147270165022607 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from gobject import timeout_add from os.path import isfile, join from gtk.gdk import pixbuf_new_from_file, INTERP_BILINEAR from modules.explorer.playlist import Playlist from modules.explorer.views.lightweight import LightweightView from modules.explorer.views.basic import BasicView from modules.explorer.views.normal import NormalView from modules.explorer.views.full import FullView from modules.explorer.views.albums import AlbumsView from modules.explorer.views.playlists import PlaylistsView from modules.explorer.views.webradios import WebradiosView from common.config import ConfigLoader from common.functions import Functions class Views: def __init__(self, glade_file, conf, force_view=False, force_reload=False): if not force_view: if conf['mode'] not in ('full', 'normal', 'basic', 'lightweight', 'albums', 'playlists', 'webradios'): raise SystemExit('Ahah. You are funny.') view_mode = conf['mode'] else: view_mode = force_view self.glade_file = glade_file self.config = conf self.userconf = ConfigLoader() self.functions = Functions() # Hide all widgets, will be shown after by the selected view self.glade_file.get_widget('hbox3').hide() self.glade_file.get_widget('vpaned1').hide() self.glade_file.get_widget('toolbar3').hide() self.glade_file.get_widget('progressbar1').hide() self.glade_file.get_widget('alignment6').hide() self.glade_file.get_widget('vbox2').hide() self.glade_file.get_widget('hbox6').hide() self.glade_file.get_widget('frame4').hide() self.glade_file.get_widget('scrolledwindow1').hide() self.glade_file.get_widget('scrolledwindow7').hide() self.glade_file.get_widget('scrolledwindow10').hide() self.glade_file.get_widget('scrolledwindow11').hide() self.glade_file.get_widget('vbox11').hide() self.glade_file.get_widget('toolbar6').hide() # Hide the playlist's widgets glade_file.get_widget('scrolledwindow3').hide() glade_file.get_widget('toolbar4').hide() # Show a message if no music folder selected if self.config['folder'] == '': # Hide all self.glade_file.get_widget('hpaned1').hide() self.glade_file.get_widget('hbox4').hide() self.glade_file.get_widget('menu-refresh').set_sensitive(False) self.glade_file.get_widget('menuitem2').set_sensitive(False) self.glade_file.get_widget('alignment6').show() pimg = self.glade_file.get_widget('image4') pixbuf = pixbuf_new_from_file(join(self.functions.datadir, 'image', 'logo_head_big.png')) pixbuf = pixbuf.scale_simple(85, 85, INTERP_BILINEAR) pimg.set_from_pixbuf(pixbuf) widget = self.glade_file.get_widget('label4') widget.set_markup(_('Welcome in Bluemindo!\n\n' 'First of all, you need to configure the explorer module ' 'in order to choose your root music directory. This is easy!' ' Go in the File menu, click on Preferences and you will be ' 'able to configure all Bluemindo\'s modules.')) # Start the view class_name = eval(view_mode.capitalize() + 'View') v = class_name(self.glade_file, self.config, self.userconf) if force_reload: v.reload_database(force_scan=True) def reload_config(self, newconf, force_reload=False): """This function sends new config parameters.""" # Show widgets if they were hidden self.glade_file.get_widget('hpaned1').show() self.glade_file.get_widget('hbox4').show() self.glade_file.get_widget('menu-refresh').set_sensitive(True) self.glade_file.get_widget('menuitem2').set_sensitive(True) self.glade_file.get_widget('alignment6').hide() # We need to change the view mode if self.config['mode'] != newconf['mode'] or force_reload: Views(self.glade_file, newconf, newconf['mode'], force_reload) # And we need to show/hide columns in the playlist playlist = Playlist(self.glade_file, newconf, self.userconf) playlist.update_columns(newconf['columns']) # Hide playlist if view mode is webradios if newconf['mode'] == 'webradios': self.glade_file.get_widget('vpaned1').hide() self.glade_file.get_widget('scrolledwindow3').hide() self.glade_file.get_widget('toolbar4').hide()bluemindo-0.3/src/modules/explorer/views/lightweight.py0000644000175000017500000000511711224142355023362 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from threading import Thread from gobject import timeout_add, idle_add from gtk.gdk import threads_enter, threads_leave from time import sleep from modules.explorer.playlist import Playlist from modules.explorer.musicdb import MusicDatabase class LightweightView(object): ref = None ref2 = None def __new__(cls, *args, **kws): # Singleton if cls.ref is None: cls.ref = super(LightweightView, cls).__new__(cls, args, kws) return cls.ref def __init__(self, glade_file, config, userconf): self.config = config self.glade_file = glade_file # Start the playlist self.playlist = Playlist(glade_file, config, userconf) # Add all songs to the playlist self.reload_database() def reload_database(self, force_scan=False): pbar = self.glade_file.get_widget('progressbar1') pbar.set_text(_('Opening folder…')) pbar.show() thread = Thread(group=None, target=self.add_songs_to_playlist, name='artists', args=(pbar, force_scan)) self.thread_name = thread thread.start() def timeout_progress(self, pbar): if self.thread_name.isAlive(): pbar.pulse() return True else: pbar.hide() def add_songs_to_playlist(self, pbar, force_scan): """This function is called at the start-up and add songs.""" threads_enter() # Call database musicdb = MusicDatabase(self.config, pbar) if self.config['scan'] or force_scan: musicdb.scan() pbar.set_text(_('Loading music…')) self.database = musicdb.load() sleep(1) timeout_add(100, self.timeout_progress, pbar) self.playlist.clear_playlist() idle_add(self.playlist.add_songs, self.database) threads_leave()bluemindo-0.3/src/modules/explorer/views/webradios.py0000644000175000017500000003357311145664276023046 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gobject import timeout_add, markup_escape_text from threading import Thread from gettext import gettext as _ from gtk.gdk import Pixbuf as gPixbuf from gtk.glade import XML as glade_XML from gtk import (TreeViewColumn, ListStore, TreeStore, CellRendererText, STOCK_DIRECTORY, STOCK_FILE, STOCK_ABOUT, CellRendererPixbuf, ICON_SIZE_MENU, TREE_VIEW_COLUMN_FIXED) from cPickle import dump, load from urllib2 import urlopen from os.path import isfile, join, exists from os import makedirs, remove from common.webservices import Shoutcast from common.functions import Functions from modules.explorer.playlist import Playlist from modules.explorer import viewscommon from common.sqlite import SQLite unescape = viewscommon.unescape class WebradiosView(object): ref = None ref2 = None def __new__(cls, *args, **kws): # Singleton if cls.ref is None: cls.ref = super(WebradiosView, cls).__new__(cls, args, kws) return cls.ref def __init__(self, glade_file, config, userconf): # This playlist will not be used playlist = Playlist(glade_file, config, userconf) glade_file.get_widget('vpaned1').hide() glade_file.get_widget('scrolledwindow3').hide() glade_file.get_widget('toolbar4').hide() # Variables self.shoutcast = Shoutcast() self.glade_file = glade_file self.config = config self.userconf = userconf self.functions = Functions() # Show widgets self.glade_file.get_widget('vbox2').show() self.glade_file.get_widget('scrolledwindow10').show() self.glade_file.get_widget('scrolledwindow11').show() self.glade_file.get_widget('toolbar6').show() self.glade_file.get_widget('tool-radios-add').hide() self.glade_file.get_widget('tool-radios-delete').hide() self.glade_file.get_widget('tool-radios-refresh').hide() if WebradiosView.ref2 is None: WebradiosView.ref2 = 42 self.launch() def reload_database(self, force_scan=False): pass def launch(self): # Start GUI tview = self.glade_file.get_widget('treeview8') tview.connect('row-activated', self.load_radios_category) treestore = TreeStore(gPixbuf, str, str) tview.set_model(treestore) column = TreeViewColumn() render_text = CellRendererText() render_pixbuf = CellRendererPixbuf() column.pack_start(render_pixbuf, expand=False) column.add_attribute(render_pixbuf, 'pixbuf', 0) column.pack_start(render_text, expand=True) column.add_attribute(render_text, 'markup', 1) tview.append_column(column) # Append fav_title = '%s' % markup_escape_text(_('Bookmarks')) web_title = '%s' % markup_escape_text(_('World Wide Web')) logo = tview.render_icon(STOCK_ABOUT, ICON_SIZE_MENU) it_fav = treestore.append(None, (logo, fav_title, 'fav')) logo = tview.render_icon(STOCK_DIRECTORY, ICON_SIZE_MENU) it_web = treestore.append(None, (logo, web_title, 'web')) # Add categories pbar = self.glade_file.get_widget('progressbar1') pbar.set_text(_('Downloading…')) pbar.show() thread = Thread(group=None, target=self.add_categories, name='categories', args=(it_web, treestore, tview)) self.thread_name = thread timeout_add(100, self.timeout_progress, pbar) thread.start() # Radios tree tview = self.glade_file.get_widget('treeview9') tview.connect('row-activated', self.launch_radio) width = tview.get_allocation().width self.r_tstore = ListStore(str, str, int, str) self.r_tstore_f = self.r_tstore.filter_new(None) self.r_tstore_f.set_visible_func(self.filter_visible_radios) tview.set_model(self.r_tstore_f) column = TreeViewColumn() column.pack_start(render_text, expand=True) column.add_attribute(render_text, 'markup', 0) column.set_title(_('Name')) column.set_expand(True) column.set_min_width(int(width / 100. * 75.)) column.set_sizing(TREE_VIEW_COLUMN_FIXED) tview.append_column(column) column = TreeViewColumn() column.pack_start(render_text, expand=False) column.add_attribute(render_text, 'markup', 1) column.set_title(_('Listener count')) column.set_max_width(int(width / 100. * 25.)) tview.append_column(column) # GTK signals entry_vi = self.glade_file.get_widget('entry2') tool_add = self.glade_file.get_widget('tool-radios-add') tool_del = self.glade_file.get_widget('tool-radios-delete') tool_ref = self.glade_file.get_widget('tool-radios-refresh') entry_vi.connect('key-release-event', self.radios_filter, self.r_tstore_f) tool_add.connect('clicked', self.add_a_radio) tool_del.connect('clicked', self.delete_a_radio) tool_ref.connect('clicked', self.refresh_a_category) def add_a_radio(self, widget): """This function add a bookmark radio.""" txt = '%s' newwin = glade_XML(join(self.functions.datadir, 'glade', 'mainwindow.glade'), 'dialog1', domain='bluemindo') new_win = newwin.get_widget('dialog1') new_win.resize(200, 150) response = new_win.run() title = newwin.get_widget('entry3').get_text() url = newwin.get_widget('entry4').get_text() if response and title != '' and url != '': sql = SQLite() sql.execute('insert into radios (name, url) ' 'values (?, ?)', (title, url)) fnm = url.split('.') ext = fnm[len(fnm)-1] self.r_tstore.append((txt % markup_escape_text(title), txt % markup_escape_text(url), 0, ext)) sql.close() new_win.destroy() def delete_a_radio(self, widget): """This function deletes a bookmark radio.""" tview = self.glade_file.get_widget('treeview9') tstore = tview.get_model() (mod, iter_) = tview.get_selection().get_selected() if iter_ is not None: title = self.functions.clear_html(unescape(tstore[iter_][0])) url = self.functions.clear_html(unescape(tstore[iter_][1])) sql = SQLite() sql = sql.execute('delete from radios where ' 'name=? and url=?', (title, url)) sql.close() del mod[iter_] def refresh_a_category(self, widget): """This function refresh an already downloaded category.""" val = self.current_category genre_hash = self.functions.get_hash(val, 'webradios') catfile = join(self.config['__data-dir'], 'webradios', genre_hash) if isfile(catfile): remove(catfile) pbar = self.glade_file.get_widget('progressbar1') pbar.set_text(_('Downloading…')) pbar.show() tview = self.glade_file.get_widget('treeview9') treestore = tview.get_model() thread = Thread(group=None, target=self.add_radios, name='update_radios', args=(val, treestore, tview)) self.thread_name = thread timeout_add(100, self.timeout_progress, pbar) thread.start() def timeout_progress(self, pbar): if self.thread_name.isAlive(): pbar.pulse() return True else: pbar.hide() def add_categories(self, it_web, treestore, tview): """This function add all categories.""" logo = tview.render_icon(STOCK_FILE, ICON_SIZE_MENU) radios_dir = join(self.config['__data-dir'], 'webradios') categories_file = join(radios_dir, 'categories') if not isfile(categories_file): # Create directory if not exists(radios_dir): makedirs(radios_dir) # Get shoutcast list categories = self.shoutcast.get_categories() dump(categories, open(categories_file, 'w')) else: categories = load(open(categories_file, 'r')) for category in categories: treestore.append(it_web, (logo, markup_escape_text(category), markup_escape_text(category))) def load_radios_category(self, tview, path, column): """This function handles row activation.""" (mod, iter_) = tview.get_selection().get_selected() iter_has_depth = tview.get_model().iter_depth(iter_) val = unescape(mod.get_value(iter_, 2)) if not iter_has_depth and val == 'fav': # GUI update self.glade_file.get_widget('tool-radios-add').show() self.glade_file.get_widget('tool-radios-delete').show() self.glade_file.get_widget('tool-radios-refresh').hide() self.r_tstore.clear() sql = SQLite() radios = sql.execute('select * from radios') txt = '%s' for radio in radios: fnm = radio[1].split('.') ext = fnm[len(fnm)-1] self.r_tstore.append((txt % markup_escape_text(radio[0]), txt % markup_escape_text(radio[1]), 0, ext)) sql.close() else: self.current_category = val # GUI update self.glade_file.get_widget('tool-radios-add').hide() self.glade_file.get_widget('tool-radios-delete').hide() self.glade_file.get_widget('tool-radios-refresh').show() # Add radios pbar = self.glade_file.get_widget('progressbar1') pbar.set_text(_('Downloading…')) pbar.show() tview = self.glade_file.get_widget('treeview9') treestore = tview.get_model() thread = Thread(group=None, target=self.add_radios, name='radios', args=(val, treestore, tview)) self.thread_name = thread timeout_add(100, self.timeout_progress, pbar) thread.start() def add_radios(self, genre, tstore, tview): """This function add all channels of a category.""" txt = '%s' genre_hash = self.functions.get_hash(genre, 'webradios') radios_file = join(self.config['__data-dir'], 'webradios', genre_hash) if not isfile(radios_file): # Get shoutcast list radios = self.shoutcast.get_radios_by_category(genre) dump(radios, open(radios_file, 'w')) else: radios = load(open(radios_file, 'r')) # Sort the radios def add_zero(integer): integer = '0000000' + str(integer) return int(integer) radios.sort(lambda x,y:cmp(add_zero(x[2]), add_zero(y[2]))) radios.reverse() # Add radios self.r_tstore.clear() for radio in radios: self.r_tstore.append(( txt % markup_escape_text(self.map_str(radio[0])), txt % str(radio[2]), int(radio[1]), 'shoutcast')) def launch_radio(self, tview, path, column): """This function handles row activation.""" (mod, iter_) = tview.get_selection().get_selected() radio_type = unescape(mod.get_value(iter_, 3)) radio_id = unescape(str(mod.get_value(iter_, 2))) radio_title = unescape(mod.get_value(iter_, 0)) title = self.map_str(self.functions.clear_html(radio_title), 35) if radio_type == 'shoutcast': pls = self.shoutcast.get_radio_url(radio_id) url = self.shoutcast.get_file_in_pls(pls) elif radio_type == 'm3u': pls = self.functions.clear_html(unescape(mod.get_value(iter_, 1))) url = self.get_url_in_m3u(pls) else: print '%s is not handled by Bluemindo.' % radio_type return radio_infos = (title, url) self.config['__extensions'].load_event('OnPlayNewRadio', radio_infos) def map_str(self, string, limit=70): """This function returns the string with a limited size.""" if len(string) > limit: return string[:limit] + '…' else: return string def get_url_in_m3u(self, pls): """This function get the URL in a m3u file.""" urlo = urlopen(pls) content = urlo.read().split('\n') for line in content: if line.startswith('http://'): return line def filter_visible_radios(self, model, iter_): """This function returns True if the row have to be shown.""" if len(model) > 0: search = self.glade_file.get_widget('entry2').get_text() searchin = model.get_value(iter_, 0) if search and searchin: if search.upper() in searchin.upper(): return True else: return True def radios_filter(self, entry, event, tstore): """This function forces the filter.""" tstore.refilter() bluemindo-0.3/src/modules/explorer/views/basic.py0000644000175000017500000001413411224661214022123 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from threading import Thread from gobject import (TYPE_STRING as gString, markup_escape_text as escape, timeout_add) from gtk.gdk import Pixbuf as gPixbuf, threads_enter, threads_leave from gtk import(STOCK_DIRECTORY, STOCK_CDROM, STOCK_FILE, TreeStore, CellRendererText, TreeViewColumn, CellRendererPixbuf, ICON_SIZE_MENU) from modules.explorer.playlist import Playlist from modules.explorer.musicdb import MusicDatabase from modules.explorer import viewscommon unescape = viewscommon.unescape insensitive_sort = viewscommon.insensitive_sort class BasicView(object): ref = None ref2 = None def __new__(cls, *args, **kws): # Singleton if cls.ref is None: cls.ref = super(BasicView, cls).__new__(cls, args, kws) return cls.ref def __init__(self, glade_file, config, userconf): self.playlist = Playlist(glade_file, config, userconf) self.glade_file = glade_file self.config = config self.userconf = userconf # Show widget self.glade_file.get_widget('vbox2').show() self.glade_file.get_widget('scrolledwindow7').show() if BasicView.ref2 is None: BasicView.ref2 = 42 self.launch() def launch(self): treestore = TreeStore(gPixbuf, gString) column = TreeViewColumn() render_pixbuf = CellRendererPixbuf() column.pack_start(render_pixbuf, expand=False) column.add_attribute(render_pixbuf, 'pixbuf', 0) render_text = CellRendererText() column.pack_start(render_text, expand=True) column.add_attribute(render_text, 'markup', 1) tview = self.glade_file.get_widget('treeview7') tview.set_model(treestore) tview.append_column(column) tview.connect('row-activated', self.add_to_playlist) # Add artists self.reload_database() def reload_database(self, force_scan=False): tview = self.glade_file.get_widget('treeview7') treestore = tview.get_model() pbar = self.glade_file.get_widget('progressbar1') pbar.set_text(_('Opening folder…')) pbar.show() thread = Thread(group=None, target=self.insert_artists, name='artists', args=(pbar, treestore, tview, force_scan)) self.thread_name = thread thread.start() def timeout_progress(self, pbar): if self.thread_name.isAlive(): pbar.pulse() return True else: pbar.hide() def insert_artists(self, pbar, treestore, tview, force_scan): """This function is called at the start-up and add artists.""" threads_enter() # Call database musicdb = MusicDatabase(self.config, pbar) if self.config['scan'] or force_scan: musicdb.scan() pbar.set_text(_('Loading music…')) self.database = musicdb.load() timeout_add(100, self.timeout_progress, pbar) self.songs_tree = viewscommon.get_representation(self.database, self.config['artwork']) self.continue_to_pulse = False pbar.set_text(_('Loading artists…')) items = self.songs_tree.items() items.sort(insensitive_sort) items_sort = [key for key, value in items] treestore.clear() for artist in items_sort: logo = tview.render_icon(STOCK_DIRECTORY, ICON_SIZE_MENU) t = treestore.append(None, (logo, '%s' % escape(artist))) for album in self.songs_tree[artist]: logo = tview.render_icon(STOCK_CDROM, ICON_SIZE_MENU) u = treestore.append(t, (logo, '%s' % escape(album))) for song in self.songs_tree[artist][album]: logo = tview.render_icon(STOCK_FILE, ICON_SIZE_MENU) v = treestore.append(u, (logo, escape(song[0]))) threads_leave() def add_to_playlist(self, tview, path, column): """This function handles row activation.""" (mod, iter_) = tview.get_selection().get_selected() iter_has_depth = tview.get_model().iter_depth(iter_) if not iter_has_depth: artist_ = unescape(mod.get_value(iter_, 1))[3:-4] songs = [] for album in self.songs_tree[artist_]: for sgs in self.songs_tree[artist_][album]: songs.append(sgs) elif iter_has_depth == 1: artist = tview.get_model().iter_parent(iter_) artist_ = unescape(mod.get_value(artist, 1))[3:-4] album_ = unescape(mod.get_value(iter_, 1))[3:-4] songs = [] for sgs in self.songs_tree[artist_][album_]: songs.append(sgs) elif iter_has_depth == 2: album = tview.get_model().iter_parent(iter_) artist = tview.get_model().iter_parent(album) song_ = unescape(mod.get_value(iter_, 1)) artist_ = unescape(mod.get_value(artist, 1))[3:-4] album_ = unescape(mod.get_value(album, 1))[3:-4] songs = [] for sgs in self.songs_tree[artist_][album_]: if song_ == sgs[0]: songs.append(sgs) try: self.playlist.add_songs(songs) except NameError: pass bluemindo-0.3/src/modules/explorer/views/full.py0000644000175000017500000002605611224661214022012 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from thread import start_new_thread from threading import Thread from os.path import join, isfile from gobject import (TYPE_STRING as gString, markup_escape_text, idle_add, timeout_add, threads_init) from gtk.glade import XML as glade_XML from gtk.gdk import (Pixbuf as gPixbuf, pixbuf_new_from_file, INTERP_BILINEAR, threads_enter, threads_leave) from gtk import (ListStore, TreeViewColumn, CellRendererPixbuf, CellRendererText, STOCK_ORIENTATION_PORTRAIT, ICON_SIZE_LARGE_TOOLBAR, Tooltip, VBox) from modules.explorer.playlist import Playlist from modules.explorer.musicdb import MusicDatabase from modules.explorer import viewscommon unescape = viewscommon.unescape insensitive_sort = viewscommon.insensitive_sort from common.functions import Functions from common.webservices import LastFm from common.statistics import Statistics class FullView(object): ref = None ref2 = None def __new__(cls, *args, **kws): # Singleton if cls.ref is None: cls.ref = super(FullView, cls).__new__(cls, args, kws) return cls.ref def __init__(self, glade_file, config, userconf): self.playlist = Playlist(glade_file, config, userconf) self.glade_file = glade_file self.config = config self.userconf = userconf self.player_data = join(self.userconf.datadir, 'modules', 'player') self.functions = Functions() self.lastfm = LastFm() # Show widget self.glade_file.get_widget('vbox2').show() self.glade_file.get_widget('scrolledwindow1').show() if FullView.ref2 is None: FullView.ref2 = 42 self.launch() def launch(self): threads_init() treestore = ListStore(gPixbuf, gString, gString) tview = self.glade_file.get_widget('treeview1') tview.set_model(treestore) tview.set_rules_hint(True) # GTK Handlers tview.connect('row-activated', self.load_item) tview.get_selection().connect('changed', self.load_artist_covers) column = TreeViewColumn() render_pixbuf = CellRendererPixbuf() column.pack_start(render_pixbuf, expand=False) column.add_attribute(render_pixbuf, 'pixbuf', 0) tview.append_column(column) column = TreeViewColumn() render_text = CellRendererText() column.pack_start(render_text, expand=True) column.add_attribute(render_text, 'markup', 1) tview.append_column(column) column = TreeViewColumn() render_text = CellRendererText() column.pack_start(render_text, expand=False) column.add_attribute(render_text, 'markup', 2) column.set_visible(False) tview.append_column(column) # Tooltips tview.connect('query-tooltip', self.query_tooltip) # Add artists self.reload_database() def reload_database(self, force_scan=False): tview = self.glade_file.get_widget('treeview1') treestore = tview.get_model() pbar = self.glade_file.get_widget('progressbar1') pbar.set_text(_('Opening folder…')) pbar.show() thread = Thread(group=None, target=self.insert_artists, name='artists', args=(pbar, treestore, force_scan)) self.thread_name = thread thread.start() def timeout_progress(self, pbar): if self.thread_name.isAlive(): pbar.pulse() return True else: pbar.hide() def insert_artists(self, pbar, treestore, force_scan): """This function is called at the start-up and add artists.""" threads_enter() # Call database musicdb = MusicDatabase(self.config, pbar) if self.config['scan'] or force_scan: musicdb.scan() pbar.set_text(_('Loading music…')) self.database = musicdb.load() timeout_add(100, self.timeout_progress, pbar) self.songs_tree = viewscommon.get_representation(self.database, self.config['artwork']) self.continue_to_pulse = False pbar.set_text(_('Loading artists…')) items = self.songs_tree.items() items.sort(insensitive_sort) items_sort = [key for key, value in items] treestore.clear() artists_list = [] for artist in items_sort: artists_list.append(artist) artist_ = markup_escape_text(artist) artist_picture = join(self.config['__data-dir'], 'artists', self.functions.get_hash(artist, 'picture')) if isfile(artist_picture): logo = pixbuf_new_from_file(artist_picture) logo = logo.scale_simple(24, 24, INTERP_BILINEAR) else: logo = None treestore.append((logo, artist_, 'artist')) if self.config['artwork']: start_new_thread(self.lastfm.get_pictures, (artists_list,)) threads_leave() def show_albums_covers(self, artist): """This function shows all artist's arts.""" artist = unescape(unicode(artist)) def load_album_in_playlist(widget, alb): """This function loads an album in the playlist.""" songs = [] for song in self.songs_tree[str(artist)][alb]: songs.append(song) self.playlist.add_songs(songs) # Show the albums container self.glade_file.get_widget('hbox6').show() self.glade_file.get_widget('frame2').show() self.glade_file.get_widget('scrolledwindow4').hide() self.glade_file.get_widget('scrolledwindow5').hide() self.glade_file.get_widget('label7').set_markup( '' + markup_escape_text(unicode(artist)) + '') # Delete all buttons container = self.glade_file.get_widget('hbuttonbox1') for wdg in container: container.remove(wdg) # Sort albums ordered_albums = [] for album in self.songs_tree[str(artist)]: ordered_albums.append(album) ordered_albums.sort() # Add a button for each albums, based on the model for album in ordered_albums: widgets_ = glade_XML(join(self.functions.datadir, 'glade', 'mainwindow.glade'), 'button2', domain='bluemindo') album_button = widgets_.get_widget('button2') # Add the album name and the cover widgets_.get_widget('label8').set_markup( '' + markup_escape_text(unicode(album)) + '') _file = join(self.player_data, 'covers', self.functions.get_hash(album, artist)) if isfile(_file): icon = pixbuf_new_from_file(_file) icon = icon.scale_simple(120, 100, INTERP_BILINEAR) else: icon = pixbuf_new_from_file(join(self.functions.datadir, 'image', 'logo_head_big.png')) icon = icon.scale_simple(85, 85, INTERP_BILINEAR) widgets_.get_widget('image5').set_from_pixbuf(icon) album_button.show_all() self.glade_file.get_widget('hbuttonbox1').add(album_button) # GTK handler album_button.connect('clicked', load_album_in_playlist, album) def load_item(self, tview, path, column): """This function handles the artists or playlists loading.""" (mod, iter_) = tview.get_selection().get_selected() value = mod.get_value(iter_, 1) typ = mod.get_value(iter_, 2) if typ in 'artist': self.show_albums_covers(value) artist = unescape(unicode(value)) songs = [] for alb in self.songs_tree[str(artist)]: for sg in self.songs_tree[str(artist)][alb]: songs.append(sg) # Finally insert songs in playlist :) self.playlist.add_songs(songs) def load_artist_covers(self, selection): """This function loads covers for an artist.""" if selection: (mod, iter_) = selection.get_selected() if (iter_ and mod.get_value(iter_, 2) not in ('title', 'playlist')): artist = unescape(unicode(mod.get_value(iter_, 1))) self.show_albums_covers(artist) def query_tooltip(self, widget, x, y, keyboard_tip, tooltip): """Create a tooltip.""" try: (path, col, x, y) = widget.get_path_at_pos(int(x), int(y)) it = widget.get_model().get_iter(path) value = unescape(widget.get_model().get_value(it, 1)) kind = widget.get_model().get_value(it, 2) wdg = glade_XML(join(self.functions.datadir, 'glade', 'mainwindow.glade'), 'window2') picture_wdg = wdg.get_widget('image8') artist_picture = join(self.config['__data-dir'], 'artists', self.functions.get_hash(value, 'picture')) if isfile(artist_picture): logo = pixbuf_new_from_file(artist_picture) logo = logo.scale_simple(150, 120, INTERP_BILINEAR) picture_wdg.set_from_pixbuf(logo) else: picture_wdg.hide() wdg.get_widget('label9').set_markup('%s' % markup_escape_text(unicode(value))) # Count albums and songs nb_albums = 0 nb_songs = 0 for alb in self.songs_tree[value]: nb_albums += 1 for sg in self.songs_tree[value][alb]: nb_songs += 1 wdg.get_widget('label10').set_text(_('%d albums') % nb_albums) wdg.get_widget('label11').set_text(_('%d songs') % nb_songs) # Show statistics stats = Statistics() nb_times = stats.get_stats_for_artist(value) wdg.get_widget('label17').set_markup(_('%d plays') % nb_times) vbox = VBox() hbox = wdg.get_widget('hbox7') hbox.reparent(vbox) tooltip.set_custom(vbox) return True except (TypeError, ValueError): passbluemindo-0.3/src/modules/explorer/views/albums.py0000644000175000017500000002124511224661214022326 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from threading import Thread from gobject import (TYPE_STRING as gString, markup_escape_text as escape, timeout_add, markup_escape_text, idle_add) from gtk.gdk import (Pixbuf as gPixbuf, threads_enter, threads_leave, pixbuf_new_from_file, INTERP_BILINEAR) from gtk import(STOCK_DIRECTORY, STOCK_CDROM, STOCK_FILE, TreeStore, CellRendererText, TreeViewColumn, CellRendererPixbuf, ICON_SIZE_MENU, ListStore) from gtk.glade import XML as glade_XML from os.path import join, isfile from common.functions import Functions from modules.explorer.playlist import Playlist from modules.explorer.musicdb import MusicDatabase from modules.explorer import viewscommon unescape = viewscommon.unescape insensitive_sort = viewscommon.insensitive_sort class AlbumsView(object): ref = None ref2 = None def __new__(cls, *args, **kws): # Singleton if cls.ref is None: cls.ref = super(AlbumsView, cls).__new__(cls, args, kws) return cls.ref def __init__(self, glade_file, config, userconf): self.playlist = Playlist(glade_file, config, userconf) self.glade_file = glade_file self.config = config self.userconf = userconf self.player_data = join(self.userconf.datadir, 'modules', 'player') self.functions = Functions() # Show widget self.glade_file.get_widget('vbox2').show() self.glade_file.get_widget('vbox11').show() if AlbumsView.ref2 is None: AlbumsView.ref2 = 42 self.launch() def launch(self): self.glade_file.get_widget('checkbutton1').set_active(True) def click(widget): self.reload_database() self.glade_file.get_widget('checkbutton1').connect('clicked', click) def clear_filter(widget): self.glade_file.get_widget('entry5').set_text('') self.glade_file.get_widget('button6').connect('clicked', clear_filter) self.glade_file.get_widget('entry5').connect('changed', self.filter_) # Add albums self.filter_widgets = [] self.reload_database() def reload_database(self, force_scan=False): pbar = self.glade_file.get_widget('progressbar1') pbar.set_text(_('Opening folder…')) pbar.show() thread = Thread(group=None, target=self.insert_albums, name='albums', args=(pbar, force_scan)) self.thread_name = thread thread.start() def timeout_progress(self, pbar): if self.thread_name.isAlive(): pbar.pulse() return True else: pbar.hide() def insert_albums(self, pbar, force_scan): """This function is called at the start-up and add artists.""" threads_enter() # Call database musicdb = MusicDatabase(self.config, pbar) if self.config['scan'] or force_scan: musicdb.scan() pbar.set_text(_('Loading music…')) self.database = musicdb.load() timeout_add(100, self.timeout_progress, pbar) self.songs_tree = viewscommon.get_representation(self.database, self.config['artwork']) self.continue_to_pulse = False pbar.set_text(_('Loading artists…')) group_artists = self.glade_file.get_widget('checkbutton1').get_active() self.glade_file.get_widget('checkbutton1').set_sensitive(False) self.glade_file.get_widget('entry5').set_sensitive(False) self.glade_file.get_widget('button6').set_sensitive(False) self.albums_tree = [] album_names = [] album_iter = {} i = 0 for art in self.songs_tree: for alb in self.songs_tree[art]: if group_artists: if alb not in album_names: album_names.append(alb) self.albums_tree.append({'album': alb, 'artist': [art]}) album_iter[alb] = i i = i + 1 else: it = album_iter[alb] al = self.albums_tree[it] if art not in al['artist']: al['artist'].append(art) else: album_names.append(alb) self.albums_tree.append({'album': alb, 'artist': [art]}) i = i + 1 self.albums_tree.sort(insensitive_sort) Thread(group=None, target=self.show_albums, name='add_albums', args=tuple()).start() threads_leave() def show_albums(self): # Delete all buttons container = self.glade_file.get_widget('vbuttonbox1') for wdg in container: idle_add(container.remove, wdg) for alb in self.albums_tree: widgets = glade_XML(join(self.functions.datadir, 'glade', 'mainwindow.glade'), 'button5', domain='bluemindo') button = widgets.get_widget('button5') album_ = markup_escape_text(unicode(alb['album'])) if len(alb['artist']) > 1: artist = '%d artists' % len(alb['artist']) else: artist = markup_escape_text(unicode(alb['artist'][0])) bdir = join(self.player_data, 'covers') base = join(self.functions.datadir, 'image', 'logo_head_big.png') if len(alb['artist']) > 1: for art in alb['artist']: cover = join(bdir, self.functions.get_hash(album_, art)) if isfile(cover): break else: cover = join(bdir, self.functions.get_hash(album_, artist)) if not isfile(cover): icon = pixbuf_new_from_file(base) else: icon = pixbuf_new_from_file(cover) scaled_icon = icon.scale_simple(24, 24, INTERP_BILINEAR) idle_add(widgets.get_widget('image6').set_from_pixbuf, scaled_icon) idle_add(widgets.get_widget('label18').set_markup, '%s%s%s' % ('', album_, '')) idle_add(widgets.get_widget('label19').set_markup, '%s%s%s' % ('', artist, '')) idle_add(button.show_all) idle_add(container.add, button) self.filter_widgets.append([button, album_, alb['artist']]) button.connect('clicked', self.click_album) self.filter_(self.glade_file.get_widget('entry5')) self.glade_file.get_widget('checkbutton1').set_sensitive(True) self.glade_file.get_widget('button6').set_sensitive(True) self.glade_file.get_widget('entry5').set_sensitive(True) self.glade_file.get_widget('entry5').grab_focus() def filter_(self, widget): text = widget.get_text().upper() for wdg in self.filter_widgets: if text not in wdg[1].upper(): wdg[0].hide() else: wdg[0].show() def click_album(self, widget): for wdg in self.filter_widgets: if wdg[0] == widget: artist = eval(unescape(unicode(wdg[2]))) album = unescape(unicode(wdg[1])) break group_artists = self.glade_file.get_widget('checkbutton1').get_active() songs = [] if group_artists: for art in self.songs_tree: for alb in self.songs_tree[art]: if alb == album: for sg in self.songs_tree[art][alb]: songs.append(sg) else: for sg in self.songs_tree[artist[0]][str(album)]: songs.append(sg) if len(songs) > 0: songs.sort(lambda x,y:cmp(x[6], y[6])) self.playlist.add_songs(songs, False)bluemindo-0.3/src/modules/explorer/views/playlists.py0000644000175000017500000002663411145664276023113 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from gtk.gdk import Pixbuf as gPixbuf from gobject import TYPE_STRING as gString from gtk import (ListStore, TreeViewColumn, CellRendererPixbuf, STOCK_ABOUT, CellRendererText, ICON_SIZE_MENU, STOCK_REFRESH, Dialog, STOCK_OK, RESPONSE_OK, STOCK_CANCEL, RESPONSE_CANCEL, HBox, image_new_from_stock, STOCK_DIALOG_QUESTION, Label, ICON_SIZE_DIALOG, FileFilter, FileChooserDialog, FILE_CHOOSER_ACTION_OPEN, FILE_CHOOSER_ACTION_SAVE, STOCK_OPEN, STOCK_SAVE_AS) from gtk.glade import XML as glade_XML from random import randint from os.path import join, exists import tagpy from common.statistics import Statistics from common.functions import Functions from common.playlists import Playlists playlists_management = Playlists() from modules.explorer.playlist import Playlist from modules.explorer.musicdb import MusicDatabase class PlaylistsView(object): ref = None ref2 = None def __new__(cls, *args, **kws): # Singleton if cls.ref is None: cls.ref = super(PlaylistsView, cls).__new__(cls, args, kws) return cls.ref def __init__(self, glade_file, config, userconf): self.playlist = Playlist(glade_file, config, userconf) self.glade_file = glade_file self.config = config self.userconf = userconf self.functions = Functions() self.statistics = Statistics() self.musicdb = MusicDatabase(self.config, None) # Show widgets self.glade_file.get_widget('vbox2').show() self.glade_file.get_widget('frame4').show() if PlaylistsView.ref2 is None: PlaylistsView.ref2 = 42 self.launch() def reload_database(self, force_scan=False): pass def launch(self): # Start GUI label_automatic = self.glade_file.get_widget('label12') label_saved = self.glade_file.get_widget('label13') label_automatic.set_markup('Automatic') label_saved.set_markup('Playlists') treestore = ListStore(gPixbuf, gString, gString) auto_tview = self.glade_file.get_widget('treeview5') auto_tview.set_model(treestore) auto_tview.set_rules_hint(True) render_pixbuf = CellRendererPixbuf() render_text = CellRendererText() auto_column = TreeViewColumn() auto_column.pack_start(render_pixbuf, expand=False) auto_column.add_attribute(render_pixbuf, 'pixbuf', 0) auto_column.pack_start(render_text, expand=True) auto_column.add_attribute(render_text, 'markup', 1) auto_tview.append_column(auto_column) logo_t = auto_tview.render_icon(STOCK_ABOUT, ICON_SIZE_MENU) logo_r = auto_tview.render_icon(STOCK_REFRESH, ICON_SIZE_MENU) treestore.append((logo_t, _('Top 50 songs'), 's50')) treestore.append((logo_t, _('Top 100 songs'), 's100')) treestore.append((logo_t, _('Top 10 albums'), 'a10')) treestore.append((logo_r, _('Random 50 songs'), 'r50')) treestore.append((logo_r, _('Random 100 songs'), 'r100')) _treestore = ListStore(gString) saved_tview = self.glade_file.get_widget('treeview6') saved_tview.set_model(_treestore) saved_tview.set_rules_hint(True) saved_column = TreeViewColumn() saved_column.pack_start(render_text, expand=True) saved_column.add_attribute(render_text, 'markup', 0) saved_tview.append_column(saved_column) playlists_list = playlists_management.get_playlists_list() for playlist_name in playlists_list: _treestore.append((playlist_name,)) # Connect GTK signals auto_tview.get_selection().connect('changed', self.load_playlist, 'a') saved_tview.get_selection().connect('changed', self.load_playlist, 's') tool_add = self.glade_file.get_widget('tool-playlists-add') tool_add.connect('clicked', self.add_playlist, _treestore) tool_remove = self.glade_file.get_widget('tool-playlists-remove') tool_remove.connect('clicked', self.remove_playlist, _treestore, saved_tview) tool_import = self.glade_file.get_widget('tool-playlists-import') tool_import.connect('clicked', self.import_playlist, _treestore) tool_export = self.glade_file.get_widget('tool-playlists-export') tool_export.connect('clicked', self.export_playlist, saved_tview) def load_playlist(self, selection, pltype): """Load a selected playlist.""" if selection: (mod, iter_) = selection.get_selected() if not iter_: return self.playlist.clear_playlist(None) songs = [] if pltype == 'a': # Automatic playlist pls = mod.get_value(iter_, 2) songs_list = self.musicdb.load() if pls == 's50': # Top 50 songs songs = self.statistics.get_top() elif pls == 's100': # Top 100 songs songs = self.statistics.get_top('filename', 100) elif pls == 'a10': # Top 10 albums albums = self.statistics.get_top('album', 10) songs_list.sort(lambda x,y:cmp(x[6], y[6])) for album in albums: for sg in songs_list: if sg[2] == album[0]: songs.append(sg[8]) elif pls in ('r50', 'r100'): # Random playlists if pls == 'r50': # Random 50 songs maxsongs = 50 elif pls == 'r100': # Random 100 songs maxsongs = 100 randoms = [] i = 0 while i <= maxsongs: randoms.append(randint(0, len(songs_list))) i += 1 for randid in randoms: songs.append(songs_list[randid][8]) else: # Saved playlist pls = mod.get_value(iter_, 0) songs = playlists_management.load_playlist(pls) # Get audio tags new_playlist = [] for song in songs: if type(song) == tuple: song = song[0] if exists(song): tagpy_file = tagpy.FileRef(str(song)) tags = tagpy_file.tag() audio = tagpy_file.audioProperties() title = tags.title artist = tags.artist album = tags.album comment = tags.comment genre = tags.genre year = tags.year track = tags.track length = audio.length new_playlist.append((title, artist, album, comment, genre, year, track, length, song)) if len(new_playlist) > 0: # Add songs to playlist self.playlist.add_songs(new_playlist, False, pls) def add_playlist(self, widget, treestore): """Create a new playlist.""" newwin = glade_XML(join(self.functions.datadir, 'glade', 'playlistmenu.glade'), 'dialog1', domain='bluemindo') new_win = newwin.get_widget('dialog1') response = new_win.run() text = newwin.get_widget('entry1').get_text() if response and text: playlists_management.create_new_playlist(text) treestore.append((text,)) new_win.destroy() def remove_playlist(self, widget, treestore, treeview): """Remove a playlist.""" selection = treeview.get_selection() if selection: (mod, iter_) = selection.get_selected() if not iter_: return playlist = mod.get_value(iter_, 0) title = _('Delete the playlist?') dialog = Dialog(title, None, 0,(STOCK_OK, RESPONSE_OK, STOCK_CANCEL, RESPONSE_CANCEL)) hbox = HBox(False, 8) hbox.set_border_width(8) dialog.vbox.pack_start(hbox, False, False, 0) stock = image_new_from_stock(STOCK_DIALOG_QUESTION, ICON_SIZE_DIALOG) hbox.pack_start(stock, False, False, 0) label = Label(_('Do you really want to delete ' 'the %s playlist?') % playlist) hbox.pack_start(label, False, False, 0) dialog.show_all() reponse = dialog.run() if reponse == RESPONSE_OK: playlists_management.delete_playlist(playlist) del treestore[iter_] dialog.destroy() def import_playlist(self, widget, treestore): """Import a playlist into Bluemindo.""" fcd_filter = FileFilter() fcd_filter.add_mime_type('audio/x-mpegurl') fcd_filter.set_name(_('Winamp-like playlists')) fcd_playlist = FileChooserDialog(_('Choose a playlist'), None, FILE_CHOOSER_ACTION_OPEN, (STOCK_CANCEL, RESPONSE_CANCEL, STOCK_OPEN, RESPONSE_OK )) fcd_playlist.add_filter(fcd_filter) playlist = fcd_playlist.run() if playlist == RESPONSE_OK: filename = fcd_playlist.get_filename() playlist_name = playlists_management.import_playlist(filename) treestore.append((playlist_name,)) fcd_playlist.destroy() def export_playlist(self, widget, treeview): """Export a playlist from Bluemindo.""" selection = treeview.get_selection() if selection: (mod, iter_) = selection.get_selected() if not iter_: return playlist = mod.get_value(iter_, 0) fcd_filter = FileFilter() fcd_filter.add_mime_type('audio/x-mpegurl') fcd_filter.set_name(_('Winamp-like playlists')) fcd_playlist = FileChooserDialog(_('Export playlist as…'), None, FILE_CHOOSER_ACTION_SAVE,(STOCK_CANCEL, RESPONSE_CANCEL, STOCK_SAVE_AS, RESPONSE_OK)) fcd_playlist.add_filter(fcd_filter) _playlist = fcd_playlist.run() if _playlist == RESPONSE_OK: filename = fcd_playlist.get_filename() playlists_management.export_playlist(playlist, filename) fcd_playlist.destroy()bluemindo-0.3/src/modules/explorer/editsongwindow.glade0000644000175000017500000004332611150355570023405 0ustar xbrightxbright 400 300 True window1 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 GTK_SHADOW_NONE True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 7 2 5 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 100 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK -1 0 3000 1 10 10 True False 1 2 6 7 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 100 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 0 100 1 10 10 True False 1 2 5 6 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Year: 6 7 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Track: 5 6 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 2 4 5 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Genre: 4 5 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Title: True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Artist: 1 2 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Album: 2 3 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 2 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 2 1 2 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 2 2 3 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Comment: 3 4 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 2 3 4 False True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Edit a song</b> True label_item True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False 5 1 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK GTK_BUTTONBOX_EDGE True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-cancel True 0 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-ok True 0 1 False 5 2 bluemindo-0.3/src/modules/explorer/configuration.glade0000644000175000017500000004071711130010127023171 0ustar xbrightxbright 400 300 True window1 False True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 2 10 True 1 2 3 4 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Yes 0 True 1 2 2 3 170 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 0.15000000596046448 Fetch artworks: 2 3 GTK_FILL GTK_FILL 170 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 0.15000000596046448 View mode: 3 4 GTK_FILL GTK_FILL True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 3 3 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Year 0 True 2 3 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Comment 0 True 2 3 1 2 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Length 0 True 1 2 2 3 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Album 0 True 1 2 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Genre 0 True 1 2 1 2 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Artist 0 True 2 3 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Title 0 True 1 2 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Track 0 True 1 2 4 5 170 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 0.15000000596046448 Show columns: 4 5 GTK_FILL GTK_FILL True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Yes 0 True 1 2 1 2 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER 1 2 170 20 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2.2351741291171123e-10 Folder: GTK_FILL GTK_FILL 170 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Scan at startup: 1 2 GTK_FILL GTK_FILL False bluemindo-0.3/src/modules/explorer/editsong.py0000644000175000017500000001075211150355570021526 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gobject import markup_escape_text from gtk.glade import XML as glade_XML from os import getcwd from os.path import join import tagpy from common.sqlite import SQLite class EditSong(object): def __init__(self): self.song_to_edit = None def edit_song_datas(self, song): """Edit datas of a song.""" self.song_to_edit = song self.widgets = glade_XML(join(getcwd(), 'modules', 'explorer', 'editsongwindow.glade'), 'window1', domain='bluemindo') self.window = self.widgets.get_widget('window1') lbl_title = self.widgets.get_widget('label1') replace = (markup_escape_text(song[0]), markup_escape_text(song[1])) lbl_title.set_markup('Edit %s - %s' % replace) self.window.set_title('Edit %s - %s' % replace) entry_title = self.widgets.get_widget('entry1') entry_artist = self.widgets.get_widget('entry2') entry_album = self.widgets.get_widget('entry3') entry_comment = self.widgets.get_widget('entry6') entry_genre = self.widgets.get_widget('entry5') entry_title.set_text(song[0]) entry_artist.set_text(song[1]) entry_album.set_text(song[2]) entry_comment.set_text(song[3]) entry_genre.set_text(song[4]) spin_track = self.widgets.get_widget('spinbutton1') spin_year = self.widgets.get_widget('spinbutton2') spin_track.set_value(float(song[6])) spin_year.set_value(float(song[5])) button_cancel = self.widgets.get_widget('button-cancel') button_validate = self.widgets.get_widget('button-validate') button_cancel.connect('clicked', self.cancel) button_validate.connect('clicked', self.validate) def cancel(self, widget): """Cancel and close""" self.window.destroy() self.song_to_edit = None def validate(self, widget): """Write the new datas on the file.""" filename = self.song_to_edit[8] entry_title = self.widgets.get_widget('entry1') entry_artist = self.widgets.get_widget('entry2') entry_album = self.widgets.get_widget('entry3') entry_comment = self.widgets.get_widget('entry6') entry_genre = self.widgets.get_widget('entry5') spin_track = self.widgets.get_widget('spinbutton1') spin_year = self.widgets.get_widget('spinbutton2') title = str(entry_title.get_text()) artist = str(entry_artist.get_text()) album = str(entry_album.get_text()) comment = str(entry_comment.get_text()) genre = str(entry_genre.get_text()) track = int(spin_track.get_value()) year = int(spin_year.get_value()) # tagpy initialization filetag = tagpy.FileRef(filename) tags = filetag.tag() tags.title = title tags.artist = artist tags.album = album tags.comment = comment tags.genre = genre tags.track = track tags.year = year filetag.save() # SQL modifications sqlite = SQLite() sqlite.execute('update songs set title ="%(title)s", ' 'artist ="%(artist)s", album ="%(album)s", ' 'comment ="%(comment)s", genre ="%(genre)s", ' 'track ="%(track)s", year="%(year)s" where ' 'filename ="(%(filename)s"' % {'title': title, 'artist': artist, 'album': album, 'comment': comment, 'genre': genre, 'track': str(track), 'year': str(year), 'filename': filename }) sqlite.close() # Close everything self.cancel(None)bluemindo-0.3/src/modules/explorer/__init__.py0000644000175000017500000001553711223756231021457 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from ConfigParser import ConfigParser, NoSectionError from gtk.gdk import CONTROL_MASK from gtk import (STOCK_DIRECTORY, settings_get_default, AccelGroup, accel_map_change_entry) from random import randrange from os.path import join, isdir from os import makedirs from common.config import ConfigLoader from modules.explorer.views import Views from modules.explorer.config import Config from modules.explorer.musicdb import MusicDatabase class Explorer: def __init__(self, extensionsloader): self.extensions = extensionsloader self.module = {'name': 'Explorer', 'logo': STOCK_DIRECTORY, 'configurable': True} # Start configuration name = self.module['name'].lower() + '.cfg' self.config = ConfigLoader() self.config_file = join(self.config.confdir, 'modules', name) self.data_dir = join(self.config.datadir, 'modules', 'explorer') if not isdir(self.data_dir): makedirs(self.data_dir) def start_module(self): """This function starts the module.""" def launch_explorer(glade_file): """This function launch the explorer.""" self.widgets = glade_file # Create the View object config = self.get_configuration() self.view = Views(glade_file, config) self.cfg = Config(self.module, self.view, self.config_file, self.get_configuration) pbar = glade_file.get_widget('progressbar1') self.musicdb = MusicDatabase(config, pbar) # Change view menu menubar = self.widgets.get_widget('viewmenu') views = dict() settings = settings_get_default() settings.set_property('gtk-can-change-accels', True) ag = AccelGroup() menubar.set_accel_group(ag) window = self.widgets.get_widget('window1') window.add_accel_group(ag) # Add accelerators menubar.set_accel_path('
/_Show') accel_map_change_entry('
/_Show/Lightweight', ord('l'), CONTROL_MASK, True) accel_map_change_entry('
/_Show/Basic', ord('b'), CONTROL_MASK, True) accel_map_change_entry('
/_Show/Normal', ord('n'), CONTROL_MASK, True) accel_map_change_entry('
/_Show/Full', ord('f'), CONTROL_MASK, True) accel_map_change_entry('
/_Show/Albums', ord('u'), CONTROL_MASK, True) accel_map_change_entry('
/_Show/Playlists', ord('p'), CONTROL_MASK, True) accel_map_change_entry('
/_Show/Webradios', ord('w'), CONTROL_MASK, True) # Connect menu items views['lightweight'] = self.widgets.get_widget('menu-view-lightweight') views['basic'] = self.widgets.get_widget('menu-view-basic') views['normal'] = self.widgets.get_widget('menu-view-normal') views['full'] = self.widgets.get_widget('menu-view-full') views['albums'] = self.widgets.get_widget('menu-view-albums') views['playlists'] = self.widgets.get_widget('menu-view-playlists') views['webradios'] = self.widgets.get_widget('menu-view-webradio') # Check the radio button if config['mode'] == 'lightweight': views['lightweight'].set_active(True) elif config['mode'] == 'basic': views['basic'].set_active(True) elif config['mode'] == 'normal': views['normal'].set_active(True) elif config['mode'] == 'full': views['full'].set_active(True) elif config['mode'] == 'albums': views['albums'].set_active(True) elif config['mode'] == 'playlists': views['playlists'].set_active(True) else: views['webradios'].set_active(True) def on_view_change(widget, view_name): conf = self.get_configuration() Views(self.widgets, conf, view_name) for view in views: views[view].connect('activate', on_view_change, view) def play_item(): songs_list = self.musicdb.load() if len(songs_list) > 1: index = randrange(len(songs_list) - 1) song = songs_list[index] self.extensions.load_event('OnPlayNewSong', song) def configuration(args): """Load the configuration interface.""" self.cfg.configuration(args) def save_config(args): """Load the configuration interface.""" self.cfg.save_config(args) # Connect to Bluemindo's signals self.extensions.connect('OnBluemindoStarted', launch_explorer) self.extensions.connect('OnPlayPressedWithoutQueue', play_item) self.extensions.connect('OnModuleConfiguration', configuration) self.extensions.connect('OnModuleConfigurationSave', save_config) def get_configuration(self): configparser = ConfigParser() configparser.read(self.config_file) config = {} try: for item in configparser.items(self.module['name']): if item[1].isdigit(): value = int(item[1]) else: value = str(item[1]) config[item[0]] = value except NoSectionError: config['repeat'] = True config['artwork'] = True config['scan'] = False config['mode'] = 'full' config['folder'] = '' config['columns'] = 'track,title,artist,album,length' name = self.module['name'].lower() + '.cfg' config['__config-file'] = self.config_file config['__data-dir'] = self.data_dir config['__module-name'] = self.module['name'] config['__extensions'] = self.extensions return configbluemindo-0.3/src/modules/explorer/playlist.py0000644000175000017500000004002611224142355021545 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from os.path import join from random import shuffle as random_shuffle from ConfigParser import ConfigParser, NoSectionError from gobject import (TYPE_STRING as gString, markup_escape_text) from gtk.glade import XML as glade_XML from gtk import (ListStore, TreeViewColumn, CellRendererText, TREE_VIEW_COLUMN_FIXED, MenuItem) from common.functions import Functions from common.playlists import Playlists from modules.explorer.editsong import EditSong from modules.explorer import viewscommon unescape = viewscommon.unescape class Playlist(object): ref = None ref2 = None def __new__(cls, *args, **kws): # Singleton if cls.ref is None: cls.ref = super(Playlist, cls).__new__(cls, args, kws) return cls.ref def __init__(self, glade_file, config, userconf): glade_file.get_widget('vpaned1').show() glade_file.get_widget('scrolledwindow3').show() glade_file.get_widget('toolbar4').show() if Playlist.ref2 is None: Playlist.ref2 = 42 self.launch(glade_file, config, userconf) def launch(self, glade_file, config, userconf): self.glade_file = glade_file self.config = config self.userconf = userconf self.extensions = self.config['__extensions'] self.functions = Functions() self.playlists_management = Playlists() self.column_status = {0: True, 1: True, 2: True, 3: True, 4: True, 5: True, 6: True, 7: True } # Show related widgets self.glade_file.get_widget('vpaned1').show() self.glade_file.get_widget('hbox6').hide() self.glade_file.get_widget('toolbar4').show() # Customize the TreeView playlist_win_tree = self.glade_file.get_widget('treeview2') playlist_win_tree.set_rules_hint(True) playlist_win_tree.connect('row-activated', self.load_song) # Right click context menu playlist_win_tree.connect('button-release-event', self.context_menu, playlist_win_tree.get_selection()) # Create the TreeStore self.liststore = ListStore(gString, gString, gString, gString, gString, gString, gString, gString, gString) # Create the TreeModelFilter self.liststore_filter = self.liststore.filter_new(None) self.liststore_filter.set_visible_func(self.filter_visible_songs) # Set this model playlist_win_tree.set_model(self.liststore_filter) playlist_win_tree.set_headers_clickable(True) # Playlist buttons btn_clear = self.glade_file.get_widget('tool-clear') btn_shuffle = self.glade_file.get_widget('tool-shuffle') btn_repeat = self.glade_file.get_widget('tool-repeat') # Playlist filter combo = self.glade_file.get_widget('combobox1') combo.append_text(_('Title')) combo.append_text(_('Artist')) combo.append_text(_('Album')) combo.set_active(0) self.glade_file.get_widget('entry1').connect('key-release-event', self.playlist_filter) self.glade_file.get_widget('toolbutton5').connect('clicked', self.clearfilter) # Get repeat mode if self.config['repeat']: self.is_repeat = True else: self.is_repeat = False # Active or deactive repeat button if self.is_repeat: btn_repeat.set_active(True) else: btn_repeat.set_active(False) # GTK handlers btn_clear.connect('clicked', self.clear_playlist) btn_shuffle.connect('clicked', self.shuffle_playlist) btn_repeat.connect('clicked', self.repeat) # This function add a column to the playlist treeview def add_column(infos, show, column_name): # This function calculate the real width of a column def width_calculator(percent): return int(playlist_win_tree.get_allocation().width / 100. * percent) title = infos[0] expand = infos[1] width = width_calculator(infos[2]) position = infos[3] column = TreeViewColumn() column.set_title(title) self.columns_dict[column_name] = column if expand: column.set_min_width(width) column.set_expand(True) column.set_sizing(TREE_VIEW_COLUMN_FIXED) else: column.set_min_width(width) render_text = CellRendererText() column.pack_start(render_text, expand=True) column.add_attribute(render_text, 'markup', position) column.set_clickable(True) column.connect('clicked', self.reorder_by_column, position) if not show: column.set_visible(False) playlist_win_tree.append_column(column) # Get playlist configuration columns_ = self.config['columns'] show_columns = columns_.split(',') # Column settings # For each colum, a list contains: title, expand, width and position columns_infos = {'track': [_('#'), False, 4, 0], 'title': [_('Title'), True, 31, 1], 'artist': [_('Artist'), True, 20, 2], 'album': [_('Album'), True, 20, 3], 'genre': [_('Genre'), True, 14, 4], 'comment': [_('Comment'), True, 14, 5], 'year': [_('Year'), False, 8, 6], 'length': [_('Length'), False, 9, 7], '__infos': ['', False, 0, 8] } self.columns_list = ['track', 'title', 'artist', 'album', 'genre', 'comment', 'year', 'length', '__infos'] self.columns_dict = {'track': None, 'title': None, 'artist': None, 'album': None, 'genre': None, 'comment': None, 'year': None, 'length': None, '__infos': None} # Add columns for column in self.columns_list: if column in show_columns: add_column(columns_infos[column], True, column) else: add_column(columns_infos[column], False, column) def update_columns(self, cols): """This function updates the hidden columns.""" show = cols.split(',') for col in self.columns_list: if col in show: self.columns_dict[col].set_visible(True) else: self.columns_dict[col].set_visible(False) def filter_visible_songs(self, model, iter_): """This function filter visible songs.""" if len(model) > 0: search = self.glade_file.get_widget('entry1').get_text() typ = self.glade_file.get_widget('combobox1').get_active() if typ: searchin = model.get_value(iter_, typ + 1) else: searchin = model.get_value(iter_, 1) if search and searchin: if search.upper() in searchin.upper(): return True else: return True def playlist_filter(self, entry, event): """This function changes the filter.""" self.liststore_filter.refilter() def clearfilter(self, widget): """This function deletes the filter.""" self.glade_file.get_widget('entry1').set_text('') self.liststore_filter.refilter() def clear_playlist(self, widget=None): """This function clears all songs in the playlist.""" self.liststore.clear() self.named_playlist = None def shuffle_playlist(self, widget): """This function shuffles all songs in the playlist.""" n = 0 for a in self.liststore_filter: n += 1 index = range(0, n) random_shuffle(index) # Reorder the TreeModel if len(index) > 1: self.liststore.reorder(index) def repeat(self, widget): """This function turns on/off the repeat mode.""" _repeat = widget.get_active() try: cfgparser = ConfigParser() cfgparser.read(self.config['__config-file']) cfgparser.set(self.config['__module-name'], 'repeat', _repeat) cfgparser.write(open(self.config['__config-file'], 'w')) except NoSectionError: pass self.config['repeat'] = _repeat self.is_repeat = _repeat def reorder_by_column(self, treeviewcolumn, columnid): """This function reorders the playlist by column.""" def add_zero(userdata): if userdata.isdigit(): if int(userdata) < 10: return '0' + userdata return userdata # Create a list with all songs songs = [] for sg in self.liststore: songs.append((sg[0], sg[1], sg[2], sg[3], sg[4], sg[5], sg[6], sg[7], sg[8])) # Sort the playlist songs.sort(lambda x,y:cmp( add_zero(self.functions.clear_html(x[columnid])), add_zero(self.functions.clear_html(y[columnid])) )) if self.column_status[columnid]: songs.reverse() self.column_status[columnid] = False else: self.column_status[columnid] = True # Empty-ise playlist self.liststore.clear() # Re-fill the playlist for sg in songs: self.liststore.append(sg) def context_menu(self, widget, event, selection): """Generate the right click context menu on the playlist.""" if event.button == 3: (mod, iter_) = selection.get_selected() if iter_: # A song is selected infos = eval(unescape(mod.get_value(iter_, 8))) filename = infos[8] def get_lyrics(widget): """Show lyrics of the song.""" self.extensions.load_event('OnLyricsRequest', infos) def edit_song(widget): """Edit song's datas.""" song = eval(unescape(unicode(mod[iter_][8]))) editsong = EditSong() editsong.edit_song_datas(song) def remove_item(widget): """Remove a song from the playlist.""" if (self.named_playlist is not None and self.named_playlist not in ('s50', 's100', 'a10', 'r50', 'r100')): self.playlists_management.remove_item_from_playlist( filename, self.named_playlist) del mod[iter_] def add_in_playlist(widget, playlist_name): """This function adds an item to a playlist.""" self.playlists_management.add_item_to_playlist( filename, playlist_name) widgets = glade_XML(join(self.functions.datadir, 'glade', 'playlistmenu.glade'), 'menu1', domain='bluemindo') menu = widgets.get_widget('menu1') menu.attach_to_widget(widget, None) menu.popup(None, None, None, event.button, event.time) widgets.get_widget('menu-play').connect('activate', self.load_song, None, None) widgets.get_widget('menu-playlist-remove').connect('activate', remove_item) widgets.get_widget('menu-playlist-lyrics').connect('activate', get_lyrics) widgets.get_widget('menu-playlist-edit').connect('activate', edit_song) playlist_menu = widgets.get_widget('menu-playlist-add_menu') for plist in self.playlists_management.get_playlists_list(): menu_item = MenuItem(label=plist, use_underline=False) playlist_menu.append(menu_item) menu_item.connect('activate', add_in_playlist, plist) del menu_item menu.show_all() # Hide song edit for the moment widgets.get_widget('menu-playlist-edit').hide() def song_already_in(self, song): """Return TRUE if the song is already in the playlist.""" for sg in self.liststore: if eval(unescape(sg[8])) == song: return True def add_songs(self, songs, orderise=True, named_playlist=None): """This function add a list of songs in the playlist.""" def add_zero(userdata): if userdata.isdigit(): if int(userdata) < 10: return '0' + userdata return userdata stbar = self.glade_file.get_widget('statusbar1') ordered_songs = [] n_length = 0 n_occurs = 0 start = '' end = '' for song in songs: if not self.song_already_in(song): _title = start + markup_escape_text(song[0]) + end _artist = start + markup_escape_text(song[1]) + end _album = start + markup_escape_text(song[2]) + end _comment = start + markup_escape_text(song[3]) + end _genre = start + markup_escape_text(song[4]) + end _year = start + markup_escape_text(str(song[5])) + end _track = start + add_zero(str(song[6])) + end _length = start + self.functions.human_length(song[7]) + end ordered_songs.append((_track, _title, _artist, _album, _genre, _comment, _year, _length, markup_escape_text(str(song)) )) n_length += song[7] n_occurs += 1 if orderise: ordered_songs.sort(lambda x,y:cmp(x[0], y[0])) ordered_songs.sort(lambda x,y:cmp(x[3].lower(), y[3].lower())) ordered_songs.sort(lambda x,y:cmp(x[2].lower(), y[2].lower())) for song in ordered_songs: self.liststore.append(song) if named_playlist is not None: self.named_playlist = named_playlist else: self.named_playlist = None if n_occurs > 0: stbar.push(0, _('Added %(nbsongs)u songs (%(totalength)s).') % { 'nbsongs': n_occurs, 'totalength': self.functions.human_length(n_length)}) def load_song(self, widget, rowid, column): """Load a song.""" tview = self.glade_file.get_widget('treeview2') (mod, iter_) = tview.get_selection().get_selected() # Test if a song is selected if tview.get_selection().get_selected()[1]: song = eval(unescape(mod.get_value(iter_, 8))) self.extensions.load_event('OnPlayNewSong', song)bluemindo-0.3/src/modules/explorer/config.py0000644000175000017500000001272311147270165021161 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from gtk.glade import XML as glade_XML from gtk import STOCK_DIRECTORY, ListStore from ConfigParser import ConfigParser, DuplicateSectionError from os.path import join, isfile from os import getcwd class Config: def __init__(self, module, view, config_file, get_configuration): self.module = module self.view = view self.config_file = config_file self.get_configuration = get_configuration def save_config(self, name): if name == self.module['name']: wdg = self.conf_widgets combo = wdg.get_widget('combobox1') it = combo.get_active_iter() md = combo.get_model() view_mode = md[it][1] fcbutton = wdg.get_widget('filechooserbutton1') folder = fcbutton.get_filename() chck_scan = wdg.get_widget('check-scan') scan = chck_scan.get_active() chck_artwork = wdg.get_widget('check-artwork') artwork = chck_artwork.get_active() columns = '' if wdg.get_widget('check-column-track').get_active(): columns += 'track,' if wdg.get_widget('check-column-title').get_active(): columns += 'title,' if wdg.get_widget('check-column-artist').get_active(): columns += 'artist,' if wdg.get_widget('check-column-album').get_active(): columns += 'album,' if wdg.get_widget('check-column-genre').get_active(): columns += 'genre,' if wdg.get_widget('check-column-comment').get_active(): columns += 'comment,' if wdg.get_widget('check-column-year').get_active(): columns += 'year,' if wdg.get_widget('check-column-length').get_active(): columns += 'length,' columns = columns[:-1] configparser = ConfigParser() configparser.read(self.config_file) try: configparser.add_section(self.module['name']) except DuplicateSectionError: pass configparser.set(self.module['name'], 'repeat', True) configparser.set(self.module['name'], 'artwork', int(artwork)) configparser.set(self.module['name'], 'scan', int(scan)) configparser.set(self.module['name'], 'mode', view_mode) configparser.set(self.module['name'], 'folder', folder) configparser.set(self.module['name'], 'columns', columns) if isfile(self.config_file): force_reload = False else: force_reload = True configparser.write(open(self.config_file, 'w')) self.view.reload_config(self.get_configuration(), force_reload) def configuration(self, args): (name, widgets) = args if name == self.module['name']: # Load the glade and put the vertical box in the module's # configuration one config = self.get_configuration() self.conf_widgets = glade_XML(join(getcwd(), 'modules', 'explorer', 'configuration.glade'), 'vbox1', domain='bluemindo') hbox = widgets.get_widget('hbox1') try: kids = hbox.get_children() hbox.remove(kids[2]) except: pass hbox.add(self.conf_widgets.get_widget('vbox1')) columns = config['columns'].split(',') for column in columns: self.conf_widgets.get_widget('check-column-' + column).set_active(True) views = [(_('Lightweight'), 'lightweight'), (_('Basic'), 'basic'), (_('Normal'), 'normal'), (_('Full'), 'full'), (_('Albums'), 'albums'), (_('Playlists'), 'playlists'), (_('Webradios'), 'webradios')] combo = self.conf_widgets.get_widget('combobox1') lst = ListStore(str, str) combo.set_model(lst) for view in views: it = lst.append((view[0], view[1])) if view[1] == config['mode']: combo.set_active_iter(it) fcbutton = self.conf_widgets.get_widget('filechooserbutton1') fcbutton.set_current_folder(config['folder']) check_scan = self.conf_widgets.get_widget('check-scan') if config['scan']: check_scan.set_active(True) check_artwork = self.conf_widgets.get_widget('check-artwork') if config['artwork']: check_artwork.set_active(True)bluemindo-0.3/src/modules/explorer/musicdb.py0000644000175000017500000001156611224405566021347 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from gobject import idle_add from re import compile as re_compile from os.path import join from os import walk import tagpy from common.config import ConfigLoader from common.sqlite import SQLite class MusicDatabase: def __init__(self, config, pbar): self.config = config self.pbar = pbar self.confl = ConfigLoader() self.stored_result = [] def do_scan(self): """This function scan the music directory.""" songs = list() songs_filename = list() songs_artists = list() songs_albums = list() nb_song = 0 fileregxp = re_compile('.+\.(flac|ogg|oga|mp3|mpc)$') # Walk in the music folder if self.config['folder'] is None: folder = '' else: folder = self.config['folder'] song_files = list() for (dir_, rep, files) in walk(folder): for file_ in files: if fileregxp.match(file_): nb_song += 1 song_files.append(join(dir_, file_)) self.pbar.set_fraction(0) id_song = 0 for song in song_files: id_song += 1 filename = song tagpy_file = tagpy.FileRef(filename) tags = tagpy_file.tag() audio = tagpy_file.audioProperties() try: title = tags.title artist = tags.artist album = tags.album comment = tags.comment genre = tags.genre year = tags.year track = tags.track length = audio.length except AttributeError: continue if id_song % 5: self.pbar.set_fraction(float(id_song) / float(nb_song)) self.pbar.set_text(_('Added %d songs') % id_song) songs_filename.append(filename) songs_artists.append(artist) songs_albums.append(album) songs.append((title, artist, album, comment, genre, year, track, length, filename)) # Serialize songs sqlite = SQLite() sqlite.execute('delete from songs') sqlite.executemany('insert into songs (title, artist, album, ' 'comment, genre, year, track, length, filename) ' 'values (?, ?, ?, ?, ?, ?, ?, ?, ?)', songs) # Update statistics self.pbar.set_text(_('Update statistics…')) # Update songs cursor = sqlite.execute('select * from stats_songs') for song in cursor.fetchall(): filename = song[0] if filename not in songs_filename: # Delete this songs from statistics sqlite.execute('delete from stats_songs where filename=:val', {'val': filename}) # Update artists cursor = sqlite.execute('select * from stats_artists') for artist in cursor.fetchall(): artist = artist[0] if artist not in songs_artists: # Delete this artist from statistics sqlite.execute('delete from stats_artists where artist=:val', {'val': artist}) # Update albums cursor = sqlite.execute('select * from stats_albums') for album in cursor.fetchall(): album = album[0] if album not in songs_albums: # Delete this album from statistics sqlite.execute('delete from stats_albums where album=:val', {'val': album}) # The job is ended o/ sqlite.close() def scan(self): self.do_scan() def load(self, force_reload=False): if not force_reload and len(self.stored_result) > 0: # Return the already loaded songs return self.stored_result else: # Load the songs sqlite = SQLite() cursor = sqlite.execute('select * from songs order by ' 'artist, album, title') songs = cursor.fetchall() sqlite.close() return songsbluemindo-0.3/src/modules/explorer/viewscommon.py0000644000175000017500000000537411154061535022263 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from thread import start_new_thread from xml.sax.saxutils import unescape as xml_unescape from common.webservices import Amazon amazon = Amazon() def unescape(string): """Unescape an escaped string with markup_escape_text.""" _str = xml_unescape(string, {''': "'", '"': '"'}) return _str def insensitive_sort(x, y): """Sort a list, no matter of case.""" if isinstance(x, dict) and isinstance(y, dict): x = x['album'] y = y['album'] else: x = x[0] y = y[0] if x.lower() == y.lower(): return 0 elif x.lower() < y.lower(): return -1 else: return 1 def get_representation(list_songs, fetch_artwork=True): """This function returns a artist→album→songs presentation.""" def unique(seq, keepstr=True): """This function ensures there are only unique values.""" t = type(seq) if t in (str, unicode): t = (list, ''.join)[bool(keepstr)] seen = [] return t(c for c in seq if not (c in seen or seen.append(c))) def song_sort(x, y): """This function sorts songs.""" if x[3] == y[3]: return 0 elif x[3] < y[3]: return -1 else: return 1 songs_tree = {} artists = [] albums = [] albums_dl = [] for result in list_songs: artists.append(result[1]) albums.append((result[1], result[2])) artists = unique(artists) albums = unique(albums) for a in artists: songs_tree[a] = {} for a in albums: if fetch_artwork and (a[0] != '' and a[1] != ''): albums_dl.append((a[0], a[1])) songs_tree[a[0]][a[1]] = [] # Download albums pictures if fetch_artwork: start_new_thread(amazon.get_pictures, (albums_dl,)) # Add songs for result in list_songs: songs_tree[result[1]][result[2]].append(result) # Sort songs for artist in songs_tree.values(): for album in artist.values(): album.sort(song_sort) return songs_treebluemindo-0.3/src/modules/lyrics/configuration.glade0000644000175000017500000000750511020337174022650 0ustar xbrightxbright 400 130 True window1 False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True 200 True 0 Active lyrics retrieval: False False True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Yes 0 True 1 5 True 200 True 0 Server: False False True lyricwiki.org lyrc.com.ar lyriki.com 1 1 False bluemindo-0.3/src/modules/lyrics/__init__.py0000644000175000017500000001423211224222634021107 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from gtk import STOCK_FILE, WRAP_WORD from threading import Thread from ConfigParser import ConfigParser, NoSectionError from os.path import join, isdir from os import makedirs from common.config import ConfigLoader from common.functions import Functions functions = Functions() from modules.lyrics.config import Config from modules.lyrics.lyricsdownloader import LyricsDownloader class Lyrics: def __init__(self, extensionsloader): self.extensions = extensionsloader self.module = {'name': 'Lyrics', 'logo': STOCK_FILE, 'configurable': True} def start_module(self): """This function starts the module.""" # Start configuration name = self.module['name'].lower() + '.cfg' self.config = ConfigLoader() self.config_file = join(self.config.confdir, 'modules', name) self.data_dir = join(self.config.datadir, 'modules', 'lyrics') if not isdir(self.data_dir): makedirs(self.data_dir) def open_bluemindo(glade): """Bluemindo has been opened.""" self.state = 0 self.glade = glade self.current = None conf = self.get_configuration() self.lcdown = LyricsDownloader(conf) self.cfg = Config(self.module, self.lcdown, self.config_file, self.get_configuration) def show_lyrics_for_song(song): title = song[0] artist = song[1] self.current = (title, artist) thread = Thread(group=None, target=self.get_lyrics, name='lyrics', args=(title, artist)) thread.start() def force_show_lyrics(song): self.glade.get_widget('tool-lyrics').set_active(True) def configuration(args): self.cfg.configuration(args) def save_config(args): self.cfg.save_config(args) # Connect to Bluemindo's signals self.extensions.connect('OnBluemindoStarted', open_bluemindo) self.extensions.connect('OnPlayNewSong', show_lyrics_for_song) self.extensions.connect('OnLyricsRequest', show_lyrics_for_song) self.extensions.connect('OnLyricsRequest', force_show_lyrics) self.extensions.connect('OnToolLyricsPressed', self.hideorshow) self.extensions.connect('OnToolReloadLyricsPressed', self.reload_lyric) self.extensions.connect('OnToolSaveLyricsPressed', self.save_lyric) self.extensions.connect('OnModuleConfiguration', configuration) self.extensions.connect('OnModuleConfigurationSave', save_config) def get_configuration(self): configparser = ConfigParser() configparser.read(self.config_file) config = {} try: for item in configparser.items(self.module['name']): try: value = int(item[1]) except ValueError: value = str(item[1]) config[item[0]] = value except NoSectionError: config['server'] = 'lyricwiki.org' config['active'] = True name = self.module['name'].lower() + '.cfg' config['__config-file'] = self.config_file config['__data-dir'] = self.data_dir return config def get_lyrics(self, title, artist, force_download=False): """Set the lyrics in the textview.""" text = self.glade.get_widget('textview1') text.set_wrap_mode(WRAP_WORD) buffer_ = text.get_buffer() buffer_.set_text(_('Downloading lyrics for %(title)s - %(artist)s…') % {'title': self.current[0], 'artist': self.current[1]}) lyrics = self.lcdown.get_lyrics(title, artist, force_download) if lyrics == '': lyrics = (_('No lyrics found for %(title)s - %(artist)s!') % {'title': self.current[0], 'artist': self.current[1]}) buffer_.set_text(lyrics) def reload_lyric(self): """Reload the current lyric.""" if self.current is None: return (title, artist) = self.current self.current = (title, artist) thread = Thread(group=None, target=self.get_lyrics, name='lyrics', args=(title, artist, True)) thread.start() def save_lyric(self): """Save the lyric (usefull if user has made corrections).""" if self.current is None: return (title, artist) = self.current song_hash = functions.get_hash(title, artist) lyrics_file = join(self.data_dir, '%s.lyrics' % song_hash) text = self.glade.get_widget('textview1') buffer_ = text.get_buffer() lyrics = buffer_.get_text(buffer_.get_start_iter(), buffer_.get_end_iter()) file_ = open(lyrics_file, 'w') file_.write(lyrics) file_.close() def hideorshow(self, song=None): if self.state == 0: self.state = 1 self.glade.get_widget('hbox3').show() self.glade.get_widget('toolbar3').show() self.glade.get_widget('vpaned1').hide() self.glade.get_widget('toolbar4').hide() elif self.state == 1: self.state = 0 self.glade.get_widget('hbox3').hide() self.glade.get_widget('toolbar3').hide() self.glade.get_widget('vpaned1').show() self.glade.get_widget('toolbar4').show()bluemindo-0.3/src/modules/lyrics/lyricsdownloader.py0000644000175000017500000000771111224206201022730 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from urllib2 import urlopen, Request, quote as urllib_quote, HTTPError from re import findall as re_findall from os.path import isfile, join from common.webservices import WebServices from common.functions import Functions functions = Functions() class LyricsDownloader: """A class to handles many lyrics websites.""" def __init__(self, config): self.config = config def reload_config(self, newconf): """Update the module configuration.""" del self.config self.config = newconf def get_lyrics(self, title, artist, force_download): """Return lyrics for a song.""" song_hash = functions.get_hash(title, artist) lyrics_file = join(self.config['__data-dir'], '%s.lyrics' % song_hash) if not self.config['active']: return _('Lyrics fetching is currently disabled.') if isfile(lyrics_file) and not force_download: # The lyrics already exists, return them file_ = open(lyrics_file) lyrics = file_.read() file_.close() else: # We need to download lyrics for this song # Choose a server if self.config['server'] == 'lyricwiki.org': lrc = LyricWiki elif self.config['server'] == 'lyrc.com.ar': lrc = Lyrc else: lrc = Lyriki # Get the lyrics lrcobj = lrc() lyrics = lrcobj.get_lyrics(title, artist) if lyrics != '': # Save the lyrics file_ = open(lyrics_file, 'w') file_.write(lyrics) file_.close() # Return the lyrics return lyrics class LyricWiki(WebServices): """Wrapper for lyricwiki.org.""" def get_lyrics(self, title, artist): lyrics = '' artist.replace(' ', '_') title.replace(' ', '_') url = ("http://www.lyricwiki.org/%s:%s" % ( urllib_quote(str(artist)), urllib_quote(str(title)))) try: page = self.get_html(url) except HTTPError: page = '' for lyrics in re_findall("(?s)
(.*?)\n", page): lyrics = lyrics.replace('
', '\n') return lyrics class Lyrc(WebServices): """Wrapper for lyrc.com.ar.""" def get_lyrics(self, title, artist): lyrics = '' url = ('http://lyrc.com.ar/en/tema1en.php?artist=%s&songname=%s' % ( urllib_quote(str(artist)), urllib_quote(str(title)))) page = self.get_html(url) for lyrics in re_findall('(?s)(.*?)
' '
(.*?)

', page): lyrics = lyrics.replace('
', '\n') return lyricsbluemindo-0.3/src/modules/lyrics/config.py0000644000175000017500000000610211131476662020623 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gtk.glade import XML as glade_XML from ConfigParser import ConfigParser, DuplicateSectionError from os.path import join from os import getcwd class Config: def __init__(self, module, lcdown, config_file, get_configuration): self.module = module self.lcdown = lcdown self.config_file = config_file self.get_configuration = get_configuration def save_config(self, name): if name == self.module['name']: check_active = self.conf_widgets.get_widget('checkbutton1') combo_server = self.conf_widgets.get_widget('combobox1') active = check_active.get_active() server = combo_server.get_model()[combo_server.get_active()][0] configparser = ConfigParser() configparser.read(self.config_file) try: configparser.add_section(self.module['name']) except DuplicateSectionError: pass configparser.set(self.module['name'], 'active', int(active)) configparser.set(self.module['name'], 'server', server) configparser.write(open(self.config_file, 'w')) self.lcdown.reload_config(self.get_configuration()) def configuration(self, args): (name, widgets) = args if name == self.module['name']: # Load the glade and put the vertical box in the module's # configuration one config = self.get_configuration() self.conf_widgets = glade_XML(join(getcwd(), 'modules', 'lyrics', 'configuration.glade'), 'vbox1', domain='bluemindo') hbox = widgets.get_widget('hbox1') try: kids = hbox.get_children() hbox.remove(kids[2]) except: pass hbox.add(self.conf_widgets.get_widget('vbox1')) check_popup = self.conf_widgets.get_widget('checkbutton1') if config['active']: check_popup.set_active(True) combo = self.conf_widgets.get_widget('combobox1') if config['server'] == 'lyricwiki.org': combo.set_active(0) elif config['server'] == 'lyrc.com.ar': combo.set_active(1) elif config['server'] == 'lyriki.com': combo.set_active(2)bluemindo-0.3/src/modules/trayicon/traycontext.glade0000644000175000017500000000445311027232315022705 0ustar xbrightxbright True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True gtk-media-play True True True gtk-media-next True True True gtk-media-previous True True True True Bluemindo True True gtk-home 1 True gtk-quit True True bluemindo-0.3/src/modules/trayicon/__init__.py0000644000175000017500000001440411154157023021434 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from gobject import timeout_add from gtk.glade import XML as glade_XML from gtk.gdk import POINTER_MOTION_MASK, pixbuf_new_from_file, INTERP_BILINEAR from gtk import STOCK_INFO, EventBox, Image, status_icon_new_from_file from os.path import join from os import getcwd try: from egg.trayicon import TrayIcon trayicon_type = 'egg' except ImportError: trayicon_type = 'gtk' from media.gstreamer import GStreamer from common.functions import Functions functions = Functions() class Trayicon: def __init__(self, extensionsloader): self.extensions = extensionsloader self.module = {'name': 'Trayicon', 'logo': STOCK_INFO, 'configurable': False} def start_module(self): """This function starts the module.""" def start_bluemindo(glade): self.window = glade.get_widget('window1') default = join(functions.datadir, 'image', 'logo_head_small.png') if trayicon_type == 'egg': self.tray = TrayIcon('Bluemindo') ebox = EventBox() ebox.set_visible_window(False) ebox.set_events(POINTER_MOTION_MASK) ebox.connect('button-press-event', self.on_clicked) ebox.connect('enter-notify-event', self.on_tray_enter) ebox.connect('leave-notify-event', self.on_tray_leave) self.tray_icon = Image() pixbuf = pixbuf_new_from_file(default) pixbuf = pixbuf.scale_simple(20, 20, INTERP_BILINEAR) self.tray_icon.set_from_pixbuf(pixbuf) ebox.add(self.tray_icon) self.tray.add(ebox) self.tray.show_all() self.is_the_cursor_on_tray = False else: self.tray = status_icon_new_from_file(default) self.tray.connect('activate', self.on_gtk_clicked) self.tray.connect('popup-menu', self.trayicon_menu) self.extensions.load_event('OnTrayIconAnswer', self.tray) # Connect to Bluemindo's signals self.extensions.connect('OnBluemindoStarted', start_bluemindo) self.extensions.connect('OnBluemindoWindowClosed', self.hideshow) self.extensions.connect('OnStopPressed', self.handler_stop) self.extensions.connect('OnPlayPressed', self.handler_play) self.extensions.connect('OnPlayNewSong', self.handler_play) def hideshow(self): """Hide or show Bluemindo's main window.""" if self.window.get_properties('visible')[0]: functions.close_bluemindo(self.window, False) self.window.hide() elif not self.window.get_properties('visible')[0]: self.window.show() functions.open_bluemindo(self.window) def on_gtk_clicked(self, widget): self.hideshow() def trayicon_menu(self, status_icon=None, button=0, activate_time=0): def tray_window(widget): """Hide or show the window.""" self.hideshow() def tray_quit(widget): """Quit Bluemindo. :'(""" self.extensions.load_event('OnBluemindoQuitted') functions.close_bluemindo(self.window) def tray_play(widget): """Load the Play button.""" self.extensions.load_event('OnPlayPressed') def tray_next(widget): """Load the next button.""" self.extensions.load_event('OnNextPressed') def tray_previous(widget): """Load the Previous button.""" self.extensions.load_event('OnPreviousPressed') # Right button: context menu wdg = glade_XML(join(getcwd(), 'modules', 'trayicon', 'traycontext.glade'), 'menu1', domain='bluemindo') wdg.get_widget('menu1').popup(None, None, None, button, activate_time) wdg.get_widget('tray-bluemindo').connect('activate', tray_window) wdg.get_widget('tray-quit').connect('activate', tray_quit) wdg.get_widget('menu-play').connect('activate', tray_play) wdg.get_widget('menu-previous').connect('activate', tray_previous) wdg.get_widget('menu-next').connect('activate', tray_next) def on_clicked(self, widget, event): if event.button == 1: # Left button: hide/show self.hideshow() elif event.button == 3: # Functions for the right click context menu self.trayicon_menu() def on_tray_enter(self, widget, event): """The tray is focused by the mouse: show current song if any.""" def show_notif(): if self.is_the_cursor_on_tray: self.extensions.load_event('OnNotificationRequest') self.is_the_cursor_on_tray = True timeout_add(2000, show_notif) def on_tray_leave(self, widget, event): self.is_the_cursor_on_tray = False def handler_stop(self): """Updates the status icon when we stop our music.""" self._set_icon('stop') def handler_play(self, song=None): """Updates the status icon when we play or pause our music.""" gst = GStreamer() cur = gst.getstatus() if cur == 'PAUSED': self._set_icon('pause') else: self._set_icon('play') def _set_icon(self, what): if trayicon_type == 'gtk': return wtf = join(functions.datadir, 'image', 'logo_head_small_%s.png' % what) pixbuf = pixbuf_new_from_file(wtf) pixbuf = pixbuf.scale_simple(20, 20, INTERP_BILINEAR) self.tray_icon.set_from_pixbuf(pixbuf)bluemindo-0.3/src/modules/__init__.py0000644000175000017500000000000011020337174017566 0ustar xbrightxbrightbluemindo-0.3/src/modules/player/configuration.glade0000644000175000017500000001777511143051435022650 0ustar xbrightxbright 400 130 True window1 False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True 250 True 0 Change window title: False False True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Yes 0 True 1 5 True 250 True 0 Show a popup with cover: False False True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Yes 0 True 1 5 1 True 250 True 0 0.10000000149011612 GStreamer playback: False True True True Default 0 True True True True Gapless 0 True True 1 1 2 True 250 True 0 Start minimized: False True True Yes 0 True 1 3 False bluemindo-0.3/src/modules/player/__init__.py0000644000175000017500000000766611147654433021125 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gtk import STOCK_MEDIA_PLAY from ConfigParser import ConfigParser, NoSectionError from os.path import join, isdir from os import makedirs from common.config import ConfigLoader from modules.player.player_gui import PlayerGUI from modules.player.config import Config class Player: def __init__(self, extensionsloader): self.extensions = extensionsloader self.module = {'name': 'Player', 'logo': STOCK_MEDIA_PLAY, 'configurable': True} def start_module(self): """This function starts the module.""" # Start configuration name = self.module['name'].lower() + '.cfg' self.config = ConfigLoader() self.config_file = join(self.config.confdir, 'modules', name) self.data_dir = join(self.config.datadir, 'modules', 'player') if not isdir(self.data_dir): makedirs(self.data_dir) def launch_player(glade_file): conf = self.get_configuration() self.player_gui = PlayerGUI(glade_file, conf) self.cfg = Config(self.module, self.player_gui, self.config_file, self.get_configuration) def handler_previous(): self.player_gui.handler_previous() def handler_stop(): self.player_gui.handler_stop() def handler_play(): self.player_gui.handler_play() def handler_next(): self.player_gui.handler_next() def handler_play_new_song(song): self.player_gui.handler_play_new_song(song) def handler_play_new_radio(song): self.player_gui.handler_play_new_radio(song) def configuration(args): self.cfg.configuration(args) def save_config(args): self.cfg.save_config(args) # Connect to Bluemindo's signals self.extensions.connect('OnBluemindoStarted', launch_player) self.extensions.connect('OnPreviousPressed', handler_previous) self.extensions.connect('OnStopPressed', handler_stop) self.extensions.connect('OnPlayPressed', handler_play) self.extensions.connect('OnNextPressed', handler_next) self.extensions.connect('OnPlayNewSong', handler_play_new_song) self.extensions.connect('OnPlayNewRadio', handler_play_new_radio) self.extensions.connect('OnModuleConfiguration', configuration) self.extensions.connect('OnModuleConfigurationSave', save_config) def get_configuration(self): configparser = ConfigParser() configparser.read(self.config_file) config = {} try: for item in configparser.items(self.module['name']): try: value = int(item[1]) except ValueError: value = str(item[1]) config[item[0]] = value except NoSectionError: config['popup'] = True config['title'] = True config['gstplayback'] = 'default' config['startminimized'] = True name = self.module['name'].lower() + '.cfg' config['__config-file'] = self.config_file config['__data-dir'] = self.data_dir config['__extensions'] = self.extensions return configbluemindo-0.3/src/modules/player/player_gui.py0000644000175000017500000004764011225140330021501 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gettext import gettext as _ from gobject import markup_escape_text, timeout_add, idle_add from gtk.glade import XML as glade_XML from gtk.gdk import pixbuf_new_from_file, INTERP_BILINEAR, display_get_default from gtk import (FileChooserDialog, FILE_CHOOSER_ACTION_OPEN, STOCK_CANCEL, RESPONSE_CANCEL, STOCK_OPEN, RESPONSE_OK, FileFilter, STOCK_MEDIA_PAUSE, STOCK_MEDIA_PLAY, MAPPED) from os.path import join, isfile from os import remove from shutil import copyfile from gst import QueryError as gst_QueryError from gui.volumemanagement import VolumeManagement from common.statistics import Statistics from common.functions import Functions from common.config import ConfigLoader from media.gstreamer import GStreamer from modules.explorer import viewscommon unescape = viewscommon.unescape class PlayerGUI: def __init__(self, glade_file, config): self.glade_file = glade_file self.config = config self.userconf = ConfigLoader() self.functions = Functions() self.new_album_state = None self.statistics = Statistics() # Minimize if not config['startminimized']: self.glade_file.get_widget('window1').show() # GStreamer self.gst = GStreamer() self.gst.set_playback(config['gstplayback']) self.plb = config['gstplayback'] self.gst.stop() if config['gstplayback'] == 'gapless': self.gst.player.connect('about-to-finish', self.song_nearly_ended) self.glade_file.get_widget('hscale1').set_sensitive(False) self.repeat = self.glade_file.get_widget('tool-repeat') # VolumeManagement volume = VolumeManagement(self.glade_file) volume.change_volume(volume.volume) datadir = self.functions.datadir album_cover = self.glade_file.get_widget('image1') self.current_file = [join(datadir, 'image', 'logo_head_big.png'), None] self.image_set(album_cover, join(datadir, 'image', 'logo_head_big.png'), 85) alb_btn = self.glade_file.get_widget('button1') alb_btn.connect('clicked', self.on_album_clicked) alb_btn.connect('enter', self.on_album_enter) alb_btn.connect('leave', self.on_album_leave) self.glade_file.get_widget('label2').set_alignment(0, 0.5) self.glade_file.get_widget('label3').set_alignment(0, 0.5) self.glade_file.get_widget('toolbutton1').set_expand(True) self.glade_file.get_widget('hscale1').connect('change-value', self.on_change_value) def reload_config(self, newconf): self.config['title'] = newconf['title'] self.config['popup'] = newconf['popup'] if self.plb != newconf['gstplayback']: # Stop playback self.config['__extensions'].load_event('OnStopPressed') # Start a new playback self.gst.set_playback(newconf['gstplayback']) self.plb = newconf['gstplayback'] # Connect if we are in gapless mode if newconf['gstplayback'] == 'gapless': self.gst.player.connect('about-to-finish', self.song_nearly_ended) def song_nearly_ended(self, *args): if self.plb == 'gapless': idle_add(self.handler_next, True) def image_set(self, pimg, pfile, pscale): """Change album art image from a pixbuf.""" pixbuf = pixbuf_new_from_file(pfile) pixbuf = pixbuf.scale_simple(pscale, pscale, INTERP_BILINEAR) pimg.set_from_pixbuf(pixbuf) def on_album_clicked(self, widget): """Change current album artwork.""" if self.current_file[1] is not None: (artist, album) = self.current_file[1] file_selector = FileChooserDialog(_('Change album artwork'), None, FILE_CHOOSER_ACTION_OPEN, (STOCK_CANCEL, RESPONSE_CANCEL, STOCK_OPEN, RESPONSE_OK)) filter_ = FileFilter() filter_.add_mime_type('image/gif') filter_.add_mime_type('image/png') filter_.add_mime_type('image/jpeg') file_selector.set_filter(filter_) fld = file_selector.run() # Select a file, copy it in the covers directory file_ = None if fld == RESPONSE_OK: file_ = file_selector.get_filename() if not file_ == None: _file = join(self.config['__data-dir'], 'covers', self.functions.get_hash(album, artist)) if isfile(_file): remove(_file) copyfile(file_, _file) self.image_set(self.glade_file.get_widget('image1'), _file, 90) file_selector.destroy() def on_album_enter(self, widget): """Create a popup window with the album cover.""" def show_cover(): if self.is_the_cursor_on_album and self.config['popup']: try: self.new_album_state.destroy() except AttributeError: pass img = self.glade_file.get_widget('image1') pixbuf = img.get_pixbuf() widg = glade_XML(join(self.functions.datadir, 'glade', 'albumpreview.glade')) main_window = widg.get_widget('window1') self.new_album_state = main_window # Position dsp = display_get_default() scr = dsp.get_default_screen() tmp1, x0, y0, tmp2 = dsp.get_pointer() main_window.move((x0 + 5), (y0 + 5)) main_window.show() # Change image from pixbuf pimg = widg.get_widget('image1') pixbuf = pixbuf_new_from_file(self.current_file[0]) pixbuf = pixbuf.scale_simple(285, 285, INTERP_BILINEAR) pimg.set_from_pixbuf(pixbuf) self.is_the_cursor_on_album = True timeout_add(1000, show_cover) def on_album_leave(self, widget): """Delete the popup window if exists.""" self.is_the_cursor_on_album = False if self.new_album_state: self.new_album_state.destroy() def on_change_value(self, widget, scroll, value): """Jump to another place in the song.""" seconds = int(value) self.gst.seek(seconds) def handler_previous(self): """This is the handler for the OnPreviousPressed signal.""" cur = self.gst.getnow() self.gst.stop() extensions = self.config['__extensions'] tree_pls = self.glade_file.get_widget('treeview2') text_lyr = self.glade_file.get_widget('textview1') if tree_pls.flags() & MAPPED or text_lyr.flags() & MAPPED: model = tree_pls.get_model() else: # TODO: webradios return if len(model) > 0: n = 0 id_in_playlist = 0 for song in model: songfile = eval(unescape(str(song[8]))) if songfile[8] == cur: id_in_playlist = n n = n + 1 _p = 0 _n = id_in_playlist - 1 if _n < _p and self.repeat.get_active(): # Load the previous song (last in playlist) song_id = len(model) - 1 song_data = eval(unescape(str(model[song_id][8]))) extensions.load_event('OnPlayNewSong', song_data) elif _n < _p and not self.repeat.get_active(): # Don't do anything pass else: # Load the previous song song_data = eval(unescape(str(model[_n][8]))) extensions.load_event('OnPlayNewSong', song_data) else: # The playlist is empty but we want another song extensions.load_event('OnPlayPressedWithoutQueue') def handler_stop(self): """This is the handler for the OnStopPressed signal.""" # Stop the song cur = self.gst.getnow() self.gst.stop() # Update GUI self.glade_file.get_widget('hscale1').set_sensitive(False) self.glade_file.get_widget('hscale1').set_value(0) self.glade_file.get_widget('tool-play').set_stock_id(STOCK_MEDIA_PLAY) self.glade_file.get_widget('hscale1').set_value(0) self.glade_file.get_widget('label1').set_text('') self.glade_file.get_widget('label2').set_text('') self.glade_file.get_widget('label3').set_text('') self.glade_file.get_widget('label5').set_text('') self.glade_file.get_widget('label6').set_text('') # Delete the album art datadir = self.functions.datadir album_cover = self.glade_file.get_widget('image1') self.current_file = [join(datadir, 'image', 'logo_head_big.png'), None] self.image_set(album_cover, join(datadir, 'image', 'logo_head_big.png'), 85) # Delete the content of the current playing file self.current_playing_file('') tree_pls = self.glade_file.get_widget('treeview2') text_lyr = self.glade_file.get_widget('textview1') if tree_pls.flags() & MAPPED or text_lyr.flags() & MAPPED: model = tree_pls.get_model() else: # TODO: webradios return # Delete bold in playlist u = -1 for sng in model: u += 1 songfile = eval(unescape(str(sng[8]))) if songfile[8] == cur: sng = model[u] model[u] = [self.functions.clear_html(sng[0], True), self.functions.clear_html(sng[1], True), self.functions.clear_html(sng[2], True), self.functions.clear_html(sng[3], True), self.functions.clear_html(sng[4], True), self.functions.clear_html(sng[5], True), self.functions.clear_html(sng[6], True), self.functions.clear_html(sng[7], True), sng[8] ] def handler_play(self): """This is the handler for the OnPlayPressed signal.""" # Get status rtn = self.gst.playpause(None) sta = self.gst.getstatus() extensions = self.config['__extensions'] tree_pls = self.glade_file.get_widget('treeview2') text_lyr = self.glade_file.get_widget('textview1') if tree_pls.flags() & MAPPED or text_lyr.flags() & MAPPED: model = tree_pls.get_model() else: # TODO: webradios return if len(model) > 0 and (sta == 'NULL' or sta == 'STOP'): # If the playlist is not empty, load the first song song_data = eval(unescape(str(model[0][8]))) extensions.load_event('OnPlayNewSong', song_data) elif len(model) == 0 and (sta == 'NULL' or sta == 'STOP'): # The playlist is empty but we want another song extensions.load_event('OnPlayPressedWithoutQueue') # Play button tplay = self.glade_file.get_widget('tool-play') if self.gst.getstatus() == 'PAUSED': tplay.set_stock_id(STOCK_MEDIA_PLAY) else: tplay.set_stock_id(STOCK_MEDIA_PAUSE) def handler_next(self, gapless=False): """This is the handler for the OnNextPressed signal.""" cur = self.gst.getnow() if not gapless: self.gst.stop() extensions = self.config['__extensions'] tree_pls = self.glade_file.get_widget('treeview2') text_lyr = self.glade_file.get_widget('textview1') if tree_pls.flags() & MAPPED or text_lyr.flags() & MAPPED: model = tree_pls.get_model() else: # TODO: webradios return if len(model) > 0: n = 0 id_in_playlist = -1 for song in model: songfile = eval(unescape(str(song[8]))) if songfile[8] == cur: id_in_playlist = n n = n + 1 _p = len(model) - 1 _n = id_in_playlist + 1 if _n > _p and self.repeat.get_active(): # Load the next song (first in playlist) song_data = eval(unescape(str(model[0][8]))) extensions.load_event('OnPlayNewSong', song_data) elif _n > _p and not self.repeat.get_active(): # Don't do anything pass else: # Load the next song song_data = eval(unescape(str(model[_n][8]))) extensions.load_event('OnPlayNewSong', song_data) else: # The playlist is empty but we want another song extensions.load_event('OnPlayPressedWithoutQueue') def handler_play_new_song(self, sg): """This is the handler for the OnPlayNewSong signal.""" song = eval(unescape(str(sg))) title = song[0] artist = song[1] album = song[2] length = self.functions.human_length(song[7]) # Start playing the song cur = self.gst.getnow() self.gst.playpause(song[8]) if cur != self.gst.getnow(): # Update statistics self.statistics.set_stats_for_song(song[8], None) self.statistics.set_stats_for_artist(artist, None) self.statistics.set_stats_for_album(album, None) # Update GUI self.glade_file.get_widget('hscale1').set_sensitive(True) self.glade_file.get_widget('toolbar2').set_sensitive(True) self.glade_file.get_widget('label1').set_markup( '%s' % markup_escape_text(title)) self.glade_file.get_widget('label2').set_markup( '%s' % markup_escape_text(artist)) self.glade_file.get_widget('label3').set_text(album) self.glade_file.get_widget('label5').set_text('00:00') self.glade_file.get_widget('label6').set_text(' - ' + length) # Scale self.glade_file.get_widget('hscale1').set_range(0, float(song[7])) # Play button tplay = self.glade_file.get_widget('tool-play') if self.gst.getstatus() == 'PAUSED': tplay.set_stock_id(STOCK_MEDIA_PLAY) else: tplay.set_stock_id(STOCK_MEDIA_PAUSE) # Timer timeout_add(500, self.scale_timer) # Current playing file self.current_playing_file('%s - %s (from: %s)' % (title, artist, album)) # Window title if self.config['title']: self.glade_file.get_widget('window1').set_title('Bluemindo ' '[ %s - %s ]' % (title, artist)) else: self.glade_file.get_widget('window1').set_title('Bluemindo') # Set album image filename = join(self.config['__data-dir'], 'covers', self.functions.get_hash(album, artist)) if not isfile(filename): datadir = self.functions.datadir filename = join(datadir, 'image', 'logo_head_big.png') album_cover = self.glade_file.get_widget('image1') self.current_file = [filename, (artist, album)] self.image_set(album_cover, filename, 85) # Bold tree_pls = self.glade_file.get_widget('treeview2') text_lyr = self.glade_file.get_widget('textview1') if tree_pls.flags() & MAPPED or text_lyr.flags() & MAPPED: model = tree_pls.get_model() else: # TODO: webradios return if len(model) > 0: # Fetching the playlist and get the current playing song id i = 0 for sng in model: if eval(unescape(str(sng[8]))) == song: break i += 1 sng = model[i] if (not model[i][0].startswith('') and not model[i][0].endswith('')): model[i] = ['%s' % sng[0], '%s' % sng[1], '%s' % sng[2], '%s' % sng[3], '%s' % sng[4], '%s' % sng[5], '%s' % sng[6], '%s' % sng[7], sng[8] ] # Get the normal text, without bold emphasis u = 0 for sng in model: if (sng[1].startswith('') and sng[1].endswith('') and i != u): sng = model[u] model[u] = [self.functions.clear_html(sng[0], True), self.functions.clear_html(sng[1], True), self.functions.clear_html(sng[2], True), self.functions.clear_html(sng[3], True), self.functions.clear_html(sng[4], True), self.functions.clear_html(sng[5], True), self.functions.clear_html(sng[6], True), self.functions.clear_html(sng[7], True), sng[8] ] u += 1 def handler_play_new_radio(self, radio): """This is the handler for the OnPlayNewRadio signal.""" (title, url) = radio # Update GUI self.glade_file.get_widget('tool-play').set_stock_id(STOCK_MEDIA_PAUSE) self.glade_file.get_widget('toolbar2').set_sensitive(False) self.glade_file.get_widget('hscale1').set_value(0) self.glade_file.get_widget('label2').set_text('') self.glade_file.get_widget('label3').set_text('') self.glade_file.get_widget('label5').set_text('') self.glade_file.get_widget('label6').set_text('') self.glade_file.get_widget('label1').set_markup( '%s' % markup_escape_text(title)) self.glade_file.get_widget('label2').set_markup( '%s' % markup_escape_text(_('Webradios'))) self.gst.playpause(url) def current_playing_file(self, data): """Update the current-playing file in `datadir`.""" file_ = open(join(self.userconf.datadir, 'current-playing'), 'wb') file_.write(data) file_.close() def scale_timer(self): # This function updates the seek bar try: label = self.glade_file.get_widget('label5') hscale = self.glade_file.get_widget('hscale1') pos = self.gst.getposition() position = int(self.gst.getposition() / 1000000000) label.set_text(self.functions.human_length(position)) hscale.set_value(position) return True except gst_QueryError: pass state = self.gst.getstatus() if state == 'NULL' and self.config['gstplayback'] != 'gapless': extensions = self.config['__extensions'] extensions.load_event('OnNextPressed')bluemindo-0.3/src/modules/player/config.py0000644000175000017500000001007511143051435020604 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from gtk.glade import XML as glade_XML from ConfigParser import ConfigParser, DuplicateSectionError from os.path import join from os import getcwd class Config: def __init__(self, module, player_gui, config_file, get_configuration): self.module = module self.player_gui = player_gui self.config_file = config_file self.get_configuration = get_configuration def save_config(self, name): if name == self.module['name']: check_popup = self.conf_widgets.get_widget('checkbutton4') check_title = self.conf_widgets.get_widget('checkbutton3') radio_gstpb_default = self.conf_widgets.get_widget('radiobutton1') radio_gstpb_gapless = self.conf_widgets.get_widget('radiobutton2') check_minimize = self.conf_widgets.get_widget('checkbutton1') popup = check_popup.get_active() title = check_title.get_active() if radio_gstpb_default.get_active(): playback = 'default' elif radio_gstpb_gapless.get_active(): playback = 'gapless' start = check_minimize.get_active() configparser = ConfigParser() configparser.read(self.config_file) try: configparser.add_section(self.module['name']) except DuplicateSectionError: pass configparser.set(self.module['name'], 'popup', int(popup)) configparser.set(self.module['name'], 'title', int(title)) configparser.set(self.module['name'], 'gstplayback', playback) configparser.set(self.module['name'], 'startminimized', int(start)) configparser.write(open(self.config_file, 'w')) self.player_gui.reload_config(self.get_configuration()) def configuration(self, args): (name, widgets) = args if name == self.module['name']: # Load the glade and put the vertical box in the module's # configuration one config = self.get_configuration() self.conf_widgets = glade_XML(join(getcwd(), 'modules', 'player', 'configuration.glade'), 'vbox1', domain='bluemindo') hbox = widgets.get_widget('hbox1') try: kids = hbox.get_children() hbox.remove(kids[2]) except: pass hbox.add(self.conf_widgets.get_widget('vbox1')) check_popup = self.conf_widgets.get_widget('checkbutton4') if config['popup']: check_popup.set_active(True) check_title = self.conf_widgets.get_widget('checkbutton3') if config['title']: check_title.set_active(True) radio_gstpb_default = self.conf_widgets.get_widget('radiobutton1') radio_gstpb_gapless = self.conf_widgets.get_widget('radiobutton2') radio_gstpb_gapless.set_group(radio_gstpb_default) if config['gstplayback'] == 'default': radio_gstpb_default.set_active(True) elif config['gstplayback'] == 'gapless': radio_gstpb_gapless.set_active(True) check_minimized = self.conf_widgets.get_widget('checkbutton1') if config['startminimized']: check_minimized.set_active(True)bluemindo-0.3/src/extensionsloader.py0000644000175000017500000001633511241614017017766 0ustar xbrightxbright# -*- coding: utf-8 -*- # Bluemindo: A really simple but powerful audio player in Python/PyGTK. # Copyright (C) 2007-2009 Erwan Briand # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation version 3 of the License. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from pickle import load from os.path import join, isdir, isfile, realpath from os import listdir, getcwd from imp import find_module from sys import path from common.functions import Functions from common.config import ConfigLoader functions = Functions() config = ConfigLoader() class ExtensionsLoader(object): def __init__(self): # Activated plugins self.actived_plugins = load(open(join(config.confdir, 'Plugins.cfg'))) # All available signals # If you think you need another signal, please report a bug. # http://codingteam.net/project/bluemindo/bugs/add self.signals = { 'OnBluemindoStarted': list(), # When Bluemindo is starting # glade 'OnBluemindoWindowClosed': list(), # When Bluemindo main window is closed 'OnBluemindoQuitted': list(), # When Bluemindo is quitted 'OnNotificationRequest': list(), # When we want to force desktop notification 'OnTrayIconAnswer': list(), # Send the trayicon object 'OnToolLyricsPressed': list(), # When lyrics button is pressed 'OnToolReloadLyricsPressed': list(), # When lyrics reload button is pressed 'OnToolSaveLyricsPressed': list(), # When lyrics save button is pressed 'OnLyricsRequest': list(), # When we whant to show the lyrics of a song # (title, artist, album, comment, genre, year, track, length, file) 'OnModuleConfiguration': list(), # When a module is waiting for configuration # (module, confglade) 'OnModuleConfigurationSave': list(), # When: Exit from module's configuration # module 'OnPlaylistRequested': list(), # When we open a playlist # playlist_name 'OnPreviousPressed': list(), # When previous button is pressed 'OnStopPressed': list(), # When stop button is pressed 'OnPlayPressed': list(), # When play button is pressed 'OnNextPressed': list(), # When next button is pressed 'OnPlayPressedWithoutQueue': list(), # When play is pressed with an empty playlist 'OnPlayNewSong': list(), # When a new song have to be launched # (title, artist, album, comment, genre, year, track, length, file) 'OnPlayNewRadio': list() # When a new radio have to be launched # (title, url) } # Starting self.modules = [] self.plugins = [] self.conflist = {} self.is_in_config = False path.append(config.unofficial_plugins) def load(self): """Load all modules and plugins""" # Load both modules and plugins for exttype in ('modules', 'plugins', config.unofficial_plugins): for file_ in listdir(exttype): # Get only modules without `.` as first # character (exclude .svn/) if (isdir(join(getcwd(), exttype, file_)) and not file_.startswith('.')): name = file_.lower() # Try to load the module try: if exttype in ('modules', 'plugins'): module = __import__(''.join([exttype + '.', name])) cls = getattr(getattr(module, name), name.capitalize()) obj = cls(self) else: fp, pathname, desc = find_module(name) if pathname.startswith(exttype): module = __import__(name) cls = getattr(module, name.capitalize()) obj = cls(self) else: continue if exttype == 'modules': # Start the module obj.start_module() elif name in self.actived_plugins: # Start the plugin obj.start_plugin() if exttype == 'modules': self.modules.append(obj.module) else: obj.plugin['__object'] = obj self.plugins.append(obj.plugin) except Exception, error: print "\n---------" print ('Extension `%s`, registered in *%s*, could not ' 'start.' % (file_, exttype) ) print error print "---------\n" if exttype == 'modules': raise SystemExit("Bluemindo's modules are required" " to launch the software.\n" "Exiting.") def connect(self, signal, function): """Connect a signal with a module's function""" if self.signals.has_key(signal): self.signals[signal].append(function) else: print "`%s` don't exist." % signal def load_event(self, signal, args=None): """Load an event, call related functions""" if self.signals.has_key(signal): dct = self.signals[signal] for dct_ in dct: if args is not None: dct_(args) else: dct_() else: print "`%s` don't exist." % signal def get_extensions(self): self.modules.sort((lambda x,y:cmp(x['name'], y['name']))) self.plugins.sort((lambda x,y:cmp(x['name'], y['name']))) return {'modules': self.modules, 'plugins': self.plugins} def get_actived_plugins(self): return load(open(join(config.confdir, 'Plugins.cfg'))) def activate_plugin(self, name): for plugin in self.plugins: if plugin['name'] == name.capitalize(): obj = plugin['__object'] obj.start_plugin() return def shutdown_plugin(self, name): for plugin in self.plugins: if plugin['name'] == name.capitalize(): obj = plugin['__object'] obj.stop_plugin() returnbluemindo-0.3/launch.sh0000755000175000017500000000006411241614017015040 0ustar xbrightxbright#!/bin/bash cd `dirname $0`/src ./bluemindo.py "$@"bluemindo-0.3/AUTHORS0000644000175000017500000000010111013271170014263 0ustar xbrightxbrightErwan Briand (JID: xbright@codingteam.net)bluemindo-0.3/INSTALL0000644000175000017500000000173611166120617014273 0ustar xbrightxbrightDepends: * python http://www.python.org * python-gtk2 http://www.pygtk.org * python-glade2 * python-gst0.10 http://gstreamer.freedesktop.org/modules/gst-python.html * python-tagpy http://news.tiker.net/software/tagpy Python =< 2.4 depends: * python-elementtree http://effbot.org/zone/element-index.htm * python-pysqlite2 http://oss.itsystementwicklung.de/trac/pysqlite Optional depends: * python-eggtrayicon * python-notify http://www.galago-project.org * python-xmpp http://xmpppy.sourceforge.net Optional, for GNOME users: * python-dbus http://www.freedesktop.org/wiki/Software/DBusBindings Once you installed needed dependencies, you can install Bluemindo, by launching (as root): # make install If you want to uninstall Bluemindo, just launch (as root): # make uninstall As it's a Python developped software, you don't have to compile it for using it. Just launch (as normal user): $ ./launch.shbluemindo-0.3/CHANGELOG0000644000175000017500000000432511241615014014443 0ustar xbrightxbrightBluemindo 0.3 (16 August 2009) * Refactorized the whole source code * Introduced gapless playback * Ability to start Bluemindo minimized * Improved the VolumeManager button * Updated Last.fm and Amazon APIs * Explorer modules completely rewritten: * Introduced Albums view * Introduced Playlists (automatic and manual) * Introduced Webradios and Shoutcast support * Ability to change view during Bluemindo execution! * Listening statistics * New TrayIcon module * Sort in playlist/views is now case-insensitive * Plugins: * Ability to disable or enable plugins * Audioscrobbler can now use Last.fm, Libre.fm or any other servers * Jabber is now hidden thanks to privacy lists * Gajim has been removed * Added chinese, italian, swedish and brazilian i18n * Added a debian/ folder for packaging * A lot of bug fixes (mainly related to unespace, unicode, playlist, sockets…) Bluemindo 0.2.1 (21 June 2008) * Added Slovak translation * Added a Makefile, .desktop... * Added a plugins management system * Added a plugin to change your status message with your tune (works with Gajim only) * Show the current playing song in bold (if in playlist) * Added three new commands: --volume, --curent-cover and --current-lyrics * Bug fixes related to: * &, < and > in cells * newer GNOME API for media keys * artist fetching * exit if a module can't load * unicode related * previous, next, stop and play buttons * typo 'powerfull' * i18n * desktop notifications * album cover popup * columns ordering Bluemindo 0.2 (27 May 2008) * 4 view mode: * Lightweight * Basic * Normal * Full * Ability to download an artist picture * Ability to send your tune via PEP Bluemindo 0.1 (25 December 2007) * Initial release.bluemindo-0.3/data/image/bluemindo.png0000644000175000017500000005173010723263165017724 0ustar xbrightxbrightPNG  IHDR"sRGBbKGD pHYs B(xtIME  !ʮ IDATxw$WuVU3V9"DA0<16=`$`d ! i6NTGUuWl~jC9\!k/\ \lK P `'}s\$?k ;ˁ˒b`\ϔ)y88q ZE#l; M_ڃǁgܞ k~^drwLqBeB\u F@DJ;RȢJYl^?Klx'~F ^ix 3_D䕼aJF2^:BV? ڡҎlgB?@}1fh9F/xDYM#VJnaF*lxLbP6E;EdC>-hIh<`]OK ASaIc5Tj (R#K} 8:@P%Ӽ{?jv񮧟$&I`$٪@l!0 J@t_SuezuSxKNX*oegU6 @6AS]A! 4wNc ›p `#pff lIq HU2r=1i9@(x0^vg~mU\Ƽ؅Jݥ ~Ɲ<$'.#Cèg!3`Ay~+U~J gog1AVckxg(%Vq"Z&i9@V%8^&s7S̚CîOXV3.zGv8@:χ|pN0)ĕPlH0o! ?g7 Ģ#]pa[١9F#tk򛬍#vs{g5`z VIk9@V 0^1!LJm( \2PsfbH"Y& P }$~mX?Т֢͌ ~cIn%GbFp ՂC5x0j;]DV9u)| w1͇Wo3eٛXYCsvh7,}irq\Y\1\`"RP$.<#x"816"An< ,=_W-ܱNdț+ 3ȩ? 0~yx;GJ2{FpYLUf\EϓԔegRmu)hR㦸p\ }ͥ Q$)[F=#TE[ul}mc&6#Ցa5 A1KX:: ^ yOG0=9p/HedžKACqtF$l=26||kGxHu 5xG_.>iHADncrtp.yYSsr!L]*/: ZuER9h7VmΣ:f6T[Z j#10qPItYbGq bLx} S)>p1VrjA!#ʡ}HkYɂ @~ m ӴoYl>;Lm{P¬۶z" ((Ab5$"1X99<ֿ$8Ř]~>^ȓ zb `(%hAMLS3{ODF!CH:T 6n[ |u(F{GAXh֍rYMR>MIe¼(adQRr% b-n:g_{{vy4FژGJS/"W%F VW9b(νx|Nv?|1>>矱C`4a)KO`mtdD*n߀+|˕+C8g^{S)y%1zZmd ^ Su}#WJ\gΏ߹gBk>+i^BWOE3Dr ^ExYu" y1 t?^\,3Eάrm3HO;[f(e_y?p-B+O͸ȡѱgQ={#Y%PKLO1bo$Z΋qN\GU "#w%ƾ9Z27d *6b`G;o~)_$ q%<)`rM-?axpLe?޳@XsC 6 "lπ> U+răpxly l)bDe{ؕ2o2邋r 0!X/&DgV띣@*S.n``킡'SޯA+bq qu-Io9j7ؖFzUMO{ꏾ,30øǾ$`[b=33A_%.L+εQWT_>k{ C/m`NhUv8"G'?$j Wp vl&kŹvw 8PI P{JI7[rp[Yl+PIJ 5wN{<0^#Tvr GƱGs)xc;R_\w@Ho78.QNdZ;@J Gr cnVd (?\˿PƜJ!^/l8C)g7< k!U1S*uqYI-kw RcTAod&ZB\A7{cY.>Ǵj:."H}d . d2H~>L8ݣXM`޶^f?Kh77p~ܜlrjOcm^LCl+ZaҤ!y%"`$^&`϶׫G՝/5Oq[n`qsG~^oD43iv6y4G4B;qh7,.")H"#"$YBrS+T"?Byp+g{~8xn~_!Df(y(:Bq(`ظ+h5h1c tLJ# ^VH?Imv.WAg"F,*EG\u1qpsF \8Q!.g7]{ F{%ɱ[TX!is?cEcɒjod#Ff1{$-˒dZm4˶ty%K{gEs4^v19gY.y{shdWqs("htYͲaAKgK*$YtݭE"C W}7kNc7&Mr&unxdv\vrѬh92HݲhمF D.GQ9@"&θn_7a(J I{P*B+vgzǽnG"|[\c1=AYz`d@d*T]CTo#"}?.Y>܊!€ғ$hFI&%ITq"eEpw\p"/c^gQHkYK&E ko-?\h&ɨ @l~ID$-0LYDV ",:]?t y)"StB58.Xp "7r,I|Ɵ^!*_ۧGZ";;^͂>ޱ_D~Kw3$|dm="J:bFsK52.xKhz+YvqWOc=ؒhZY$9C("6 w]vd9Pa{DmbQ s,}bܪTKI$1 2?^Ln{I??v$|"$L[DTLH JPΙQ Fde§Pnfa?d3*h߯4Tw-!rP;[@&ymq݈:b*u#)b:z\d%XE-4UCe!huԩ:Ţnq "ۗsf#uIDwHv""ra5agz n,!]w Ī,@JҦei) FR,.4U"U2+ԑnɖh"g,x]"򻅩w_Q";R(%ڕtnQp;d=k"+U>!|u 1KD 2dmΟCxGЊd}T+mZ-YȢ6@i.kذ_c/mS-(n;3IZPdqvZzGC-6Y/#w`䭥-@19<(zXθYJ@\MܴX#e#5rƺQ/ɱjhD6\E%E{Zվ_j00MwM8+}9D~zcAr B@a~@-C&sG(MɦGgP+ǘi`7EDs<{\r DwhXuDBVA]5kZyr憘ske0 'O\D-.1z¾Q:8=XFIe {0ʎQqx79ܐ$h"b 2PYe(Nּo␍bi[&V{}$ 7 ]=yq |4ɵ#|.jaOgx>+\ j)v+;9n{=q*t :7Y|2'M{4x>:}{@-i_Evh?̭V ]C`w\ܘv52HŎ$Pݒ6B>^O2xm*Hheza26˖.Vw,Ĵ> n[>\%"0d46ZsP&uY4 HJ~mӎEw/]BX$ғFP#lIEzO+%S߷ }{ *:,cs֮ncIGrvgF} b 7þFXsr8v _ER,K,2]XĪt*mOU6QͲgc?wN_3FCO.:%-V=xI HW'/9@Nkwkd4q8PwhQ[p"c/ZG$x{3i|a"VE$Y%,!}.t"4 r:YWޫABqєAѯKV+R=џ(7zɡ%gES. Nȷ9F?թE:! =LDѮAr6B۪4l (OY 9C6ճha3Qr9sQ 4"\#'nX ~?$*  0|{-^eʴH5z(-ᚸX,kಫF@1i-*mJ#[*8C9@rK?;mřu%G?h*H7e 2 Xiu| %,PwxwU!Ei7ڛ ⦅ftB eC.ոd`DIjXBIorTW Lf""E$Hn]ov#|qPQ(d)f9)nRgw[-3 㖣89@r뗷V1q>hXBS$޲AaU: 9 L#D9@r#*.PtzxVwX]NDN?k9@ruu"/vD,z:xCZ؏7f8m .Nj,8DNNHhT%= H o m9@r[ʾ܆DO@ T]z2 7D+$%ܬ_S8c"Op$)RL|iS@?ۑ9ѬqN{e=itZa,M"9@r;݌ =1]HVV(OAlܖ |2>SN\~Bcd:%#ȢG (^ pہ_٬9@r;%XDN .:Vv czݫA_/ZYxdミ N xE+u{g0RLC<7N܎Śm3*2 ^ŘB$p[p%fnu37!"4{ ܨձVH5=?RY+VBx0Hnj7sũpq}5Y'P#DRY6?͇^Dc|pwM6BYs0%OV @%V)Sp^/@u1%ýٵ ;v)9cYN A6kDmXRDgG9@r[*c{ݡ&7(m]:kt?^3 I!,$p oQ7wr|%~IU?i^jwq ($C$z%KJ5i,Ѝ>4Hn\V?VF 2Ε6ۈ1qWL4N=X/l;Ī b!tNr_5_Tw<2e5ɒXɡ h. W.u9?ɞxYJ"YHǽrG{~K>4WWGWwgKeX88Ch@hu߉q% )_!ahuFtA2Ufziw?e 0f@ i#[jAQL1 #a S9@r[ʆ$Yw,K4PŶĽ:V<#Q{Gjw)6HnKY-iq\pĆǝ3N \om*!=m%KA _[''ȩka`WX;kvE0ZrzR՘=̹7^$#YW-s9:d \cyأ9G$s){kq`uE׮EמSf&n ۊ=U4Aid7{-Ǣnx^gqrQ7}DkyK{:I&V֫cX 4'+9=sbط[(m@=#k/׉?{U#+j5hdiA7V09B]_Dʌek]ӺЌtsEZ 5*|B* RvuK'c>VNN\^axSiģ."kx}3[}5e bni9DbH *҈#"Djbߗ@!D/"w{E ,h-)8s `0xykbqP(a*DV&ZF ch1`lw)bEˁijl(* "1t>xZ1 ;9v /oּΨ¶ܗ~ٟ;O0( 4<0O]+S.A>a4Z[Pz=-n#Rh*8WK&Y[5O\B8lJRZuzBWa֏ٽrϼϏ s#W |m?{|sϚ_C`5JwL݊o_6P%/z#EC5ۖ=T7'YkW;>=k&^iPRU/Cœ%c<>d+$ pKP35]5;Rࢱ"TQ[ߛnÁ^!H WXg=π}'\1=.cj%Z&r}0R~><#GZIѾƝGD 8v&A8)<,p,Q\d|yK{ /A䖃޾íBT߹ _ܑ3rXWb*HU{ΏR8Uf5>?3Ӓ_iF4"[&CKR{J=4B Dj,;$rXd],MtRʵ~_gz陸C֗x;hH-8̩=ͫt{>w^=eV_sDrV5&V>c=TxGk FZj0{&ղJ3a,{Գ,ZQ^v١Q= &I߉/3K*BCئ4:=p^eXq?Ru4Z>l 0YrGh;glxm0[Ⱦk}*q> ţD,gcdj3G+6`(my׸9U yKs ODSQ?1KGa8,P2Q`Q[+6qѺ|bXoXwݾ)F #d U2 / Tq EsB9 tʔwGD "~bG;2Axk]eB+J@N]Q>uA{@I]zuceFp!Z{5$ٿTOQ^`.@ F0CXsO&@) 1#w?z_rcc^a<Ը#&EkI<5?~U픞*!Ϊw{*\/(=ޓXЧq T'$C= ˏi:.mSRVD[Zu'k |qph>9qd%?`]١7QT%]NͤŊ {8;R=,"B>erD`6߸PQMZ]{"1L&dq4Xcs?<9q*~fGu>۟BxCkA^X(N"[aVhWgF!^W+MsG 4Kc( q3Q>_JEtfĭϡz&jfgqLDnZ2vtJ XWd#[}a`0 >B>ʂ!:J{Awjd_p蔃dMu [u|,*)8\l©7貺X=MՒz\//l4ݭt+Gr|t R+S;o ^^9V?U'iM_͋m7\!0nUML=ݫxG)|ڡEL r ՟q?}Տ|˝=G (ˋ42l.$ZdR3Q\m ܃gřS5 nہOĆ '.Ui>$) یS.|b_8WD=tu+ Ǭ␩MWzقdM[WR8~.?=-x;R^{?$WQ>30˳O8F_Q7q noDɺa*go~ +gnV`IghWtH'"=-l'Mb7Ђȃ©_ٕGS7CC{ J50y4r dQP(nnț*>,Rl:E~Hiѯ?2L0E}sQIb61^ g!퀹܅mW||a/]WqKc->TX,sYļ8NA|An#>+AWsHW@tDWOIM|adegw"U[-hn5[FX `ȍP:lI0@IDATgYCÙVBuí)mx-v]^/NS;1kT8jS*xpeqhdqץYfXtc)/zMHar97]&Dlf,‡tq'S`3F2k/ T+Ln7GJ(mTq~};9tw?ۣAZjE}n f74D1rW-O <ñxU¡Vt+t=tEgoa^aNsO?"vGyЙ9H"aR.VVߺέTL-"t3wxD䋅 "ƬZdĿf\)@&`)|̶ (.ȕQ9|bh7{̂vv@Dbt ;.T d_)Au/YTR+2<k jn%mt<cZ)g2ݻr݈lJXlv{x/ fb[~8(`$vhPu(F|x·aP_}(g S#t"& wJl݉@?3?6/c^ۀcpmK>O,w*>~Jj7^058vv>hs(,Hڷʟ;TNl)|J IRVh9{wOw۷~u1;I`f{ uV"PL@K\ڬB#TfV7GoFM7Ǽxpm _Ã>䔋VڀS- Tg*b;[kR\?v#"W|XwArUνmfzI}E IH!1;.\8T eg#$Jl 6ؤ8qP e(L A2#hl=޻7׋F hzfjuzFݭw>s=ZTB82cbdZK}dOMl-ȑD:8 @?~j"w/-SlҺ=kΛ)M!{O7Oo;3x&_b9q*x5R2eMBVh Q6 Z:*GG/֧"r3JnK|0{`bL&>P%F< yʄFk5)]Ȇmgdv&p-`o4a\G+ɨ KZR-UV@mBE<ו#< |xepcZ9a̜vl3:͛h{=&CL }ڄ.,Zn9kxJ*ՑQLc67?(/Ŭ^1ۙ4ʟwN&.g1wakUP*IռGĴp91~z=9D5]ejqz`@=TzѤ}֗rEЅ,Ep3H] "r]D%9Wab-cba*-G MhY%9+fQQjw'7.ɀD>KfW0#{ZtR=lu/Mni?:If VClR8J@Xi YDqI92."׈VK$q06W=Kc HbLJYN&p*16,!"HPQj?c`xe`Wu&{Ҫ_=_Bb(fRadFk44K3 TG(]Fqj$'PD&9刭!u)|*Fd'}_IF41AJ@u BFD6$/Z%8a+elu& K9+@e)`,Lc+ߍJO'Px~O/ , Gλ_\Eb FdVF!QDX "TI!J!Z%PhU%dS)Wegc{_΀!f&YYŘ0RXL=J~$FD@VPXSw$A)=Em(^lrKfoY?^՜y3 }jM xxtO-J'>\f&_d5LB#FIyf %57|Jh0oۉc=dN=YKE:Ygv΃Lz9~-UFX-"+P2~XIa1!*^(!^GT- fL\.cʘJS`4/zК~֩;9@5#RBOD!@,D1vOiadv4L;z_/eH3I?ft&~T>1N]25K"r3pS4V-V!I1y?LIrD@+)+ri ?!(9]&O"7%`9CE`)3!u"}ؾǶh|;`xhؿv\baXy阃ØsG0#ezM>S%B3y=>}t@#r--v1k,՘P kMҳ9@_˟)f;cF#8LvødG3;X\ %ԗEJ GlڳBD SoZ k2;OF=׌"89`OFfd:}|$OG%y= F>;H25pXa# g(ƪ&%SH67 p';@گ_0>`˯=@N c5[52Ķ۸9%P2lufi/ oø+l^P $Y[OҮ_Aa\X9sMr&L)g 4$Yde9w_*Xu&74[MJt%M9s˷aiee)Fsfe|m#`օ/X :tɗebo}YlsTҜE$^Wᗊ?.B%NG]LO]@ +Μ S#ubvk 9xdTZǰ76]2~j ;IHWxFr4lEX4O|;8ZmE)z:KUIPXO~Y%_zܢ_rm% jzh%V';w&7p+Ui~Xֈi1IhEqB2}]k<=飓P -Zf$Exm?PzޙdGzXՆC2V/+d\5g.i[[J& Y+r+`[TNL̈́ !i+Il b.#?.qp8@[kym^1G᤹Verr&zc2yב#|O?bڷ?q5>lr̓_ڏh5;.5Lixڲ=_\~@n,+Jnql7iF2߅UN [AhPO"e]W,p oO.ZD򻻯X`ew:^=5Z ǁvd+:C+h_w_h]n "#~ zU]W H dH?m{^]fOa(r9־.ӉA^CEVFeˍ`sRwNH@<%o;e:의7^tBlM,i^wY O V,w w17#ߵP?!3<6 D Vkm>q; 7Wu2x etr}[UnWdK:9@n:Гt,7CIENDB`bluemindo-0.3/data/image/logo_head_small_play.png0000644000175000017500000000420311130774613022073 0ustar xbrightxbrightPNG  IHDR szzsRGBbKGD pHYstIMEIDATXí{pT?޽AE AJ 23_1Nm:VQ:J;vceԊӱ(ZIc4b^$dwbxD̙;w93O(5䶟O 7L iVxfOvzēsdm*Unk+GnyEno0ﮯ9;H~ǖa a]ipOU"IO#Dr"H;=뮺ƯYulP|Xz$ 0ADsLAX~zaw7oQjzbE8=p$}9I4qhNґ̑ugO qL fa aA9 T?Aۙ^>3}N~c@w;vI`6κl ʂ?at:Pơs(Kv W/gѴ #$tz,B V\72*[VxMNb`4fh?5E/7ڼak/EK,kwnٷ6gy>'+'hLظ J4$wr!9Qvq[P"Lx!$MUә񣅓zωό]>`Pry?IF4\yGFqCx8GO$;c[0% 0TN h 8yq˃{ DCO^喡*|΋2 RiD: :y{# _Er''%ԙG>|U@YC*9,ri2ҹs^)Wq )`d1$Ǐ3<f+̸ܖ]\%q8ԒBUo5w| Zj>{Ҧz"4c˾#ڟ8 _`IĀ+/d6??DdqF Ahե%I|oY3yz,:|2OZI$y+8`_A|iI]! XGM6NKBӎO~>oH?\kyrӓ{z/KXEcO]s_ ت39ۇ1a8q}%1  ,,4M::{V'i}k+f4jH^%B"GR=D>]Be56cV:>, tb o`ټitZ4qбQ׆!%,NYXZO xk0)Dɋ/ U%m'&NɄ島J c{tw~, {X2Ͽ9lۂV}忦O5?j"#Ȩ/2I I?wrko)u`d]  `؆ )!J{V]i$T9 |RDJ@`E7ܳឥ$ X hkxzs?8xYU``u)Zg'rы+yW']<7D>t_[2d%ҥ87u*r_ FZy?h3bp˥ j3w=ulˤ{µ^ C$P(?Datu=v]aѷ ~ceb;g*Bέ2Y E\s=C GX@|U!v=#"v,s(7ds'J%/h3]LւP-p$u- Sh*l:DUHfI#Аٵ51A nx5hH\Ҍzai.÷A0YVheӽy]~976w"wտ3T&e-!m)PjϬk⥝+ht;sl`x^[3ZӖOc -iUm˘0D:Ri.JBIy~6icFU!&@eD,<5N7V5=3y0^ Ip銏ђ҄^d;腄Z6qEZ'bQĂŝ›&Qԛ g>;k`h 5yw-@`[֢x7剾74tG?9ykrQE)M$7-{AfiT9+Q7>)I/KiwLʘ;s OVk8eceEjY6>1mS*r 5gyZ2+v4जj?830bvM6͘7ڟRLi1⻈7S *8i>̘eˢ墒_[$7gp0 U"]gvӈAݧl[[[\xzNV<e9!HX;>d0gӱDZ\DXoEMe[$, T:Xs 3#fq̚ "v}$w֮ydؚsXҐe`|22 RI|mGcYfXϩ-zw2"AU&|lY=:*yw \\ʉH67;?sKu;%OᅣAY^WcIENDB`bluemindo-0.3/data/image/logo_head_big.png0000644000175000017500000002555410723263165020515 0ustar xbrightxbrightPNG  IHDR>asRGBbKGD pHYstIME  5F IDATxy\U?ԩN 20$@$@H"BN(w;<e!rEAEwU/Td@ BBBN:ꮹꜳsTuI:Yήߴm%"he;[!X iAhzD"I$"qE؊Vm>3~: 0{KYp,B DpO@;gZP}6K~zI]2C\ U"TXq P>l@d DK㹅w pJۯ:!("!Z0 E7 ɂCޖzEgyK`eonYTJ̯7YdAcdz4@}Р!h4T,hR`b{_yގx;^9[E2Amȏ39 :s\#"R/MAi.n sQ[EAwE lܾk&y~oxDBB ֿK!J#.VD.AyOKgF-̙͡#HPg$>6Hռ_A疭{~=6DV sgE >16Rn=%$"kAnz3xlG&‡fE؜˧PE$7{]MxPD>w}TdiDnz||#_/1;8{|ܿTqP#ܛ"'̧ȷΨ#'O6N[W⾗WHZ6f9?! 0龀~uN,KOO׺SEB# r{K3DѶu_: Z1&ۇasWz`Y_Xy`3Wp?"b/f(9/,{;C:T-;W 9(A.b9­OKSDn,ܵҚ|^D~e~߷ل㲩wUL%@ADySC"8+3oj~\futU r?q?[@Ad7 d3ųd~f7U?ExW|<*;i ) -j*g`!zLY _ A&,`@񓏟Yw@*_Rgx3n 0klTa*:'l-xug/N @) D"ZBvH)Z#"HƉe2|{8(N=kv~ :ȔqD#9|QţgwYkO߶?}ocGOh)uԇXھ吵4 t6Xlސ=;׹o[ ;6H>qGLW"U"i1Z# vAAx%h%ir&g Zt7VRΡ4[s: SBtQ<X}9 ^<3ȴ9r(0souB/͒rino/JW.7X%@PAS ^Z{v?{"8o8E&gl9|r鸥Do_Kᜯ}J/j|=MwehteVX=qb8BĽwNۺK9v) 0"hvepG|P5`xiš4 |bljL?T\C|}y/__m#n+w\t|r"F {vw-'^٦lG k*2ƌ0!@[PHCC)%'&?_Mkԧ;,i1#,趆Xד,Ӹ9\6HB)EkU~3@ wcAlh?=M Q?wF7V>C^\ٛ~\qt6TL /,=ۏC_' Y0dhh2(J1Jt=$0o^,*^ {#+_>EQ+7H;Oԧ'UK>BA0'#mC6$!Pyv@A.ͱ as.BGKEko %*rm1_c>ӍOy `A.|iw\uwlt 0omA.KB5NC5A4Dz[Jϟ5(ټ%\WIj8d Bwk .ʲƀ;Ӂt| kO'z.w\êHGppl(d] b0pdq{=׿}oN}P- p ZXS!*-jЋ}5NrO;BZGJxX3It2NkFfsrO{禉.]p 8pC(:h4BuSqfUo&n]ZDv=}jjqg4]3ޑBQ2lWvݼVplDk$x_cAB'5f-68TYxIR )džyf,ov&p0MʓYZo 'ʀmɷcS>_wO1Zg6L]Bm grA˸#}-t4 қ.ťꦇ߮I>_AՐ\ %s<c)l/I^֏b_YO>z[ >WܹDh  `N#7-ꅭC).]4_zX09eU`KkcVHўBZ{ fG?sms\tC"s) B/9;ǿʩ1~۝;[݄VP%ň^1c=?}%V¨f/yF5=W/`ILrj{=a od"sfZR@(<. <Hc= aõ]_iNCn7nDuL ם'7~;`:8PMP'x ĭ>3LY)ӎ<356X7Keq{]u"XALp]`AOI`)uAb\7 [@۠<"RŌ# X-9>h*rĈԀFmic EPh SlQ7}f O5w^H L4^oy-ĉ,-$ g}BGx$SѓO96kxzߥDr? =ḏ *@J@_eJ)%Kޯ10tSDŽȝڡ #X]BWA![`6rMA] `g82!KPӄ l))nX2Y60%L]8P3mp<簳?_ڈnm ,$s( Fqp2ˋTCa nR c g½>y*f_ eJA(cO*i< rJ ?og2kJX]h@j<ɼSA]< 7ZVԼр^ML%H1_dPR?pTѣDP`3*E=hfGW_kf+ #Bw֦3/;%FLk0A,N_kVJ.>ﺁeI (v#6+g4ѺpƀkQ5;ky;_kp?6L7y*@Z GBxq`7T\!m# [blo!1t8{ϾY?ksE=c@);BA~;kk d9PWe h'T4~$Whas˪@(mo?tbZ71;G :q-K}Pvz+; ,? OPs_SLCEF_].ĔM#f#|MNT7νwZ1^y _f1SmCEEl/no$X1"up}Z ~lEkd0Yn?Qҋ[b5Fٜ+}yeDqgŊߧm(PMpJ]:'YT0 V~/j@ `q~{ڻ! okvhN9PMRT jO?q;/]O<2QDa=}&D0oH-/2.*~BLPJ T68xH]Ig @U';3LPC B5p)6F,\MV |}^i;!i+-.&)JSF3)MQQ1PW~]e N IEEv{mkf3/8X ˬWwOHkiEu͌;ZzĠW+f6u/<\ R#2~uPQ^,6psȱ)ʊ5v`zNP{L/+_/_bl=;H,}kr펮H ܜ9_s7zOfca48z~Ry{Px+5%6EǙϜ{&s"r"CZ)OQ^?+/6ֶI!@ ?/}xh2C\ U"I%&#"YRxnxje4 <YH"!ن5sO/97A8 PLy`g "Dd!"- "ҀPH"R$R➓Aa+"[%Vk$-iIENDB`bluemindo-0.3/data/image/logo_head_small_pause.png0000644000175000017500000000366211130774613022253 0ustar xbrightxbrightPNG  IHDR szzsRGBbKGD pHYstIME42IDATXíYlTwgxiL,4E`CQ %%iCiBT%jV}`(T >䥪J(Mm8mocx `~Jss]YZIQz(Q,J/NR;%QQp~yq'\"JFO|>6 `tyZ~ |* oDEKJ$mcVje\o^ S/Jvy{6g6s55I?sa\ҞOk#_GyDܱYw2r2\فKGV|E?:L@d _dD=/W'7$hMwQrzbek]v@:9kX̱!nBԄ9sb8w_hV"$a~޼|TAقOGoG;{W, 'N|wkaԥK Y7 2fNVRY,7 t\~"a$m'?`i/C1egYsEٻI"[:۠s'q^K?fڈ=饩 Rzha˞YJ}/*"Z>*=hD]?)i8|./\)Rz?y&559&'@v9 ҚcǏ,~ٵz"Wa$k$|;2qY7BLD"iҤ&Y+fi:.=smF&V dңiL$з1W;WJ~q(k]>&HlS 7})#=mO$J}5NԅSぞ"Lצ @ 񊐀m١]Xa)ڶM2qzD|wЅl%q, Ա XD۶JqñqH$84] D@~WCҶbD-TH $T`$8 Jk83@˃2#ZKU]EOɅXb$DzLPg)1#OF#yj#sLJ Z" "Remۚ MH&ǩ-8GYD(LHc)[۶Ӭm6f<R4Ցյ4~dp SIENDB`bluemindo-0.3/data/image/logo_head_small.png0000644000175000017500000000362610723263165021060 0ustar xbrightxbrightPNG  IHDR szzsRGBbKGD pHYstIME  -w޲IDATXíilT7x<=/,a3BCi %$!4(ꇤ!Q+uS|U!U!V*TH*DPCHP(fo0̼{aHOI{{b^wNY-.c֦dث10eo<1vcM7VZ7cCj6Ⱦk9z)S(n1vc }\ϩ<O\`quG}9b?V~02mm!ɄVVҥH~E#G3nO /i$_?lQPu#mEFF$"H&){ Iv-̏X9uB7`DpL @܁sy:{Z]n\XRjM tJhq;|YNf:b aN At/[:ֹ8ѼzGspKy l?*/.qz!Mk>~ȗ,Q˲X ,EtMi,o4 "SX#Q_6X ۟UӜXRf]"t['QM,J \7J5L.=ǯѱ8w]"OW76w~#}5đO"s\.`#=a06ȐJT3)\g$(9_3BMsuOT-{e tp&*YJ,90J N:zqu6%uJ^U[@d '|Em?3SIUB[qՌTäw5bkKgɯtEV˭?,{":lA,B9K7kxea=Z3j o";J У@V2}WyUlyҘxTҁLT2y#GB#N}~M0W \UF1(K1͹\h!5PV1^՘==ɔ=uF[:Ջ%TڭrX<Ljeʜt!}O+EpP%G,qAK3CVTrȍz# pA N |\ b" ]4%=B4Py P=YBx_n5hJx nK?4l;saWd`_XC݇]aXѐdb!a@UxZF_O{&f1bω҃d!1Gwi>w*Ml?62 X PP2j -1p:\I~@/&ԌIENDB`bluemindo-0.3/data/misc/bluemindo.png0000644000175000017500000002555411026457124017577 0ustar xbrightxbrightPNG  IHDR>asRGBbKGD pHYstIME  5F IDATxy\U?ԩN 20$@$@H"BN(w;<e!rEAEwU/Td@ BBBN:ꮹꜳsTuI:Yήߴm%"he;[!X iAhzD"I$"qE؊Vm>3~: 0{KYp,B DpO@;gZP}6K~zI]2C\ U"TXq P>l@d DK㹅w pJۯ:!("!Z0 E7 ɂCޖzEgyK`eonYTJ̯7YdAcdz4@}Р!h4T,hR`b{_yގx;^9[E2Amȏ39 :s\#"R/MAi.n sQ[EAwE lܾk&y~oxDBB ֿK!J#.VD.AyOKgF-̙͡#HPg$>6Hռ_A疭{~=6DV sgE >16Rn=%$"kAnz3xlG&‡fE؜˧PE$7{]MxPD>w}TdiDnz||#_/1;8{|ܿTqP#ܛ"'̧ȷΨ#'O6N[W⾗WHZ6f9?! 0龀~uN,KOO׺SEB# r{K3DѶu_: Z1&ۇasWz`Y_Xy`3Wp?"b/f(9/,{;C:T-;W 9(A.b9­OKSDn,ܵҚ|^D~e~߷ل㲩wUL%@ADySC"8+3oj~\futU r?q?[@Ad7 d3ųd~f7U?ExW|<*;i ) -j*g`!zLY _ A&,`@񓏟Yw@*_Rgx3n 0klTa*:'l-xug/N @) D"ZBvH)Z#"HƉe2|{8(N=kv~ :ȔqD#9|QţgwYkO߶?}ocGOh)uԇXھ吵4 t6Xlސ=;׹o[ ;6H>qGLW"U"i1Z# vAAx%h%ir&g Zt7VRΡ4[s: SBtQ<X}9 ^<3ȴ9r(0souB/͒rino/JW.7X%@PAS ^Z{v?{"8o8E&gl9|r鸥Do_Kᜯ}J/j|=MwehteVX=qb8BĽwNۺK9v) 0"hvepG|P5`xiš4 |bljL?T\C|}y/__m#n+w\t|r"F {vw-'^٦lG k*2ƌ0!@[PHCC)%'&?_Mkԧ;,i1#,趆Xד,Ӹ9\6HB)EkU~3@ wcAlh?=M Q?wF7V>C^\ٛ~\qt6TL /,=ۏC_' Y0dhh2(J1Jt=$0o^,*^ {#+_>EQ+7H;Oԧ'UK>BA0'#mC6$!Pyv@A.ͱ as.BGKEko %*rm1_c>ӍOy `A.|iw\uwlt 0omA.KB5NC5A4Dz[Jϟ5(ټ%\WIj8d Bwk .ʲƀ;Ӂt| kO'z.w\êHGppl(d] b0pdq{=׿}oN}P- p ZXS!*-jЋ}5NrO;BZGJxX3It2NkFfsrO{禉.]p 8pC(:h4BuSqfUo&n]ZDv=}jjqg4]3ޑBQ2lWvݼVplDk$x_cAB'5f-68TYxIR )džyf,ov&p0MʓYZo 'ʀmɷcS>_wO1Zg6L]Bm grA˸#}-t4 қ.ťꦇ߮I>_AՐ\ %s<c)l/I^֏b_YO>z[ >WܹDh  `N#7-ꅭC).]4_zX09eU`KkcVHўBZ{ fG?sms\tC"s) B/9;ǿʩ1~۝;[݄VP%ň^1c=?}%V¨f/yF5=W/`ILrj{=a od"sfZR@(<. <Hc= aõ]_iNCn7nDuL ם'7~;`:8PMP'x ĭ>3LY)ӎ<356X7Keq{]u"XALp]`AOI`)uAb\7 [@۠<"RŌ# X-9>h*rĈԀFmic EPh SlQ7}f O5w^H L4^oy-ĉ,-$ g}BGx$SѓO96kxzߥDr? =ḏ *@J@_eJ)%Kޯ10tSDŽȝڡ #X]BWA![`6rMA] `g82!KPӄ l))nX2Y60%L]8P3mp<簳?_ڈnm ,$s( Fqp2ˋTCa nR c g½>y*f_ eJA(cO*i< rJ ?og2kJX]h@j<ɼSA]< 7ZVԼр^ML%H1_dPR?pTѣDP`3*E=hfGW_kf+ #Bw֦3/;%FLk0A,N_kVJ.>ﺁeI (v#6+g4ѺpƀkQ5;ky;_kp?6L7y*@Z GBxq`7T\!m# [blo!1t8{ϾY?ksE=c@);BA~;kk d9PWe h'T4~$Whas˪@(mo?tbZ71;G :q-K}Pvz+; ,? OPs_SLCEF_].ĔM#f#|MNT7νwZ1^y _f1SmCEEl/no$X1"up}Z ~lEkd0Yn?Qҋ[b5Fٜ+}yeDqgŊߧm(PMpJ]:'YT0 V~/j@ `q~{ڻ! okvhN9PMRT jO?q;/]O<2QDa=}&D0oH-/2.*~BLPJ T68xH]Ig @U';3LPC B5p)6F,\MV |}^i;!i+-.&)JSF3)MQQ1PW~]e N IEEv{mkf3/8X ˬWwOHkiEu͌;ZzĠW+f6u/<\ R#2~uPQ^,6psȱ)ʊ5v`zNP{L/+_/_bl=;H,}kr펮H ܜ9_s7zOfca48z~Ry{Px+5%6EǙϜ{&s"r"CZ)OQ^?+/6ֶI!@ ?/}xh2C\ U"I%&#"YRxnxje4 <YH"!ن5sO/97A8 PLy`g "Dd!"- "ҀPH"R$R➓Aa+"[%Vk$-iIENDB`bluemindo-0.3/data/misc/Bluemindo.desktop0000644000175000017500000000042111154031467020406 0ustar xbrightxbright[Desktop Entry] Version=1.0 Name=Bluemindo GenericName=Bluemindo Audio Player Comment=A really simple but powerful audio player in Python/PyGTK, using Gstreamer. Exec=bluemindo Terminal=false Type=Application Icon=bluemindo.png Categories=AudioVideo;Audio;Music;Player;GTK;bluemindo-0.3/data/misc/bluemindo.10000644000175000017500000000546311241603412017140 0ustar xbrightxbright.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH bluemindo 1 "June 22, 2008" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME bluemindo \- simple yet powerfull audio player .SH SYNOPSIS .B bluemindo .RI [ options ] .SH DESCRIPTION This manual page documents briefly the .B bluemindo command. .PP .\" TeX users may be more comfortable with the \fB\fP and .\" \fI\fP escape sequences to invode bold face and italics, .\" respectively. \fBbluemindo\fP Bluemindo is a really simple but powerful audio player in Python/PyGTK, using GStreamer. It can downloads albums or artists pictures and lyrics as doing desktop notifications or sending music to your Last.fm profile or on your Jabber account (via PEP). You can also choose the view mode: lightweight (very simple way to use the player: you only have a playlist), basic (you have a tree of artists>albums and a playlist), normal (the most usual mode for audio players), full (you have a list of artists and saved playlists, a list of albums covers by artists and the playlist) or albums. .SH OPTIONS These programs follow the usual GNU command line syntax, with long options starting with two dashes (`-'). A summary of options is included below. For a complete description, see the Info files. .TP .B \-h, \-\-help Show summary of options. .TP .B \-\-current Show the current playing song artist, title and album. .TP .B \-\-current\-cover Show the path to the album cover of the current playing song. .TP .B \-\-current\-lyrics Show the lyrics for the current playing song. .TP .TP .B \-\-playpause, \-\-play, \-\-pause Play or pause the song. .TP .B \-\-stop Stop the song. .TP .B \-\-previous Jump to the previous song in playlist. .TP .B \-\-next Jump to the next song in playlist. .TP .TP .B \-\-volume-more [ STEP ] Increase the volume, you can specify a step (0 > 100). .TP .B \-\-volume-less [ STEP ] Decrease the volume, you can specify a step (0 > 100). .TP .B \-\-volume=VOLUME Set the volume: 0 > 100. .TP .TP .B \-\-reload Reload the songs from your music folder. .TP .B \-\-quit, \-\-plunge Quit Bluemindo. .SH AUTHOR bluemindo was written by Erwan Briand . .PP This manual page was written by Thibaut GIRKA , for the Debian project (but may be used by others).bluemindo-0.3/data/misc/bluemindo0000755000175000017500000000007011154031467017001 0ustar xbrightxbright#!/bin/sh cd /usr/share/bluemindo/src ./bluemindo.py $@bluemindo-0.3/data/glade/playlistmenu.glade0000644000175000017500000002301611150355570020747 0ustar xbrightxbright True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Play now True True gtk-media-play 1 True True Add to playlist True True True gtk-add 1 True Remove from this playlist True True gtk-delete 1 True True Show lyrics True True gtk-justify-fill 1 Edit song's datas True True gtk-edit 1 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 Playlist GTK_WIN_POS_CENTER_ON_PARENT GDK_WINDOW_TYPE_HINT_DIALOG False True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 60 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 GTK_SHADOW_NONE True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 80 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Name: True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Create a new playlist</b> True label_item False 1 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK GTK_BUTTONBOX_END True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-cancel True 0 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-ok True 1 1 False GTK_PACK_END bluemindo-0.3/data/glade/prefswindow.glade0000644000175000017500000002556511132101746020575 0ustar xbrightxbright 700 380 True window1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 200 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_IN True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False 10 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False 1 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Modules tab False True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 250 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_IN True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False 10 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False 1 1 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Plugins tab 1 False True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 GTK_BUTTONBOX_END True True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-about True 0 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-close True 0 1 False 5 1 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 À propos de Glade False GTK_WIN_POS_CENTER_ON_PARENT GDK_WINDOW_TYPE_HINT_DIALOG False Glade True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK GTK_BUTTONBOX_END False GTK_PACK_END bluemindo-0.3/data/glade/aboutdialog.glade0000644000175000017500000011223611241615113020507 0ustar xbrightxbright GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 About Bluemindo False GTK_WIN_POS_CENTER_ON_PARENT GDK_WINDOW_TYPE_HINT_DIALOG False Bluemindo 0.3 Copyright © 2007-2009 Erwan Briand A really simple but powerful audio player in Python/PyGTK. http://bluemindo.codingteam.net GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>. Erwan Briand <erwan@codingteam.net> Contributors: * Vincent Berset <msieurhappy@gmail.com> * Thibaut Girka <thibaut.girka@gmail.com> * Ľubomír Remák <lubomirr88@gmail.com> * Anaël Verrier <elghinn@free.fr> Brazilian Portuguese: Bruno Conde <blconde@gmail.com> Swedish: Niklas Grahn <terra.unknown@yahoo.com> Slovak: Ľubomír Remák <lubomirr88@gmail.com> Italian: Salvatore Tomarchio <tommyx_x@yahoo.it> Chinese: Shang Yuanchun <05281253@bjtu.edu.cn> Thomas Julien <terr1enrun@gmail.com> True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK GTK_BUTTONBOX_END False GTK_PACK_END bluemindo-0.3/data/glade/mainwindow.glade0000644000175000017500000024521111224222634020374 0ustar xbrightxbright window1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True _File True True gtk-refresh True True True gtk-preferences True True True True gtk-quit True True True _Show True True <main>/_Show/Lightweight _Lightweight True True True <main>/_Show/Basic _Basic True True menu-view-lightweight True <main>/_Show/Normal _Normal True True menu-view-lightweight True <main>/_Show/Full _Full True True menu-view-lightweight True <main>/_Show/Albums Alb_ums True True menu-view-lightweight True <main>/_Show/Playlists _Playlists True True menu-view-lightweight True <main>/_Show/Webradios _Webradios True True menu-view-lightweight True True Fullscreen True True True Small display (only player) True True True Display all True True True _Help True True gtk-about True True False True True 200 True True True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_IN True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False True 0 0 True 2 3 True True True gtk-index False True 0.10000000149011612 <big><b>toto</b></big> True 1 False 5 200 True True GTK_POLICY_NEVER GTK_POLICY_AUTOMATIC GTK_SHADOW_IN True True False False 1 True True gtk-dnd-multiple False True 0.10000000149011612 <big><b>toto</b></big> True 1 False 5 2 True 1 True Add a new playlist gtk-add True True Delete gtk-delete True True True True Import gtk-go-down True True Export gtk-go-up True False 3 True True GTK_POLICY_NEVER GTK_POLICY_AUTOMATIC GTK_SHADOW_IN True True False 4 label_item 1 True True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_IN True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False 2 True True GTK_POLICY_NEVER GTK_POLICY_AUTOMATIC GTK_SHADOW_IN True True False 3 True True True True Group artists 0 True False False False False True True True True True True GTK_RELIEF_NONE 0 True Delete filter gtk-remove 1 False 1 False 1 True True GTK_POLICY_NEVER GTK_POLICY_AUTOMATIC GTK_SHADOW_IN True GTK_RESIZE_QUEUE True GTK_BUTTONBOX_START 170 42 True True True 0 True 24 24 True gtk-missing-image False False 2 True True 0 label True True PANGO_ELLIPSIZE_END False True 0 label True True PANGO_ELLIPSIZE_END False 1 1 False False 2 4 False True True True GTK_TOOLBAR_ICONS True gtk-media-previous True True gtk-media-stop True True gtk-media-play True True gtk-media-next True True False 100 True True 100 100 True True 0 True gtk-missing-image False False True True 0 GTK_SHADOW_NONE True 12 True True True True False True True True False 1 True True True label_item True GTK_TOOLBAR_ICONS 1 True Show song lyrics gtk-file True True 300 True True 0 0 100 1 10 10 False GTK_POS_RIGHT True True True True 1 False 1 1 False 1 True True True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_IN True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 True True 170 True 100 True True GTK_POLICY_NEVER GTK_POLICY_AUTOMATIC GTK_SHADOW_ETCHED_OUT True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 100 True True GTK_POLICY_NEVER GTK_POLICY_AUTOMATIC GTK_SHADOW_ETCHED_OUT True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 True 0 GTK_SHADOW_ETCHED_OUT True 12 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK GTK_POLICY_AUTOMATIC GTK_POLICY_NEVER True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK GTK_RESIZE_QUEUE GTK_SHADOW_NONE True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 GTK_BUTTONBOX_START 125 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 120 100 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-missing-image True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK label True 1 False False True <b>frame2</b> True label_item 2 False True 600 True True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_IN True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False 3 True GTK_TOOLBAR_ICONS 1 True Clean the playlist gtk-clear True True Shuffle the playlist True media-playlist-shuffle True True Repeat mode True media-playlist-repeat True True True 100 True True 150 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Delete filter Cancel filter True gtk-remove True False 4 True GTK_TOOLBAR_ICONS 1 True Reload lyrics for this song gtk-refresh True True Save lyrics for this song (this feature allows you to edit lyrics or add lyrics for a song) gtk-save True False 5 True True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_IN True True 6 True 1 True Add gtk-add True True Delete gtk-remove True True Refresh gtk-refresh True True True True True True True False 7 True True 1 True 200 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0.10000000149 False True 1 False False 2 True 0 True True gtk-missing-image False False 10 True label True True False False 1 3 True True gtk-missing-image True 0 GTK_SHADOW_NONE True 12 True True 0.0099999997764825821 label False 150 True 0.0099999997764825821 label False 1 True True 0.99000000953674316 label True False GTK_PACK_END 2 True <b>frame3</b> True label_item 1 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 Webradio GTK_WIN_POS_CENTER_ON_PARENT GDK_WINDOW_TYPE_HINT_DIALOG False True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 60 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 GTK_SHADOW_NONE True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 80 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 Name: True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 False 5 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 80 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 URL: True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 False 5 1 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Add a new radio</b> True label_item 1 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK GTK_BUTTONBOX_END True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-cancel True 0 False False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-ok True 1 False False 1 False GTK_PACK_END bluemindo-0.3/data/glade/boxes.glade0000644000175000017500000001172711224222634017343 0ustar xbrightxbright 5 False GTK_WIN_POS_CENTER GDK_WINDOW_TYPE_HINT_DIALOG True True False GTK_MESSAGE_ERROR GTK_BUTTONS_CLOSE True True 2 True GTK_BUTTONBOX_END False GTK_PACK_END 5 GTK_WIN_POS_CENTER_ON_PARENT GDK_WINDOW_TYPE_HINT_DIALOG False True 2 True True 0.10000000149011612 0.10000000149011612 gtk-dialog-question 6 False 5 True 0 0.10000000149011612 label True True 2 1 1 True GTK_BUTTONBOX_END True True gtk-yes True 0 True True True True gtk-no True 1 1 False 5 GTK_PACK_END bluemindo-0.3/data/glade/albumpreview.glade0000644000175000017500000000416411026457124020726 0ustar xbrightxbright 300 300 GTK_WINDOW_POPUP window1 True GDK_WINDOW_TYPE_HINT_DOCK True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 GTK_SHADOW_OUT True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-missing-image label_item bluemindo-0.3/COPYING0000644000175000017500000010451211126267466014303 0ustar xbrightxbright GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read .bluemindo-0.3/THANKS0000644000175000017500000000062511166120617014151 0ustar xbrightxbrightCODE: Vincent Berset Thibaut Girka Ľubomír Remák Anaël Verrier TRANSLATE: Bruno Conde Niklas Grahn Ľubomír Remák Salvatore Tomarchio Shang Yuanchun <05281253@bjtu.edu.cn> ARTWORK: Thomas Julien bluemindo-0.3/Makefile0000644000175000017500000000402211224661214014667 0ustar xbrightxbrightprefix=/usr LIBDIR=$(DESTDIR)$(prefix)/lib BIN=$(DESTDIR)$(prefix)/bin DATADIR=$(DESTDIR)$(prefix)/share LOCALEDIR=$(DATADIR)/locale MANDIR=$(DATADIR)/man all: clean: rm -f locale/*/LC_MESSAGES/bluemindo.mo rm -f bluemindo.1.gz install: install -d $(LOCALEDIR) $(BIN) $(DATADIR)/bluemindo $(DATADIR)/bluemindo/image $(DATADIR)/bluemindo/glade $(DATADIR)/bluemindo/src install -m644 data/image/*.png $(DATADIR)/bluemindo/image install -m644 data/glade/*.glade $(DATADIR)/bluemindo/glade install -m644 data/misc/Bluemindo.desktop $(DATADIR)/applications install -m644 data/misc/bluemindo.png $(DATADIR)/pixmaps install -m755 data/misc/bluemindo $(BIN) cat data/misc/bluemindo.1 | gzip > bluemindo.1.gz install -m644 bluemindo.1.gz $(MANDIR)/man1 for sourcedir in `find src/ -type d | grep -v '.svn' | grep -v '.pyc' | sed 's:src/::g'` ; do \ install -d $(DATADIR)/bluemindo/src/$$sourcedir; \ for sourcefile in `find src/$$sourcedir -maxdepth 1 -type f | grep -v '.svn' | grep -v '.pyc'` ; do \ install -m644 $$sourcefile $(DATADIR)/bluemindo/src/$$sourcedir; \ done \ done install -m755 src/bluemindo.py $(DATADIR)/bluemindo/src install -m644 src/mainapplication.py $(DATADIR)/bluemindo/src install -m644 src/extensionsloader.py $(DATADIR)/bluemindo/src for localename in `find locale/ -maxdepth 1 -type d | grep -v '.svn' | sed 's:locale/::g'` ; do \ if [ -d locale/$$localename ]; then \ install -d $(LOCALEDIR)/$$localename; \ install -d $(LOCALEDIR)/$$localename/LC_MESSAGES; \ msgfmt locale/$$localename/LC_MESSAGES/bluemindo.po -o locale/$$localename/LC_MESSAGES/bluemindo.mo -v; \ install -m644 locale/$$localename/LC_MESSAGES/bluemindo.mo $(LOCALEDIR)/$$localename/LC_MESSAGES; \ fi \ done uninstall: rm -f $(DATADIR)/applications/Bluemindo.desktop rm -f $(DATADIR)/pixmaps/bluemindo.png rm -f $(BIN)/bluemindo rm -f $(MANDIR)/man1/bluemindo.1.gz rm -rf $(LIBDIR)/bluemindo rm -rf $(DATADIR)/bluemindo for gettextfile in `find $(LOCALEDIR) -name 'bluemindo.mo'` ; do \ rm -f $$gettextfile; \ donebluemindo-0.3/README0000644000175000017500000000232111241615030014101 0ustar xbrightxbrightBluemindo 0.3 A really simple but powerful audio player in Python/PyGTK, using GStreamer. Project site: * http://bluemindo.codingteam.net SVN Browse: * http://svn.codingteam.net/bluemindo * http://codingteam.net/project/bluemindo/browse ************ CREDITS ************ Author informations are in the ~/AUTHORS. For the list of contributors, see ~/THANKS. Bluemindo's sources include these libs: * PyScrobbler http://svn.berlios.de/wsvn/sonata/trunk/audioscrobbler.py From: Sonata Author: Andy Theyers License: GPLv3 ************ TRANSLATION ************ → Create the POT model: find . -name '*.py' | grep -v '.svn/' > file_list.txt && find . -name '*.glade' | grep -v '.svn/' >> file_list.txt xgettext -f file_list.txt --from-code=utf-8 --keyword=_ -o locale/bluemindo.pot rm file_list.txt → Create a PO file for your language: msginit -i locale/bluemindo.pot -o locale/$LANG/LC_MESSAGES/bluemindo.po --locale=$LL_CC → Update a PO file for your language: msgmerge -U locale/$LANG/LC_MESSAGES/bluemindo.po locale/bluemindo.pot → Compile a MO file: msgfmt locale/$LANG/LC_MESSAGES/bluemindo.po -o locale/$LANG/LC_MESSAGES/bluemindo.mo -v