├── .travis.yml ├── .gitignore ├── src ├── main │ ├── config │ │ └── license-templates │ │ │ └── APACHE-2.txt │ └── java │ │ └── info │ │ └── schnatterer │ │ └── songbirddbapi4j │ │ ├── domain │ │ ├── package-info.java │ │ ├── util │ │ │ ├── package-info.java │ │ │ ├── StringUtils.java │ │ │ └── MemberMediaItemComparator.java │ │ ├── SimpleMediaList.java │ │ ├── MemberMediaItem.java │ │ ├── MediaListTypes.java │ │ ├── MediaItem.java │ │ └── Property.java │ │ ├── package-info.java │ │ ├── SongbirdDbConnection.java │ │ └── SongbirdDb.java └── test │ └── java │ └── info │ └── schnatterer │ └── songbirddbapi4j │ └── domain │ └── util │ └── MemberMediaItemComparatorTest.java ├── README.md ├── songbird.sql ├── pom.xml └── LICENSE /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | 14 | ## Eclipse 15 | *.pydevproject 16 | .project 17 | .metadata 18 | bin/** 19 | tmp/** 20 | tmp/**/* 21 | *.tmp 22 | *.bak 23 | *.swp 24 | *~.nib 25 | local.properties 26 | .classpath 27 | .settings/ 28 | .loadpath 29 | 30 | ## Maven 31 | /target -------------------------------------------------------------------------------- /src/main/config/license-templates/APACHE-2.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) ${project.inceptionYear} ${owner} 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /src/main/java/info/schnatterer/songbirddbapi4j/domain/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Johannes Schnatterer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * Contains the domain classes (also known as entities). 19 | * @author schnatterer 20 | * 21 | */ 22 | package info.schnatterer.songbirddbapi4j.domain; 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/info/schnatterer/songbirddbapi4j/domain/util/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Johannes Schnatterer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * Contains utility classes for the domain classes (entities). 19 | * @author schnatterer 20 | * 21 | */ 22 | package info.schnatterer.songbirddbapi4j.domain.util; 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/info/schnatterer/songbirddbapi4j/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Johannes Schnatterer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * Contains the services (also know as DAOs) that use wrap the actual database logic and use the entities in the 19 | * domain package. 20 | * @author schnatterer 21 | * 22 | */ 23 | package info.schnatterer.songbirddbapi4j; 24 | 25 | -------------------------------------------------------------------------------- /src/main/java/info/schnatterer/songbirddbapi4j/domain/SimpleMediaList.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Johannes Schnatterer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.schnatterer.songbirddbapi4j.domain; 17 | 18 | import info.schnatterer.songbirddbapi4j.domain.util.MemberMediaItemComparator; 19 | 20 | import java.util.Collections; 21 | import java.util.LinkedList; 22 | import java.util.List; 23 | 24 | /** 25 | * A Playlist object. 26 | * 27 | * @author schnatterer 28 | * 29 | */ 30 | public class SimpleMediaList { 31 | 32 | /** The Media item representing the play list. */ 33 | private MediaItem list; 34 | 35 | /** The MediaItems representing the member. */ 36 | private List members = new LinkedList(); 37 | 38 | /** 39 | * @return the list 40 | */ 41 | public MediaItem getList() { 42 | return list; 43 | } 44 | 45 | /** 46 | * @param newList 47 | * the list to set 48 | */ 49 | public void setList(final MediaItem newList) { 50 | this.list = newList; 51 | } 52 | 53 | /** 54 | * @return the members 55 | */ 56 | public List getMembers() { 57 | return members; 58 | } 59 | 60 | /** 61 | * @param newMembers 62 | * the members to set 63 | */ 64 | public void setMembers(final List newMembers) { 65 | this.members = newMembers; 66 | } 67 | 68 | /** 69 | * @param descending 70 | * true sorts descendingly. 71 | */ 72 | public void sortMembers(final boolean descending) { 73 | Collections.sort(members, new MemberMediaItemComparator(false)); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # songbirdDbApi4j 2 | 3 | [![Build Status](https://travis-ci.org/schnatterer/songbirdDbApi4j.svg?branch=master)](https://travis-ci.org/schnatterer/songbirdDbApi4j) 4 | [![JitPack](https://jitpack.io/v/schnatterer/songbirdDbApi4j.svg)](https://jitpack.io/#schnatterer/songbirdDbApi4j) 5 | [![License](https://img.shields.io/github/license/schnatterer/songbirdDbApi4j.svg)](LICENSE) 6 | 7 | A java wrapper for songbird SQLite database. 8 | 9 | ## How to use 10 | For now, this is not hosted on maven central, but on this very repository. To use it, you have two options: 11 | 12 | 1. Add this repo as maven repository 13 | 2. Use jitPack 14 | 15 | See the [wiki](https://github.com/schnatterer/songbirdDbApi4j/wiki) for details on how to use the API or see the [songbirdDbTools](https://github.com/schnatterer/songbirdDbTools) that makes use of this API. 16 | 17 | ### Add this repo as maven repository 18 | Add the following maven repository to your POM.xml 19 | 20 | 21 | 22 | songbirdDbApi4j-mvn-repo 23 | https://raw.github.com/schnatterer/songbirdDbApi4j/mvn-repo/ 24 | 25 | 26 | Then add the actual dependency 27 | 28 | 29 | info.schnatterer 30 | songbirdDbApi4j 31 | 1.0 32 | 33 | 34 | ### Use jitPack 35 | Add the following maven repository to your POM.xml 36 | 37 | 38 | 39 | jitpack.io 40 | https://jitpack.io 41 | 42 | 43 | Then add the actual dependency 44 | 45 | 46 | com.github.schnatterer 47 | songbirdDbApi4j 48 | v.2.0 49 | 50 | 51 | ## Release notes 52 | See [Releases] (https://github.com/schnatterer/songbirdDbApi4j/releases). 53 | 54 | ## Examples 55 | 56 | This API is used in the following projects for example 57 | - [songbirdDbTools](https://github.com/schnatterer/songbirdDbTools) 58 | - [songbird2itunes](https://github.com/schnatterer/songbird2itunes) 59 | -------------------------------------------------------------------------------- /songbird.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2015 Johannes Schnatterer 3 | -- 4 | -- Licensed under the Apache License, Version 2.0 (the "License"); 5 | -- you may not use this file except in compliance with the License. 6 | -- You may obtain a copy of the License at 7 | -- 8 | -- http://www.apache.org/licenses/LICENSE-2.0 9 | -- 10 | -- Unless required by applicable law or agreed to in writing, software 11 | -- distributed under the License is distributed on an "AS IS" BASIS, 12 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | -- See the License for the specific language governing permissions and 14 | -- limitations under the License. 15 | -- 16 | 17 | 18 | select * from MEDIA_ITEMS where content_url like '%songname%' 19 | 20 | select DISTINCT item.media_item_id from MEDIA_ITEMS item 21 | inner join SIMPLE_MEDIA_LISTS list ON item.media_item_id = list.media_item_id COLLATE NOCASE 22 | 23 | select DISTINCT media_item_id FROM MEDIA_ITEMS 24 | 25 | 26 | select distinct list.media_item_id collate nocase from SIMPLE_MEDIA_LISTS list 27 | left join RESOURCE_PROPERTIES 28 | 29 | 30 | -- Duplicates in Playlist 31 | select * from MEDIA_ITEMS i 32 | left join RESOURCE_PROPERTIES p on i.media_item_id = p.media_item_id 33 | where p.property_id = 15 and obj = 'Playlistname.m3u' 34 | 35 | select count(l.member_media_item_id) as numberOfItems, l.member_media_item_id, i.content_url from SIMPLE_MEDIA_LISTS l 36 | left join MEDIA_ITEMS i ON l.member_media_item_id = i.media_item_id 37 | --left join RESOURCE_PROPERTIES artist ON l.media_item_id = artist.media_item_id -- AND artist.property_id = 3 38 | --left join RESOURCE_PROPERTIES track ON l.media_item_id = track.media_item_id AND track.property_id = 1 39 | where l.media_item_id = 31917 collate nocase 40 | group by member_media_item_id 41 | order by numberOfItems desc; 42 | 43 | 44 | -- Playlists a file belongs to 45 | select props.*, item.content_url from MEDIA_ITEMS item 46 | inner join SIMPLE_MEDIA_LISTS mem on item.media_item_id = mem.member_media_item_id 47 | inner join MEDIA_ITEMS list on mem.media_item_id = list.media_item_id 48 | inner join RESOURCE_PROPERTIES props on list.media_item_id = props.media_item_id and property_id = 15 49 | where item.content_url = lower('file:///X:/music/file.mp3') 50 | -------------------------------------------------------------------------------- /src/main/java/info/schnatterer/songbirddbapi4j/domain/util/StringUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Johannes Schnatterer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Copyright (C) 2013 Johannes Schnatterer 18 | * See the NOTICE file distributed with this work for additional 19 | * information regarding copyright ownership. 20 | * 21 | * Licensed under the Apache License, Version 2.0 (the "License"); 22 | * you may not use this file except in compliance with the License. 23 | * You may obtain a copy of the License at 24 | * 25 | * http://www.apache.org/licenses/LICENSE-2.0 26 | * 27 | * Unless required by applicable law or agreed to in writing, software 28 | * distributed under the License is distributed on an "AS IS" BASIS, 29 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 30 | * See the License for the specific language governing permissions and 31 | * limitations under the License. 32 | */ 33 | 34 | package info.schnatterer.songbirddbapi4j.domain.util; 35 | 36 | /** 37 | * Contains utility methods for {@link String} operations. 38 | * 39 | * @author schnatterer 40 | * 41 | */ 42 | public final class StringUtils { 43 | /** Don't instantiate utility classes! */ 44 | private StringUtils() { 45 | } 46 | 47 | /** 48 | * Returns a substring before a certain index, excluding the character at 49 | * the index. 50 | * 51 | * @param str 52 | * string to find substring in 53 | * @param index 54 | * position of the first character that is excluded. 55 | * @return a substring before a certain index, excluding the character at 56 | * the index. 57 | */ 58 | public static String substringBefore(final String str, final int index) { 59 | return str.substring(0, index); 60 | } 61 | 62 | /** 63 | * Returns a substring after a certain index, excluding the character at the 64 | * index. 65 | * 66 | * @param str 67 | * string to find substring in 68 | * @param substring 69 | * the returned value begins after this substring. 70 | * @return a substring after a certain index, excluding the character at the 71 | * index. 72 | */ 73 | public static String substringAfter(final String str, final String substring) { 74 | return str.substring(substring.length() + 1); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/info/schnatterer/songbirddbapi4j/domain/MemberMediaItem.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Johannes Schnatterer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Copyright (C) 2013 Johannes Schnatterer 18 | * See the NOTICE file distributed with this work for additional 19 | * information regarding copyright ownership. 20 | * 21 | * Licensed under the Apache License, Version 2.0 (the "License"); 22 | * you may not use this file except in compliance with the License. 23 | * You may obtain a copy of the License at 24 | * 25 | * http://www.apache.org/licenses/LICENSE-2.0 26 | * 27 | * Unless required by applicable law or agreed to in writing, software 28 | * distributed under the License is distributed on an "AS IS" BASIS, 29 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 30 | * See the License for the specific language governing permissions and 31 | * limitations under the License. 32 | */ 33 | 34 | package info.schnatterer.songbirddbapi4j.domain; 35 | 36 | /** 37 | * A member object of a playlist ({@link SimpleMediaList}), contains a {@link MediaItem} as well as the ordinal within 38 | * the playlist. 39 | * 40 | * @author schnatterer 41 | * 42 | */ 43 | public class MemberMediaItem { 44 | 45 | /** 46 | * Ordinal, that is the position of the member within the playlist. Looks something like: 47 | * 72.31.0.20.2.0.-1.-1.1.-1.0. 48 | * 49 | * Can be sorted using {@link info.schnatterer.songbirddbapi4j.domain.util.MemberMediaItemComparator}. 50 | */ 51 | private String oridnal; 52 | /** The actual member object. */ 53 | private MediaItem member; 54 | 55 | /** 56 | * @return the position of the member within the playlist 57 | */ 58 | public String getOridnal() { 59 | return oridnal; 60 | } 61 | 62 | /** 63 | * @return the actual member object. 64 | */ 65 | public MediaItem getMember() { 66 | return member; 67 | } 68 | 69 | /** 70 | * @param newOrdinal 71 | * the oridnal to set 72 | */ 73 | public void setOridnal(final String newOrdinal) { 74 | this.oridnal = newOrdinal; 75 | } 76 | 77 | /** 78 | * @param newMember 79 | * the member to set 80 | */ 81 | public void setMember(final MediaItem newMember) { 82 | this.member = newMember; 83 | } 84 | 85 | /* 86 | * (non-Javadoc) 87 | * 88 | * @see java.lang.Object#toString() 89 | */ 90 | @Override 91 | public String toString() { 92 | return "MemberMediaItem [oridnal=" + oridnal + ", member=" + member + "]"; 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/info/schnatterer/songbirddbapi4j/domain/MediaListTypes.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Johannes Schnatterer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.schnatterer.songbirddbapi4j.domain; 17 | 18 | import info.schnatterer.songbirddbapi4j.SongbirdDbConnection; 19 | 20 | import java.sql.ResultSet; 21 | import java.sql.SQLException; 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | 25 | /** 26 | * The type of a {@link MediaItem}. 27 | * 28 | * @author schnatterer 29 | * 30 | */ 31 | public final class MediaListTypes { 32 | /** Don't instantiate utility classes! */ 33 | private MediaListTypes() { 34 | } 35 | 36 | /** Gets all available list types. */ 37 | public static final String QUERY_LIST_TYPES = "select * from media_list_types"; 38 | 39 | /* 40 | * Why not use enum, which would be easier to use and more efficient? 41 | * Software must be adapted to DB changes, i.e. map is more flexible 42 | */ 43 | /** Mapping from string constants (database enums) to numerical IDs. */ 44 | private static Map listTyep2IdMap; 45 | /** Mapping from numerical IDs to string constants (database enums). */ 46 | private static Map id2ListTypeMap; 47 | 48 | /** 49 | * Initializes the mappings from string constants (database enums) to 50 | * numerical IDs and the other way round from the database. 51 | * 52 | * To avoid doing this again and again, use {@link #isInitialized()}. 53 | * 54 | * @param connection 55 | * database connection to use for querying 56 | * @throws SQLException 57 | * in case an error occurs during the db query. 58 | * 59 | */ 60 | public static void populatelistTypeMap(SongbirdDbConnection connection) 61 | throws SQLException { 62 | listTyep2IdMap = new HashMap(); 63 | id2ListTypeMap = new HashMap(); 64 | ResultSet rs = connection.executeQuery(QUERY_LIST_TYPES); 65 | while (rs.next()) { 66 | // read the result set 67 | Integer id = rs.getInt(1); 68 | String type = rs.getString(2); 69 | id2ListTypeMap.put(id, type); 70 | listTyep2IdMap.put(type, id); 71 | // logger.debug("id=" + id + "; type=" + type ); 72 | } 73 | } 74 | 75 | /** 76 | * @param id 77 | * a numerical ID to be mapped to the corresponding string 78 | * @return the string representation of a numerical ID. 79 | */ 80 | public static String id2ListType(final int id) { 81 | return id2ListTypeMap.get(id); 82 | } 83 | 84 | /** 85 | * @param listType 86 | * a string representation to be mapped to the corresponding 87 | * numerical ID. 88 | * @return the numerical ID of a string representation. 89 | */ 90 | public static int listType2Id(final String listType) { 91 | return listTyep2IdMap.get(listType); 92 | } 93 | 94 | /** 95 | * Checks if maps have been initialized from database. If not better call 96 | * {@link #populatelistTypeMap(SongbirdDbConnection)} 97 | * 98 | * @return true if initialized, otherwise false 99 | */ 100 | public static boolean isInitialized() { 101 | if (id2ListTypeMap == null || id2ListTypeMap.size() == 0 102 | || listTyep2IdMap == null || listTyep2IdMap.size() == 0) { 103 | return false; 104 | } else { 105 | return true; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/info/schnatterer/songbirddbapi4j/domain/util/MemberMediaItemComparator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Johannes Schnatterer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.schnatterer.songbirddbapi4j.domain.util; 17 | 18 | import info.schnatterer.songbirddbapi4j.domain.MemberMediaItem; 19 | 20 | import java.util.Comparator; 21 | 22 | /** 23 | * {@link Comparator}, that allows for comparing two {@link MemberMediaItem}s. 24 | * This is useful for ordering them by their 25 | * {@link MemberMediaItem#getOridnal()}, which is a String containing numbers 26 | * separated by dots. 27 | * 28 | * @author schnatterer 29 | * 30 | */ 31 | public class MemberMediaItemComparator implements Comparator { 32 | 33 | /** The character that separates the individual numbers within the ordinal. */ 34 | private static final char SEPARATOR = '.'; 35 | /** Sort descending? */ 36 | private boolean desc = false; 37 | 38 | /** 39 | * Constructs a new {@link MemberMediaItem}, which sorts the list in a 40 | * descending/ascending order. 41 | * 42 | * @param descending 43 | * if true, the list is sorted descendingly, 44 | * otherwise ascendingly. 45 | */ 46 | public MemberMediaItemComparator(final boolean descending) { 47 | desc = descending; 48 | } 49 | 50 | /* 51 | * (non-Javadoc) 52 | * 53 | * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) 54 | */ 55 | @Override 56 | public int compare(final MemberMediaItem member1, 57 | final MemberMediaItem member2) { 58 | return compare(member1.getOridnal(), member2.getOridnal()); 59 | } 60 | 61 | /** 62 | * Actual String comparator that compares two ordinals like "168.199" and 63 | * "72.26.0.1.1.1.1.1". 64 | * 65 | * @param str1 66 | * the first ordinal to be compared 67 | * @param str2 68 | * the second ordinal to be compared 69 | * @return < 0 when the first argument is less than the second. Return 0 70 | * when the first argument equals the second. Return > 0 when the 71 | * first argument is greater than the second. Or the opposite, if 72 | * #get 73 | */ 74 | public int compare(final String str1, final String str2) { 75 | // Return < 0 when the first argument is less than the second. 76 | // Return 0 when the first argument equals the second. 77 | // Return > 0 when the first argument is greater than the second. 78 | 79 | String substr1 = getSubstringToDot(str1); 80 | String substr2 = getSubstringToDot(str2); 81 | 82 | int compare = Integer.parseInt(substr1) - Integer.parseInt(substr2); 83 | 84 | // Equal and more dots -> Continue comparing 85 | if (compare == 0) { 86 | if ((substr1.length() != str1.length()) 87 | && (substr2.length() != str2.length())) { 88 | // Keep comparing 89 | return compare(StringUtils.substringAfter(str1, substr1), 90 | StringUtils.substringAfter(str2, substr2)); 91 | } else if (str1.length() != str2.length()) { 92 | // The shorter string is less 93 | compare = str1.length() - str2.length(); 94 | } 95 | } 96 | // Else: They really are equal: No more dots same value and same length 97 | 98 | if (desc) { 99 | compare = -compare; 100 | } 101 | return compare; 102 | } 103 | 104 | /** 105 | * Returns all characters to the first dot ('.')that is found. 106 | * 107 | * @param str 108 | * string to look for dots 109 | * @return a substring containing all chars until (but excluding) the first 110 | * dot. 111 | */ 112 | public String getSubstringToDot(final String str) { 113 | int dot = str.indexOf(SEPARATOR); 114 | if (dot > 0) { 115 | return StringUtils.substringBefore(str, dot); 116 | } 117 | return str; 118 | } 119 | 120 | /** 121 | * @return true, the list is sorted descendingly, otherwise 122 | * ascendingly. 123 | */ 124 | protected boolean isDescending() { 125 | return desc; 126 | } 127 | 128 | } -------------------------------------------------------------------------------- /src/test/java/info/schnatterer/songbirddbapi4j/domain/util/MemberMediaItemComparatorTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Johannes Schnatterer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Copyright (C) 2013 Johannes Schnatterer 18 | * See the NOTICE file distributed with this work for additional 19 | * information regarding copyright ownership. 20 | * 21 | * Licensed under the Apache License, Version 2.0 (the "License"); 22 | * you may not use this file except in compliance with the License. 23 | * You may obtain a copy of the License at 24 | * 25 | * http://www.apache.org/licenses/LICENSE-2.0 26 | * 27 | * Unless required by applicable law or agreed to in writing, software 28 | * distributed under the License is distributed on an "AS IS" BASIS, 29 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 30 | * See the License for the specific language governing permissions and 31 | * limitations under the License. 32 | */ 33 | 34 | package info.schnatterer.songbirddbapi4j.domain.util; 35 | 36 | import static org.junit.Assert.*; 37 | import info.schnatterer.songbirddbapi4j.domain.MemberMediaItem; 38 | import info.schnatterer.songbirddbapi4j.domain.util.MemberMediaItemComparator; 39 | 40 | import java.util.Arrays; 41 | import java.util.Collections; 42 | import java.util.LinkedList; 43 | import java.util.List; 44 | 45 | import org.junit.Test; 46 | 47 | public class MemberMediaItemComparatorTest { 48 | 49 | public static final String TEST_0 = "72.31.0.20.2.0.-1.-1.1.-1.0"; 50 | public static final String TEST_1 = "72.31.0.20.2.0.-1.-1.1"; 51 | public static final String TEST_2 = "72.31.0.20.2.0.-1.-1.0"; 52 | public static final String TEST_3 = "72.31.0.20.2.0.1.1.8"; 53 | public static final String TEST_4 = "72.31.0.20.2.0.1.1.7"; 54 | public static final String TEST_5 = "100"; 55 | public static final String TEST_6 = "71.0"; 56 | public static final String TEST_7 = "168.813.45.0.238.0.2.13.466.140.14.1.52.298.1.277.117.0.40.0.68.13.115.40.0.1.1.56.9"; 57 | public static final String TEST_8 = "168.813.45.0.238.0.2.13.466.140.14.1.52.298.1.277.117.0.40.0.68.13.115.40.0.1.1.56.8"; 58 | 59 | @Test 60 | public void testSortingNoDots() { 61 | boolean descending = false; 62 | MemberMediaItemComparator comp = new MemberMediaItemComparator( 63 | descending); 64 | assertTrue("First arg must be greater than second", 65 | comp.compare(TEST_5, TEST_6) > 0); 66 | } 67 | 68 | @Test 69 | public void testSortingNoDots2() { 70 | MemberMediaItemComparator comp = new MemberMediaItemComparator(false); 71 | assertTrue("First arg must be less than second", 72 | comp.compare(TEST_5, TEST_8) < 0); 73 | } 74 | 75 | @Test 76 | public void testDifferentLength() { 77 | MemberMediaItemComparator comp = new MemberMediaItemComparator(false); 78 | // 1 shorter than 0 79 | assertTrue("First arg must be less than second", 80 | comp.compare(TEST_1, TEST_0) < 0); 81 | } 82 | 83 | @Test 84 | public void testNegative() { 85 | MemberMediaItemComparator comp = new MemberMediaItemComparator(false); 86 | // 1 shorter than 0 87 | assertTrue("First arg must be less than second", 88 | comp.compare(TEST_2, TEST_3) < 0); 89 | } 90 | 91 | @Test 92 | public void testSorting() { 93 | List list = new LinkedList(); 94 | addMember(list, TEST_0); // 0 95 | addMember(list, TEST_1); // 1 96 | addMember(list, TEST_2); // 2 97 | addMember(list, TEST_3); // 3 98 | addMember(list, TEST_4); // 4 99 | addMember(list, TEST_5); // 5 100 | addMember(list, TEST_6); // 6 101 | addMember(list, TEST_7); // 7 102 | addMember(list, TEST_8); // 8 103 | 104 | /* Compare: Expected order: 7, 4, 3, 2, 1, 0, 6, 9, 8 */ 105 | Collections.sort(list, new MemberMediaItemComparator(false)); 106 | List actual = new LinkedList(); 107 | for (MemberMediaItem memberMediaItem : list) { 108 | actual.add(memberMediaItem.getOridnal()); 109 | } 110 | 111 | List expected = Arrays.asList(TEST_6, TEST_2, TEST_1, TEST_0, 112 | TEST_4, TEST_3, TEST_5, TEST_8, TEST_7); 113 | assertEquals(expected, actual); 114 | 115 | } 116 | 117 | private void addMember(List list, String ordinal) { 118 | MemberMediaItem member = new MemberMediaItem(); 119 | member.setOridnal(ordinal); 120 | // MediaItem mediaItem = new MediaItem(); 121 | // mediaItem.setId(list.size()); 122 | // member.setMember(mediaItem); 123 | list.add(member); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/info/schnatterer/songbirddbapi4j/domain/MediaItem.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Johannes Schnatterer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.schnatterer.songbirddbapi4j.domain; 17 | 18 | import java.util.Date; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | /** 23 | * A generic media item containing {@link Property}s. 24 | * 25 | * This is the underlying data structure for either playlist (modeled by 26 | * {@link SimpleMediaList}) or a members (modeled by {@link MemberMediaItem}). 27 | * 28 | * @author schnatterer 29 | * 30 | */ 31 | public class MediaItem { 32 | 33 | /** ID of this MediaItem within the Songbird database. */ 34 | private Integer id; 35 | 36 | /** Content URL of this MediaItems. This can be the URL to an mp3 file. */ 37 | private String contentUrl; 38 | /** 39 | * All properties of the MediaItem. See {@link Property} for available 40 | * properties. 41 | */ 42 | private Map properties = new HashMap(); 43 | /** The list type of this MediaItem. See {@link MediaListTypes}. */ 44 | private int listType; 45 | 46 | /** Date when the MediaItem has been added to songbird. */ 47 | private Date dateCreated; 48 | /** Date when the MediaItem has been last updated within songbird. */ 49 | private Date dateUpdated; 50 | 51 | /** 52 | * @return the id 53 | */ 54 | public Integer getId() { 55 | return id; 56 | } 57 | 58 | /** 59 | * @return the contentUrl 60 | */ 61 | public String getContentUrl() { 62 | return contentUrl; 63 | } 64 | 65 | /** 66 | * @param property 67 | * the property to be retrieved. See {@link Property} for 68 | * available properties. 69 | * @return the property, of null if no such property. 70 | */ 71 | public String getProperty(final String property) { 72 | if (property == null) { 73 | return null; 74 | } 75 | return properties.get(Property.property2Id(property)); 76 | } 77 | 78 | /** 79 | * @param property 80 | * the property to be retrieved. See {@link Property} for 81 | * available properties. 82 | * @return the property, of null if no such property. 83 | */ 84 | public Date getPropertyAsDate(final String property) { 85 | String propertyString = getProperty(property); 86 | if (propertyString == null) { 87 | return null; 88 | } 89 | 90 | return new Date(Long.valueOf(propertyString)); 91 | } 92 | 93 | /** 94 | * @param property 95 | * the property to be retrieved. See {@link Property} for 96 | * available properties. 97 | * @return the property, of null if no such property. 98 | */ 99 | public Long getPropertyAsLong(final String property) { 100 | String propertyString = getProperty(property); 101 | if (propertyString == null) { 102 | return null; 103 | } 104 | 105 | return Long.valueOf(propertyString); 106 | } 107 | 108 | /** 109 | * @param newId 110 | * the id to set 111 | */ 112 | public void setId(final Integer newId) { 113 | this.id = newId; 114 | } 115 | 116 | /** 117 | * @param newContentUrl 118 | * the contentUrl to set 119 | */ 120 | public void setContentUrl(final String newContentUrl) { 121 | this.contentUrl = newContentUrl; 122 | } 123 | 124 | /** 125 | * @param newProperties 126 | * the properties to set 127 | */ 128 | public void setProperties(final Map newProperties) { 129 | this.properties = newProperties; 130 | } 131 | 132 | /** 133 | * @return the listType 134 | */ 135 | public String getListType() { 136 | return MediaListTypes.id2ListType(listType); 137 | } 138 | 139 | /** 140 | * @param newListType 141 | * the listType to set 142 | */ 143 | public void setListType(final String newListType) { 144 | this.listType = MediaListTypes.listType2Id(newListType); 145 | } 146 | 147 | /** 148 | * @param newListType 149 | * the listType to set 150 | */ 151 | public void setListType(final int newListType) { 152 | this.listType = newListType; 153 | } 154 | 155 | /** 156 | * @return the properties 157 | */ 158 | public Map getProperties() { 159 | return properties; 160 | } 161 | 162 | /** 163 | * @return the dateCreated 164 | */ 165 | public Date getDateCreated() { 166 | return dateCreated; 167 | } 168 | 169 | /** 170 | * @return the dateUpdated 171 | */ 172 | public Date getDateUpdated() { 173 | return dateUpdated; 174 | } 175 | 176 | /** 177 | * @param dateCreated 178 | * the dateCreated to set 179 | */ 180 | public void setDateCreated(Date dateCreated) { 181 | this.dateCreated = dateCreated; 182 | } 183 | 184 | /** 185 | * @param dateUpdated 186 | * the dateUpdated to set 187 | */ 188 | public void setDateUpdated(Date dateUpdated) { 189 | this.dateUpdated = dateUpdated; 190 | } 191 | 192 | @Override 193 | public String toString() { 194 | return "MediaItem [id=" + id + ", contentUrl=" + contentUrl 195 | + ", properties=" + properties + ", listType=" + listType 196 | + ", dateCreated=" + dateCreated + ", dateUpdated=" 197 | + dateUpdated + "]"; 198 | } 199 | 200 | } 201 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 18 | 19 | 4.0.0 20 | 21 | info.schnatterer 22 | songbirdDbApi4j 23 | 2.1-SNAPSHOT 24 | jar 25 | 26 | songbirdDbApi4j 27 | https://github.com/schnatterer/songbirdDbApi4j 28 | 29 | 30 | 31 | Apache License, Version 2.0 32 | http://www.apache.org/licenses/LICENSE-2.0.txt 33 | 34 | 35 | 36 | 37 | scm:git:ssh://github.com/schnatterer/songbirdDbApi4j.git 38 | scm:git:ssh://git@github.com/schnatterer/songbirdDbApi4j.git 39 | https://github.com/schnatterer/songbirdDbApi4j 40 | HEAD 41 | 42 | 43 | 44 | UTF-8 45 | 2.11 46 | 47 | 49 | github 50 | 51 | 52 | 53 | 54 | 55 | org.apache.maven.plugins 56 | maven-compiler-plugin 57 | 3.3 58 | 59 | 1.6 60 | 1.6 61 | 62 | 63 | 64 | org.apache.maven.plugins 65 | maven-release-plugin 66 | 2.5.1 67 | 68 | -Prelease 69 | 70 | v.@{project.version} 71 | 72 | 73 | 74 | maven-deploy-plugin 75 | 2.8.1 76 | 77 | internal.repo::default::file://${project.build.directory}/mvn-repo 78 | 79 | 80 | 93 | 94 | 96 | com.mycila 97 | license-maven-plugin 98 | ${license-maven-plugin.version} 99 | 100 |
src/main/config/license-templates/APACHE-2.txt
101 | 102 | 2015 103 | Johannes Schnatterer 104 | 105 | 106 | LICENSE 107 | 108 |
109 | 110 | 111 | 112 | com.mycila 113 | license-maven-plugin-git 114 | 115 | ${license-maven-plugin.version} 116 | 117 | 118 | 119 | 120 | 121 | validate 122 | 123 | check 124 | 125 | 126 | 127 |
128 | 129 | 130 | org.jacoco 131 | jacoco-maven-plugin 132 | 0.7.5.201505241946 133 | 134 | 135 | initialize 136 | 137 | prepare-agent 138 | 139 | 140 | 141 | 142 |
143 |
144 | 145 | 146 | 147 | org.xerial 148 | sqlite-jdbc 149 | 3.7.2 150 | 151 | 152 | org.slf4j 153 | slf4j-api 154 | 1.7.2 155 | 156 | 157 | junit 158 | junit 159 | 4.13.1 160 | test 161 | 162 | 163 |
164 | -------------------------------------------------------------------------------- /src/main/java/info/schnatterer/songbirddbapi4j/SongbirdDbConnection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Johannes Schnatterer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.schnatterer.songbirddbapi4j; 17 | 18 | import java.sql.Connection; 19 | import java.sql.PreparedStatement; 20 | import java.sql.ResultSet; 21 | import java.sql.SQLException; 22 | import java.sql.Statement; 23 | 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | import org.sqlite.SQLiteConfig; 27 | 28 | /** 29 | * Abstracts from the Songbird database database file. Make sure to call 30 | * {@link #close()} after using it. 31 | * 32 | * This class is not thread safe. Use a new instance in each thread. 33 | * 34 | * @author schnatterer 35 | * 36 | */ 37 | public final class SongbirdDbConnection { 38 | 39 | /** 40 | * @param pathToDb 41 | * the dbUrl to set 42 | */ 43 | public SongbirdDbConnection(final String pathToDb) { 44 | this.dbUrl = JDBC_PREFIX + pathToDb; 45 | } 46 | 47 | /** SLF4J-Logger. */ 48 | private final Logger logger = LoggerFactory 49 | .getLogger(SongbirdDbConnection.class); 50 | 51 | /** The jdbc prefix used for connecting. */ 52 | private static final String JDBC_PREFIX = "jdbc:sqlite:"; 53 | 54 | /** The string representation of the JDBC driver used for connecting. */ 55 | private static final String JDBC_DRIVER = "org.sqlite.JDBC"; 56 | 57 | /** Connection timeout during the execution of a statement. */ 58 | private static final int STATEMENT_TIMEOUT = 30; 59 | 60 | /** Actual database URL to connect to. */ 61 | private String dbUrl = null; 62 | 63 | /** Connection to the database. */ 64 | private Connection connection = null; 65 | 66 | /** Closes the database connection. */ 67 | public void close() { 68 | try { 69 | close(connection); 70 | } finally { 71 | connection = null; 72 | } 73 | } 74 | 75 | /** Closes the database connection. */ 76 | private void close(Connection actualConnection) { 77 | try { 78 | if (actualConnection != null) { 79 | logger.debug("Closing connection to " + dbUrl); 80 | actualConnection.close(); 81 | } 82 | } catch (SQLException e) { 83 | // connection close failed. 84 | logger.error(e.getMessage(), e); 85 | } 86 | } 87 | 88 | /** 89 | * Returns a connection to the songbird database. 90 | * 91 | * @return a connection ready for queries to the songbird database 92 | * @throws SQLException 93 | * in case of any problems (e.g. SQLite-related or missing 94 | * database URL) 95 | */ 96 | private Connection getConnection() throws SQLException { 97 | if (connection == null) { 98 | /* See: http://www.xerial.org/trac/Xerial/wiki/SQLiteJDBC */ 99 | 100 | if (dbUrl == null) { 101 | throw new SQLException("Missing URL to songbird db"); 102 | } 103 | 104 | // Load the sqlite-JDBC driver using the current class loader 105 | try { 106 | Class.forName(JDBC_DRIVER); 107 | // Class.forName("org.sqlite.Driver"); 108 | 109 | } catch (ClassNotFoundException e) { 110 | throw new SQLException("SQLlite driver not found on classpath", 111 | e); 112 | } 113 | SQLiteConfig config = new SQLiteConfig(); 114 | config.setReadOnly(true); 115 | config.setSharedCache(true); 116 | 117 | // create a database connection 118 | logger.info("Opening connection to " + dbUrl); 119 | connection = config.createConnection(dbUrl); 120 | // logger.debug(String.format("running in %s mode", 121 | // org.sqlite.SQLiteJDBCLoader.isNativeMode() ? "native" 122 | // : "pure-java")); 123 | 124 | // connection = DriverManager.getConnection(dbUrl); 125 | // Create custom songbird collations (see DatabaseEngine.cpp) 126 | // createCollations((JdbcConnection) connection); 127 | } 128 | return connection; 129 | } 130 | 131 | /** 132 | * Create a statement using the {@link #connection}. 133 | * 134 | * @return a new statement object. 135 | * @throws SQLException 136 | * if a database access error occurs or this method is called on 137 | * a closed connection 138 | */ 139 | private Statement getStatment() throws SQLException { 140 | Connection actualConnection = getConnection(); 141 | Statement statement; 142 | statement = actualConnection.createStatement(); 143 | statement.setQueryTimeout(STATEMENT_TIMEOUT); 144 | return statement; 145 | } 146 | 147 | /** 148 | * Executes an SQL query as a new Statement. 149 | * 150 | * @param query 151 | * an SQL statement to be sent to the database, typically a 152 | * static SQL SELECT statement 153 | * 154 | * @return a ResultSet object that contains the data produced by the given 155 | * query; never null 156 | * @throws SQLException 157 | * if a database access error occurs, this method is called on a 158 | * closed Statement, the given SQL statement produces anything 159 | * other than a single ResultSet object, the method is called on 160 | * a PreparedStatement or CallableStatement 161 | */ 162 | public ResultSet executeQuery(final String query) throws SQLException { 163 | // logger.debug("Query to SQLite: " + query); 164 | return getStatment().executeQuery(query); 165 | } 166 | 167 | /** 168 | * Creates a PreparedStatement object for sending parameterized SQL 169 | * statements to the database. 170 | * 171 | * @param query 172 | * an SQL statement that may contain one or more '?' IN parameter 173 | * placeholders 174 | * 175 | * @return new default PreparedStatement object containing the pre-compiled 176 | * SQL statement 177 | * 178 | * @throws SQLException 179 | * if a database access error occurs or this method is called on 180 | * a closed connection 181 | */ 182 | public PreparedStatement preparedStatement(final String query) 183 | throws SQLException { 184 | return getConnection().prepareStatement(query); 185 | } 186 | 187 | /** 188 | * @return the dbUrl 189 | */ 190 | public String getDbUrl() { 191 | return dbUrl; 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /src/main/java/info/schnatterer/songbirddbapi4j/domain/Property.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Johannes Schnatterer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.schnatterer.songbirddbapi4j.domain; 17 | 18 | import info.schnatterer.songbirddbapi4j.SongbirdDbConnection; 19 | 20 | import java.sql.ResultSet; 21 | import java.sql.SQLException; 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | 25 | /** 26 | * The property types of a {@link MemberMediaItem}. 27 | * 28 | * @author schnatterer 29 | * 30 | */ 31 | public final class Property { 32 | /** Don't instantiate utility classes! */ 33 | private Property() { 34 | } 35 | 36 | /** Gets all available properties. */ 37 | public static final String QUERY_PROPERTIES = "select * from properties"; 38 | 39 | /** Property constant for mediaListName. */ 40 | public static final String PROP_MEDIA_LIST_NAME = "http://songbirdnest.com/data/1.0#mediaListName"; 41 | /** Property constant for trackName. */ 42 | public static final String PROP_TRACK_NAME = "http://songbirdnest.com/data/1.0#trackName"; 43 | /** Property constant for albumName. */ 44 | public static final String PROP_ALBUM_NAME = "http://songbirdnest.com/data/1.0#albumName"; 45 | /** Property constant for artistName. */ 46 | public static final String PROP_ARTIST_NAME = "http://songbirdnest.com/data/1.0#artistName"; 47 | /** Property constant for rating. */ 48 | public static final String PROP_RATING = "http://songbirdnest.com/data/1.0#rating"; 49 | /** Property constant for customType. */ 50 | public static final String PROP_CUSTOM_TYPE = "http://songbirdnest.com/data/1.0#customType"; 51 | /** Property constant for lastPlayTime. */ 52 | public static final String PROP_LAST_PLAY_TIME = "http://songbirdnest.com/data/1.0#lastPlayTime"; 53 | /** Property constant for playCount. */ 54 | public static final String PROP_PLAY_COUNT = "http://songbirdnest.com/data/1.0#playCount"; 55 | /** Property constant for lastSkipTime. */ 56 | public static final String PROP_LAST_SKIP_TIME = "http://songbirdnest.com/data/1.0#lastSkipTime"; 57 | /** Property constant for skipCount. */ 58 | public static final String PROP_SKIP_COUNT = "http://songbirdnest.com/data/1.0#skipCount"; 59 | 60 | /** 61 | * TODO Insert remaining properties 62 | * 63 | *
 64 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#duration";
 65 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#genre";
 66 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#trackNumber";
 67 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#year";
 68 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#discNumber";
 69 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#totalDiscs";
 70 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#totalTracks";
 71 | 	 * 	;
 72 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#isSortable";
 73 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#ordinal";
 74 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#defaultColumnSpec";
 75 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#isSubscription";
 76 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#outerGUID";
 77 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#originURL";
 78 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#enableAutoDownload";
 79 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#comment";
 80 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#bitRate";
 81 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#sampleRate";
 82 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#softwareVendor";
 83 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#primaryImageURL";
 84 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#albumArtistName";
 85 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#composerName";
 86 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#recordLabelName";
 87 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#language";
 88 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#lyrics";
 89 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#bpm";
 90 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#copyright";
 91 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#originPage";
 92 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#subtitle";
 93 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#key";
 94 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#columnSpec";
 95 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#defaultMediaPageURL";
 96 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#storageGUID";
 97 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#isReadOnly";
 98 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#isContentReadOnly";
 99 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#smartMediaListState";
100 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#onlyCustomMediaPages";
101 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#isPartOfCompilation";
102 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#producerName";
103 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#conductorName";
104 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#lyricistName";
105 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#copyrightURL";
106 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#metadataUUID";
107 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#originLibraryGuid";
108 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#originItemGuid";
109 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#destination";
110 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#downloadStatusTarget";
111 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#artistDetailUrl";
112 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#albumDetailUrl";
113 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#originPageTitle";
114 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#downloadButton";
115 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#downloadDetails";
116 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#originPageImage";
117 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#artistDetailImage";
118 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#albumDetailImage";
119 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#rapiScopeURL";
120 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#rapiSiteID";
121 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#disableDownload"
122 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#excludeFromHistory"
123 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#transferPolicy"
124 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#availability"
125 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#deviceId"
126 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#playCount_AtLastSync"
127 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#skipCount_AtLastSync"
128 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/dummy/smartmedialists/1.0#playlist"
129 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#subscriptionURL"
130 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#subscriptionInterval"
131 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#subscriptionNextRun"
132 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#iTunesGUID"
133 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#artistOnTour"
134 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#artistOnTourUrl"
135 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#streamName"
136 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#favesSPSNodeCreated"
137 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#listenerCount"
138 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#streamFave"
139 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#streamId"
140 | 	 * 	public static final String PROP_ = "*"
141 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#hasLyrics"
142 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#channels"
143 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#downloadMediaListGUID"
144 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#createdFirstRunSmartPlaylists"
145 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#isDRMProtected"
146 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#cdRipStatus"
147 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#deviceLibraryGuid"
148 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#shouldRip"
149 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#dontWriteMetadata"
150 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#playlistURL"
151 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#attemptedRemoteArtFetch"
152 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#uiLimitType"
153 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#columnSpec+(audio)"
154 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#defaultColumnSpec+(audio)"
155 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#lastPlayPosition"
156 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#cdDiscHash"
157 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#keywords"
158 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#description"
159 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#showName"
160 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#episodeNumber"
161 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#seasonNumber"
162 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/device/1.0#capacity"
163 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/device/1.0#freeSpace"
164 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/device/1.0#totalUsedSpace"
165 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#columnSpec+(video)"
166 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#defaultColumnSpec+(video)"
167 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#libraryItemControllerLastSeenTypes"
168 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#trackType"
169 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#playQueueMediaListGUID"
170 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#artistNewRelease"
171 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#artistNewReleaseUrl"
172 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#hasLRCfile"
173 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#mlyricsScrollCorrArray"
174 | 	 * 	public static final String PROP_ = "Location"
175 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#translatedLyrics"
176 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#originIsInMainLibrary"
177 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#importType"
178 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/data/1.0#lastSyncTime"
179 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/device/1.0#musicItemCount"
180 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/device/1.0#musicUsedSpace"
181 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/device/1.0#musicTotalPlayTime"
182 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/device/1.0#videoItemCount"
183 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/device/1.0#videoUsedSpace"
184 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/device/1.0#videoTotalPlayTime"
185 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/device/1.0#imageItemCount"
186 | 	 * 	public static final String PROP_ = "http://songbirdnest.com/device/1.0#imageUsedSpace"
187 | 	 * 
188 | */ 189 | 190 | /** Mapping from numerical IDs to string constants (database enums). */ 191 | private static Map id2PropertyMap; 192 | /** Mapping from string constants (database enums) to numerical IDs. */ 193 | private static Map property2IdMap; 194 | 195 | /** 196 | * Initializes the mappings from string constants (database enums) to 197 | * numerical IDs and the other way round from the database. 198 | * 199 | * To avoid doing this again and again, use {@link #isInitialized()}. 200 | * 201 | * @param connection 202 | * database connection to use for querying 203 | * @throws SQLException 204 | * in case an error occurs during the db query. 205 | */ 206 | public static void populateResourceMap(SongbirdDbConnection connection) 207 | throws SQLException { 208 | property2IdMap = new HashMap(); 209 | id2PropertyMap = new HashMap(); 210 | ResultSet rs = connection.executeQuery(QUERY_PROPERTIES); 211 | while (rs.next()) { 212 | // read the result set 213 | Integer id = rs.getInt(1); 214 | String name = rs.getString(2); 215 | property2IdMap.put(name, id); 216 | id2PropertyMap.put(id, name); 217 | } 218 | } 219 | 220 | /** 221 | * @param id 222 | * a numerical ID to be mapped to the corresponding string 223 | * @return the string representation of a numerical ID. 224 | */ 225 | public static String id2Property(final int id) { 226 | return id2PropertyMap.get(id); 227 | } 228 | 229 | /** 230 | * @param property 231 | * a string representation to be mapped to the corresponding 232 | * numerical ID. 233 | * @return the numerical ID of a string representation. 234 | */ 235 | public static int property2Id(final String property) { 236 | return property2IdMap.get(property); 237 | } 238 | 239 | /** 240 | * Checks if maps have been initialized from database. If not better call 241 | * {@link #populateResourceMap(SongbirdDbConnection)}. 242 | * 243 | * @return true if initialized, otherwise false 244 | */ 245 | public static boolean isInitialized() { 246 | if (property2IdMap == null || property2IdMap.size() == 0 247 | || id2PropertyMap == null || id2PropertyMap.size() == 0) { 248 | return false; 249 | } else { 250 | return true; 251 | } 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /src/main/java/info/schnatterer/songbirddbapi4j/SongbirdDb.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Johannes Schnatterer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package info.schnatterer.songbirddbapi4j; 17 | 18 | import info.schnatterer.songbirddbapi4j.domain.MediaItem; 19 | import info.schnatterer.songbirddbapi4j.domain.MediaListTypes; 20 | import info.schnatterer.songbirddbapi4j.domain.MemberMediaItem; 21 | import info.schnatterer.songbirddbapi4j.domain.Property; 22 | import info.schnatterer.songbirddbapi4j.domain.SimpleMediaList; 23 | 24 | import java.sql.PreparedStatement; 25 | import java.sql.ResultSet; 26 | import java.sql.SQLException; 27 | import java.util.Date; 28 | import java.util.LinkedList; 29 | import java.util.List; 30 | import java.util.Map; 31 | 32 | /** 33 | * Provides queries to the songbird database related to playlists and its 34 | * members. 35 | * 36 | * @author schnatterer 37 | * 38 | */ 39 | public class SongbirdDb { 40 | // /** SLF4J-Logger. */ 41 | // private static Logger logger = 42 | // LoggerFactory.getLogger(PlaylistService.class); 43 | 44 | /** 45 | * Gets all media items that have is_list = 0. 46 | */ 47 | public static final String QUERY_MEDIA_ITEMS = "select m.media_item_id, m.content_url" 48 | + ", m.created, m.updated, r.property_id, r.obj from media_items m " 49 | + "left join resource_properties as r on m.media_item_id = r.media_item_id " 50 | + "where m.is_list = 0 " + "order by m.media_item_id "; 51 | 52 | /** 53 | * Gets all media items that have is_list = 1, INCLUDING emtpy ones. Also 54 | * finds dynamic playlist (subscriptions) (unfortunately also finds playlist 55 | * that have name null) 56 | */ 57 | public static final String QUERY_MEDIA_LISTS_TYPE_SIMPLE = "select m.media_item_id, m.content_url" 58 | + ", m.media_list_type_id, m.created, m.updated, r.property_id, r.obj from media_items m " 59 | + "left join resource_properties as r on m.media_item_id = r.media_item_id " 60 | + "left join media_list_types as mlt on m.media_list_type_id = mlt.media_list_type_id " 61 | + "where m.is_list = 1 " 62 | + "and m.media_list_type_id is not null " 63 | + "order by m.media_item_id "; 64 | 65 | /** 66 | * Gets all media items with a single playlist (realized as 67 | * {@link PreparedStatement}). The results contains several lines for one 68 | * member, that is it also contains the properties, in order to minimize the 69 | * queries sent to SQLite. 70 | * 71 | */ 72 | public static final String QUERY_MEDIA_LIST = "select l.member_media_item_id media_item_id, l.ordinal, m.content_url " 73 | + ", m.media_list_type_id, m.created, m.updated, r.property_id, r.obj from simple_media_lists l " 74 | + "left join media_items m ON m.media_item_id = l.member_media_item_id " 75 | + "left join resource_properties as r on m.media_item_id = r.media_item_id " 76 | // + "where l.media_item_id =? COLLATE NOCASE " + 77 | // "order by l.member_media_item_id "; 78 | + "where l.media_item_id =? COLLATE NOCASE " 79 | + "order by l.ordinal COLLATE NOCASE"; 80 | 81 | private final String pathToDb; 82 | 83 | // @SuppressWarnings("serial") 84 | // public static final Set PLAYLISTS_IGNORE = new HashSet() 85 | // { 86 | // { 87 | // add("&chrome://songbird/locale/songbird.properties#device.download"); 88 | // add("&smart.defaultlist.recentlyplayed"); 89 | // add("&device.sync.video-togo.playlist"); 90 | // } 91 | // }; 92 | 93 | // /** SLF4J-Logger. */ 94 | // private static Logger logger = LoggerFactory 95 | // .getLogger(PlaylistService.class); 96 | 97 | // private List playListItems = new LinkedList(); 98 | // private List playLists = new 99 | // LinkedList(); 100 | 101 | /** 102 | * Creates a wrapper for the songbird database at a specific location. 103 | * 104 | * @param pathToDb 105 | * the dbUrl to the songbird database file 106 | */ 107 | public SongbirdDb(String pathToDb) { 108 | this.pathToDb = pathToDb; 109 | 110 | // Make sure Property and MediaListType Maps are initialized 111 | synchronized (SongbirdDb.class) { 112 | if (!MediaListTypes.isInitialized() || !Property.isInitialized()) { 113 | SongbirdDbConnection connection = new SongbirdDbConnection( 114 | pathToDb); 115 | try { 116 | MediaListTypes.populatelistTypeMap(connection); 117 | Property.populateResourceMap(connection); 118 | } catch (SQLException e) { 119 | throw new RuntimeException(e); 120 | } finally { 121 | connection.close(); 122 | } 123 | } 124 | } 125 | } 126 | 127 | /** 128 | * Gets only the {@link MediaItem}s that are not playlists. 129 | * 130 | * @return a list of all tracks 131 | * 132 | * @throws SQLException 133 | * database-related exceptions 134 | */ 135 | public List getAllTracks() throws SQLException { 136 | List playListItems = new LinkedList(); 137 | 138 | SongbirdDbConnection connection = new SongbirdDbConnection(pathToDb); 139 | try { 140 | ResultSet rs = connection.executeQuery(QUERY_MEDIA_ITEMS); 141 | 142 | if (rs.next()) { // If there are results at all 143 | int currentId = rs.getInt("media_item_id"); 144 | while (currentId >= 0) { 145 | MediaItem m = new MediaItem(); 146 | currentId = readMediaItem(rs, currentId, m, false); 147 | playListItems.add(m); 148 | } 149 | } 150 | return playListItems; 151 | } finally { 152 | connection.close(); 153 | } 154 | } 155 | 156 | /** 157 | * Gets only the {@link MediaItem}s that are playlists. Does not get the 158 | * {@link MediaItem}s which are members of the playlist. Ignores all 159 | * playlists that don't have a name. 160 | * 161 | * @param ignoreInternalPlaylists 162 | * true ignores Songbird's internal playlists (all 163 | * playlists having an mediaListType != simple) 164 | * @param skipDynamicLists 165 | * true does not return songbird's "smart" playlists 166 | * (all playlists whose name starts with "&smart") 167 | * @return the playlist {@link MediaItem} 168 | * @throws SQLException 169 | * database-related exceptions 170 | * @see #getPlayLists(boolean, boolean) 171 | */ 172 | public List getPlaylistItems( 173 | final boolean ignoreInternalPlaylists, 174 | final boolean skipDynamicLists) throws SQLException { 175 | /** 176 | * Note: Get all playlists and attributes in on result set like this 177 | * 178 | *
179 | 		 * select m.media_item_id, m.content_url, lt.type, p.property_name, r.obj from media_items m 
180 | 		 * 	left join resource_properties as r on m.media_item_id = r.media_item_id 
181 | 		 * 	left join media_list_types as mlt on m.media_list_type_id = mlt.media_list_type_id
182 | 		 * 	left join properties p on r.property_id =  p.property_id
183 | 		 * 	left join MEDIA_LIST_TYPES lt on m.media_list_type_id = lt.media_list_type_id
184 | 		 * where m.is_list = 1 and m.media_list_type_id is not null order by m.media_item_id
185 | 		 * 
186 | */ 187 | List playListItems = new LinkedList(); 188 | 189 | SongbirdDbConnection connection = new SongbirdDbConnection(pathToDb); 190 | try { 191 | ResultSet rs = connection 192 | .executeQuery(QUERY_MEDIA_LISTS_TYPE_SIMPLE); 193 | 194 | if (rs.next()) { // If there are results at all 195 | int currentId = rs.getInt("media_item_id"); 196 | while (currentId >= 0) { 197 | MediaItem m = new MediaItem(); 198 | currentId = readMediaItem(rs, currentId, m, true); 199 | 200 | String mediaListName = m 201 | .getProperty(Property.PROP_MEDIA_LIST_NAME); 202 | 203 | // Skip all playlist without name 204 | if (mediaListName == null) { 205 | continue; 206 | } 207 | 208 | if (ignoreInternalPlaylists) { 209 | String mediaListType = m 210 | .getProperty(Property.PROP_CUSTOM_TYPE); 211 | // if (mediaListName != null && 212 | // mediaListName.startsWith("&")) { 213 | /* 214 | * Note: Dynamic and internal playlists start with '&'. 215 | * However, a dynamic playlist's customType is "simple", 216 | * as with it is for "normal" playlists. Internal 217 | * playlists have a type like "download" or "smart". So 218 | * in order to ignore internal playlists it would be 219 | * possible to filter for the custom type. 220 | * 221 | * In addition, most internal playlists don't have a 222 | * name. But not all! 223 | * 224 | * It also seems that only internal lists have a 225 | * mediaListType of "dynamic". But not all! 226 | * 227 | * So skip any playlists with customType != "simple" 228 | */ 229 | if (mediaListType != null 230 | && !mediaListType.equals("simple")) { 231 | // && mediaListName == null && 232 | // m.getListType().equals("dynamic")) { 233 | // Skip this one 234 | continue; 235 | } 236 | } 237 | 238 | if (skipDynamicLists) { 239 | /* Note: Dynamic lists begin with "&smart". */ 240 | if (mediaListName.startsWith("&smart")) { 241 | // Skip this one 242 | continue; 243 | } 244 | } 245 | 246 | // /* Discard playlist that don't have a name property */ 247 | // if (m.getProperty(Property.PROP_MEDIA_LIST_NAME) == null) 248 | // { 249 | // logger.warn("Found playlist with no name. Skipping list. " 250 | // + m); 251 | // } else if (skipDynamicLists && 252 | // m.getListType().equals("dynamic")) { 253 | // logger.info("Skipping dynamic list " + 254 | // m.getProperty(Property.PROP_MEDIA_LIST_NAME)); 255 | // } else { 256 | playListItems.add(m); 257 | // } 258 | 259 | // logger.debug("ID: " + m.getId() + ": \"" 260 | // + m.getProperty(Property.PROP_MEDIA_LIST_NAME) 261 | // + "\"; Type: " + m.getListType()); 262 | } 263 | } 264 | return playListItems; 265 | } finally { 266 | connection.close(); 267 | } 268 | } 269 | 270 | /** 271 | * Gets all {@link MediaItem}s that are playlists and also aggregates the 272 | * {@link MediaItem}s that are members of the playlists. Ignores all 273 | * playlists that don't have a name. 274 | * 275 | * @param ignoreInternalPlaylists 276 | * true ignores Songbird's internal playlists (all 277 | * playlists having an mediaListType != simple) 278 | * @param skipDynamicLists 279 | * true does not return songbird's "smart" playlists 280 | * (all playlists whose name starts with "&smart") 281 | * 282 | * @return an object that contains the "parent" (playlist) {@link MediaItem} 283 | * as well as all of its member {@link MediaItem}s 284 | * @throws SQLException 285 | * database-related exceptions 286 | */ 287 | public List getPlayLists( 288 | final boolean ignoreInternalPlaylists, 289 | final boolean skipDynamicLists) throws SQLException { 290 | 291 | List playListItems = getPlaylistItems( 292 | ignoreInternalPlaylists, skipDynamicLists); 293 | 294 | List playLists = new LinkedList(); 295 | SongbirdDbConnection connection = new SongbirdDbConnection(pathToDb); 296 | try { 297 | PreparedStatement queryMediaList = connection 298 | .preparedStatement(QUERY_MEDIA_LIST); 299 | for (MediaItem playlistMediaItem : playListItems) { 300 | 301 | // This is taken care of by getPlaylistItems() now 302 | // if (ignoreInternalPlaylists) { 303 | // String mediaListName = 304 | // playlistMediaItem.getProperty(Property.PROP_MEDIA_LIST_NAME); 305 | // String mediaListType = 306 | // playlistMediaItem.getProperty(Property.PROP_CUSTOM_TYPE); 307 | // /* 308 | // * Note: Dynamic and internal playlists start with '&'. 309 | // However, a dynamic playlist's customType is 310 | // * "simple", as with it is for "normal" playlists. Internal 311 | // playlists have a type like "download" or 312 | // * "smart". So in order to ignore internal playlists it would 313 | // be possible to filter for the custom 314 | // type. 315 | // * In addition, most internal playlists don't have a name. 316 | // * 317 | // * So skip any playlists without name and customType != 318 | // "simple". 319 | // */ 320 | // // if (mediaListName != null && 321 | // mediaListName.startsWith("&")) { 322 | // if (mediaListName == null && mediaListType != null && 323 | // !mediaListType.equals("simple")) { 324 | // // Skip this one 325 | // continue; 326 | // } 327 | // } 328 | 329 | /* Query members of playlist */ 330 | queryMediaList.setInt(1, playlistMediaItem.getId()); 331 | 332 | ResultSet rs = queryMediaList.executeQuery(); 333 | 334 | SimpleMediaList list = new SimpleMediaList(); 335 | list.setList(playlistMediaItem); 336 | // Map members = new TreeMap(); 338 | 339 | List members = list.getMembers(); 340 | 341 | if (rs.next()) { // If there are results at all 342 | int currentId = rs.getInt("media_item_id"); 343 | while (currentId >= 0) { 344 | 345 | MemberMediaItem memberWrapper = new MemberMediaItem(); 346 | // ordinal looks like "168.225.0.-1.0" 347 | memberWrapper.setOridnal(rs.getString("ordinal")); 348 | 349 | // read the result set 350 | MediaItem member = new MediaItem(); 351 | currentId = readMediaItem(rs, currentId, member, true); 352 | 353 | memberWrapper.setMember(member); 354 | 355 | // members.put(ordinal, m); 356 | members.add(memberWrapper); 357 | } 358 | } 359 | 360 | // list.setMembers(members); 361 | list.sortMembers(true); 362 | 363 | playLists.add(list); 364 | // } catch (IOException e) { 365 | // logger.error("unable to write to file", e); 366 | // } finally { 367 | // try { 368 | // out.close(); 369 | // } catch (IOException e) { 370 | // logger.error("unable to close file", e); 371 | // } 372 | // } 373 | // } catch (FileNotFoundException e) { 374 | // logger.error("unable to open file for writing", e); 375 | // } 376 | } 377 | return playLists; 378 | } finally { 379 | connection.close(); 380 | } 381 | } 382 | 383 | /** 384 | * Reads all attributes of a {@link MediaItem} from a {@link ResultSet}. 385 | * 386 | * @param rs 387 | * database to read from 388 | * @param currentId 389 | * @param mediaItem 390 | * item to attach the properties to 391 | * @return the next id returned by the cursor 392 | * @throws SQLException 393 | */ 394 | private int readMediaItem(ResultSet rs, int currentId, MediaItem mediaItem, 395 | boolean setListType) throws SQLException { 396 | mediaItem.setId(currentId); 397 | mediaItem.setDateCreated(new Date(rs.getLong("created"))); 398 | mediaItem.setDateUpdated(new Date(rs.getLong("updated"))); 399 | if (setListType) { 400 | mediaItem.setListType(rs.getInt("media_list_type_id")); 401 | } 402 | mediaItem.setContentUrl(rs.getString("content_url")); 403 | currentId = readProperties(mediaItem, rs, "media_item_id"); 404 | return currentId; 405 | } 406 | 407 | /** 408 | * Reads all properties for an id column (integer) and appends them to 409 | * mediaItem. Note: This will only work well if rs 410 | * is ordered by idColumn. 411 | * 412 | * @param mediaItem 413 | * item to attach the properties to 414 | * @param rs 415 | * database to read from 416 | * @param idColumn 417 | * name of the ID column (needed for comparing) 418 | * @return the next id or -1 if there is no more data in rs 419 | * @throws SQLException 420 | * database-related exceptions 421 | */ 422 | private int readProperties(final MediaItem mediaItem, final ResultSet rs, 423 | final String idColumn) throws SQLException { 424 | boolean moreData = false; 425 | int currentId = mediaItem.getId(); 426 | Map props = mediaItem.getProperties(); 427 | /* Set all props to this media item */ 428 | do { 429 | props.put(rs.getInt("property_id"), rs.getString("obj")); 430 | moreData = rs.next(); 431 | if (!moreData) { 432 | break; 433 | } 434 | currentId = rs.getInt(idColumn); 435 | } while (currentId == mediaItem.getId()); 436 | 437 | if (!moreData) { 438 | return -1; 439 | } 440 | return currentId; 441 | } 442 | 443 | } 444 | --------------------------------------------------------------------------------