diff --git a/conf/admin.sql b/conf/admin.sql index 322f09e..0e2a2a9 100644 --- a/conf/admin.sql +++ b/conf/admin.sql @@ -1 +1,107 @@ INSERT INTO user(id,username,fullname,access) VALUES("", "yunoadmin", "yunoadmin", "100"); + +UPDATE `user_preference` SET `value` = 'fr_FR' WHERE `preference` = 31 ; + +SET @lastid = LAST_INSERT_ID(); + +INSERT INTO `user_preference` (`user`, `preference`, `value`) VALUES +(@lastid, 1, '1'), +(@lastid, 4, '10'), +(@lastid, 19, '32'), +(@lastid, 22, 'Ampache :: For the love of Music'), +(@lastid, 23, '0'), +(@lastid, 24, '0'), +(@lastid, 25, '80'), +(@lastid, 41, 'mpd'), +(@lastid, 29, 'web_player'), +(@lastid, 31, 'fr_FR'), +(@lastid, 32, 'm3u'), +(@lastid, 33, 'reborn'), +(@lastid, 34, '27'), +(@lastid, 35, '27'), +(@lastid, 36, '27'), +(@lastid, 51, '50'), +(@lastid, 40, '100'), +(@lastid, 44, '1'), +(@lastid, 45, '1'), +(@lastid, 46, '1'), +(@lastid, 47, '7'), +(@lastid, 49, '1'), +(@lastid, 52, '8192'), +(@lastid, 53, 'default'), +(@lastid, 55, 'default'), +(@lastid, 57, ''), +(@lastid, 69, '0'), +(@lastid, 70, '0'), +(@lastid, 71, '0'), +(@lastid, 72, '0'), +(@lastid, 73, ''), +(@lastid, 74, ''), +(@lastid, 75, ''), +(@lastid, 76, ''), +(@lastid, 77, ''), +(@lastid, 78, ''), +(@lastid, 114, '1'), +(@lastid, 113, '0'), +(@lastid, 112, '-1'), +(@lastid, 111, '1'), +(@lastid, 110, '0'), +(@lastid, 109, '0'), +(@lastid, 108, '0'), +(@lastid, 107, '0'), +(@lastid, 106, '0'), +(@lastid, 105, '0'), +(@lastid, 104, '0'), +(@lastid, 103, '7'), +(@lastid, 102, '0'), +(@lastid, 101, '0'), +(@lastid, 100, '1'), +(@lastid, 99, '0'), +(@lastid, 95, '1'), +(@lastid, 94, '0'), +(@lastid, 93, '1'), +(@lastid, 92, '1'), +(@lastid, 91, '1'), +(@lastid, 90, '1'), +(@lastid, 89, '1'), +(@lastid, 88, '1'), +(@lastid, 87, '0'), +(@lastid, 86, '1'), +(@lastid, 85, '1'), +(@lastid, 84, '0'), +(@lastid, 83, '0'), +(@lastid, 79, '50'), +(@lastid, 80, '50'), +(@lastid, 82, '1'), +(@lastid, 81, '1'), +(@lastid, 115, '0'), +(@lastid, 116, ''), +(@lastid, 117, '1'), +(@lastid, 118, '0'), +(@lastid, 119, ''), +(@lastid, 120, '0'), +(@lastid, 121, '1'), +(@lastid, 122, '1'), +(@lastid, 123, '1'), +(@lastid, 124, '0'), +(@lastid, 125, '1'), +(@lastid, 126, '1'), +(@lastid, 127, '1'), +(@lastid, 128, '1'), +(@lastid, 129, ''), +(@lastid, 130, 'album,ep,live,single'), +(@lastid, 131, '1'), +(@lastid, 132, '10'), +(@lastid, 133, '0'), +(@lastid, 134, '1'), +(@lastid, 135, '1'), +(@lastid, 136, ''), +(@lastid, 137, ''), +(@lastid, 138, ''), +(@lastid, 139, '0'), +(@lastid, 140, '0'), +(@lastid, 96, ''), +(@lastid, 97, ''), +(@lastid, 98, ''); + + diff --git a/conf/ampache.cfg.php b/conf/ampache.cfg.php index ee30470..bc3450e 100644 --- a/conf/ampache.cfg.php +++ b/conf/ampache.cfg.php @@ -4,41 +4,47 @@ ;################### ; This value is used to detect quickly -; if this config file is up to date +; if this config file is up to date ; this is compared against a value hard-coded ; into the init script -config_version = 16 +config_version = 29 ;################### ; Path Vars # ;################### -; The http host of your server. +; The public http host of your server. ; If not set, retrieved automatically from client request. ; This setting is required for WebSocket server ; DEFAULT: "" http_host = "DOMAINTOCHANGE" -; The path to your ampache install -; Do not put a trailing / on this path +; The public path to your ampache install +; Do not put a trailing / on this path ; For example if your site is located at http://localhost ; than you do not need to enter anything for the web_path -; if it is located at http://localhost/music you need to +; if it is located at http://localhost/music you need to ; set web_path to /music ; DEFAULT: "" web_path = "PATHTOCHANGE" +; The local http url of your server. +; If not set, retrieved automatically from server information. +; DEFAULT: "" +;local_web_path = "http://localhost/ampache" + ;############################## ; Session and Login Variables # ;############################## ; Hostname of your database +; For socket authentication, set the path to socket file (e.g. /var/run/mysqld/mysqld.sock) ; DEFAULT: localhost database_hostname = "localhost" ; Port to use when connecting to your database ; DEFAULT: none -;database_port = 3306 +database_port = "" ; Name of your ampache database ; DEFAULT: ampache @@ -50,44 +56,48 @@ database_username = "yunouser" ; Password for your ampache database, this can not be blank ; this is a 'forced' security precaution, the default value -; will not work +; will not work (except if using socket authentication) ; DEFAULT: "" database_password = "yunopass" +; Cryptographic secret +; This MUST BE changed with your own secret key. Ampache-specific, just pick any random string you want. +secret_key = "abcdefghijklmnoprqstuvwyz0123456" + ; Length that a session will last expressed in seconds. Default is -; one hour. +; one hour. ; DEFAULT: 3600 -session_length = "3600" +session_length = 3600 ; Length that the session for a single streaming instance will last -; the default is two hours. With some clients, and long songs this can +; the default is two hours. With some clients, and long songs this can ; cause playback to stop, increase this value if you experience that ; DEFAULT: 7200 -stream_length = "7200" +stream_length = 7200 -; This length defines how long a 'remember me' session and cookie will -; last, the default is 7200, same as length. It is up to the administrator -; of the box to increase this, for reference 86400 = 1 day -; 604800 = 1 week and 2419200 = 1 month -; DEFAULT: 86400 -remember_length = "86400" +; This length defines how long a 'remember me' session and cookie will +; last, the default is 86400, same as length. It is up to the administrator +; of the box to increase this, for reference 86400 = 1 day, +; 604800 = 1 week, and 2419200 = 1 month +; DEFAULT: 604800 +remember_length = 604800 ; Name of the Session/Cookie that will sent to the browser ; default should be fine ; DEFAULT: ampache -session_name = "ampache" +session_name = ampache ; Lifetime of the Cookie, 0 == Forever (until browser close) , otherwise in terms of seconds -; If you want cookies to last past a browser close set this to a value in seconds. +; If you want cookies to last past a browser close set this to a value in seconds. ; DEFAULT: 0 -session_cookielife = "0" +session_cookielife = 0 ; Is the cookie a "secure" cookie? This should only be set to 1 (true) if you are -; running a secure site (HTTPS). +; running a secure site (HTTPS). ; DEFAULT: 0 -session_cookiesecure = "1" +session_cookiesecure = 0 -; Auth Methods +; Auth Methods ; This defines which auth methods Auth will attempt to use and in which order. ; If auto_create isn't enabled the user must exist locally. ; DEFAULT: mysql @@ -108,7 +118,7 @@ auth_methods = "http,mysql" ;auth_password_save = "false" ; Logout redirection target -; Defaults to our own login.php, but we can override it here if, for instance, +; Defaults to our own login.php, but we can override it here if, for instance, ; we want to redirect to an SSO provider instead. logout_redirect = "https://DOMAINTOCHANGE/yunohost/sso/?action=logout" @@ -120,15 +130,15 @@ logout_redirect = "https://DOMAINTOCHANGE/yunohost/sso/?action=logout" ; This defines which file types Ampache will attempt to catalog ; You can specify any file extension you want in here separating them ; with a | -; DEFAULT: mp3|mpc|m4p|m4a|mp4|aac|ogg|rm|wma|asf|flac|spx|ra|ape|shn|wv -catalog_file_pattern = "mp3|mpc|m4p|m4a|mp4|aac|ogg|rm|wma|asf|flac|spx|ra|ape|shn|wv" +; DEFAULT: mp3|mpc|m4p|m4a|aac|ogg|oga|wav|aif|aiff|rm|wma|asf|flac|opus|spx|ra|ape|shn|wv +catalog_file_pattern = "mp3|mpc|m4p|m4a|aac|ogg|oga|wav|aif|aiff|rm|wma|asf|flac|opus|spx|ra|ape|shn|wv" ; Video Pattern ; This defines which video file types Ampache will attempt to catalog ; You can specify any file extension you want in here seperating them with ; a | but ampache may not be able to parse them -; DEAFULT: avi|mpg|flv|m4v|webm -catalog_video_pattern = "avi|mpg|flv|m4v|webm" +; DEAFULT: avi|mpg|mpeg|flv|m4v|mp4|webm|mkv|wmv|ogv|mov|divx|m2ts +catalog_video_pattern = "avi|mpg|mpeg|flv|m4v|mp4|webm|mkv|wmv|ogv|mov|divx|m2ts" ; Playlist Pattern ; This defines which playlist types Ampache will attempt to catalog @@ -149,19 +159,19 @@ catalog_prefix_pattern = "The|An|A|Die|Das|Ein|Eine|Les|Le|La" ; DEFAULT: false ;catalog_disable = "false" -; Use Access List +; Use Access List ; Toggle this on if you want ampache to pay attention to the access list -; and only allow streaming/downloading/api-rpc from known hosts api-rpc +; and only allow streaming/downloading/api-rpc from known hosts api-rpc ; will not work without this on. -; NOTE: Default Behavior is DENY FROM ALL +; NOTE: Default Behavior is DENY FROM ALL ; DEFAULT: true -access_control = "true" +access_control = "true" ; Require Session ; If this is set to true ampache will make sure that the URL passed when ; attempting to retrieve a song contains a valid Session ID This prevents ; others from guessing URL's. This setting is ignored if you have use_auth -; disabled. +; disabled. ; DEFAULT: true require_session = "true" @@ -188,62 +198,63 @@ require_localnet_session = "true" ; Track User IPs ; If this is enabled Ampache will log the IP of every completed login -; it will store user,ip,time at one row per login. The results are +; it will store user,ip,time at one row per login. The results are ; displayed in Admin --> Users ; DEFAULT: false ;track_user_ip = "false" ; User IP Cardinality ; This defines how many days worth of IP history Ampache will track -; As it is one row per login on high volume sites you will want to -; clear it every now and then. +; As it is one row per login on high volume sites you will want to +; clear it every now and then. ; DEFAULT: 42 days ;user_ip_cardinality = "42" ; Allow Zip Download ; This setting allows/disallows using zlib to zip up an entire ; playlist/album for download. Even if this is turned on you will -; still need to enabled downloading for the specific user you +; still need to enabled downloading for the specific user you ; want to be able to use this function ; DEFAULT: false ;allow_zip_download = "false" -; File Zip Download -; This settings tells Ampache to attempt to save the zip file -; to the filesystem instead of creating it in memory, you must -; also set tmp_dir_path in order for this to work -; DEFAULT: false -;file_zip_download = "false" +Allow Zip Types +; This setting allows/disallows zip download of specific object types +; If empty, all supported object types can be zipped. +; Otherwise, only the given object list can be zipped. +; POSSIBLE VALUES: artist, album, playlist, search, tmp_playlist +; DEFAULT: none +;allow_zip_types = "album" ; File Zip Comment ; This is an optional configuration option that adds a comment ; to your zip files, this only applies if you've got allow_zip_downloads ; DEFAULT: Ampache - Zip Batch Download -;file_zip_comment = "Ampache - Zip Batch Download" +;file_zip_comment = "Ampache - Zip Batch Download" ; Waveform ; This settings tells Ampache to attempt to generate a waveform ; for each song. It requires transcode and encode_args_wav settings. ; You must also set tmp_dir_path in order for this to work ; DEFAULT: false -;waveform = "false" +;waveform = "false" ; Waveform color ; The waveform color. ; DEFAULT: #FF0000 -;waveform_color = "#FF0000" +;waveform_color = "#FF0000" ; Temporary Directory Path -; If File Zip Download or Waveform is enabled this must be set to tell +; If Waveform is enabled this must be set to tell ; Ampache which directory to save the temporary file to. Do not put a ; trailing slash or this will not work. ; DEFAULT: false ;tmp_dir_path = "false" ; This setting throttles a persons downloading to the specified -; bytes per second. This is not a 100% guaranteed function, and +; bytes per second. This is not a 100% guaranteed function, and ; you should really use a server based rate limiter if you want -; to do this correctly. +; to do this correctly. ; DEFAULT: off ; VALUES: any whole number (in bytes per second) ;throttle_download = 10 @@ -261,13 +272,52 @@ getid3_tag_order = "id3v2,id3v1,vorbiscomment,quicktime,matroska,ape,asf,avi,mpe ; DEFAULT: false ;getid3_detect_id3v2_encoding = "false" +; This determines if file metadata should be write back to files +; as id3 metadata when updated. +; DEFAULT: false +;write_id3 = "false" + +; This determines if album art should be write back to files +; as id3 metadata when updated. +; DEFAULT: false +;write_id3_art = "false" + +; This determines if catalog manager users can delete medias from disk. +; DEFAULT: false +;delete_from_disk = "false" + ; This determines the order in which metadata sources are used (and in the ; case of plugins, checked) ; POSSIBLE VALUES (builtins): filename and getID3 -; POSSIBLE VALUES (plugins): MusicBrainz, plus any others you've installed. +; POSSIBLE VALUES (plugins): MusicBrainz,TheAudioDb, plus any others you've installed. ; DEFAULT: getID3 filename metadata_order = "getID3,filename" +; This determines the order in which metadata sources are used (and in the +; case of plugins, checked) for video files +; POSSIBLE VALUES (builtins): filename and getID3 +; POSSIBLE VALUES (plugins): Tvdb,Tmdb,Omdb, plus any others you've installed. +; DEFAULT: filename getID3 +metadata_order_video = "filename,getID3" + +; This determines if extended metadata grabbed from external services should be deferred. +; If enabled, extended metadata is retrieved when browsing the library item. +; If disabled, extended metadata is retrieved at catalog update. +; Today, only Artist information (summary, place formed, ...) can be deferred. +; DEFAULT: true +deferred_ext_metadata = "true" + +; Some taggers use delimiters other than \0 for fields +; This list specifies possible delimiters additional to \0 +; This setting takes a regex pattern. +; DEFAULT: // / \ | , ; +additional_genre_delimiters = "[/]{2}|[/|\\\\|\|,|;]" + +; This determines if a preview image should be retrieved from video files +; It requires encode_get_image transcode settings. +; DEFAULT: false +;generate_video_preview = "true" + ; Un comment if don't want ampache to follow symlinks ; DEFAULT: false ;no_symlinks = "false" @@ -284,9 +334,9 @@ use_auth = "true" ; If use_auth is set to false then this option is used ; to determine the permission level of the 'default' users ; default is administrator. This setting only takes affect -; if use_auth if false +; if use_auth is false ; POSSIBLE VALUES: user, admin, manager, guest -; DEFAULT: admin +; DEFAULT: guest default_auth_level = "user" ; 5 Star Ratings @@ -295,8 +345,8 @@ default_auth_level = "user" ; DEFAULT: true ratings = "true" -; User flags -; This allows user flags for almost any object in ampache +; User flags/favorites +; This allows user flags for almost any object in ampache as favorite ; POSSIBLE VALUES: false true ; DEFAULT: true userflags = "true" @@ -310,14 +360,14 @@ directplay = "true" ; Sociable ; This turns on / off all of the "social" features of ampache ; default is on, but if you don't care and just want music -; turn this off to disable all social features. +; turn this off to disable all social features. ; DEFAULT: true sociable = "true" -; Notify -; This turns on / off all Ampache notifications -; DEFAULT: true -notify = "true" +; License +; This turns on / off all licensing features on Ampache +; DEFAULT: false +licensing = "false" ; This options will turn on/off Demo Mode ; If Demo mode is on you can not play songs or update your catalog @@ -347,7 +397,43 @@ memory_cache = "true" ; Especially useful if you have a front and a back image in a folder ; comment out if ampache should search for any jpg,gif or png ; DEFAULT: folder.jpg -;album_art_preferred_filename = "folder.jpg" +;album_art_preferred_filename = "folder.jpg" + +; Album Art Store on Disk +; This defines if arts should be stored on disk instead of database. +; DEFAULT: false +;album_art_store_disk = "false" + +; Local Metadata Directory +; This define a local metadata directory with write access where to store +; heavy data if enabled (album arts, ...) +; DEFAULT: none +;local_metadata_dir = "/metadata" + +; Maximal upload size +; Specify the maximal allowed upload size for images, in bytes. +; DEFAULT: 1048576 +;max_upload_size = 1048576 + +; Album Art Minimum Width +; Specify the minimum width for arts (in pixel). +; DEFAULT: none +;album_art_min_width = 100 + +; Album Art Maximum Width +; Specify the maximum width for arts (in pixel). +; DEFAULT: none +;album_art_max_width = 1024 + +; Album Art Minimum Height +; Specify the minimum height for arts (in pixel). +; DEFAULT: none +;album_art_min_height = 100 + +; Album Art Maximum Height +; Specify the maximum height for arts (in pixel). +; DEFAULT: none +;album_art_max_height = 1024 ; Resize Images * Requires PHP-GD * ; Set this to true if you want Ampache to resize the Album @@ -357,24 +443,23 @@ memory_cache = "true" ; DEFAULT: false ;resize_images = "false" +; Statistical Graphs * Requires PHP-GD * +; Set this to true if you want Ampache to generate statistical +; graphs on usages / users. +; DEFAULT: false +;statistical_graphs = "false" + ; Art Gather Order ; Simply arrange the following in the order you would like ; ampache to search. If you want to disable one of the search ; methods simply leave it out. DB should be left as the first ; method unless you want it to overwrite what's already in the ; database -; POSSIBLE VALUES: db tags folder amazon lastfm musicbrainz google +; POSSIBLE VALUES (builtins): db tags folder lastfm musicbrainz google +; POSSIBLE VALUES (plugins): Amazon,TheAudioDb,Tmdb,Omdb,Flickr ; DEFAULT: db,tags,folder,musicbrainz,lastfm,google art_order = "db,tags,folder,musicbrainz,lastfm,google" -; Amazon Developer Key -; These are needed in order to actually use the amazon album art -; Your public key is your 'Access Key ID' -; Your private key is your 'Secret Access Key' -; DEFAULT: false -;amazon_developer_public_key = "" -;amazon_developer_private_key = "" - ; Recommendations ; Set this to true to enable display of similar artists or albums ; while browsing. Requires Last.FM. @@ -389,14 +474,14 @@ art_order = "db,tags,folder,musicbrainz,lastfm,google" ; Last.FM API Key ; Set this to your Last.FM api key to actually use Last.FM for -; recommendations. -;lastfm_api_key = "" +; recommendations and metadata. +lastfm_api_key = "d5df942424c71b754e54ce1832505ae2" ; Wanted ; Set this to true to enable display missing albums and the ; possibility for users to mark it as wanted. ; DEFAULT: false -;wanted = "false" +wanted = "true" ; Wanted types ; Set the allowed types of wanted releases (album,compilation,single,ep,live,remix,promotion,official) @@ -412,59 +497,50 @@ wanted_types = "album,official" ; EchoNest provides several music services. Currently used for missing song 30 seconds preview. ;echonest_api_key = "" +; Labels +; Use labels to browse artists per label membership. +; DEFAULT: false +;label = "false" + ; Broadcasts ; Allow users to broadcast music. ; This feature requires advanced server configuration, please take a look on the wiki for more information. ; DEFAULT: false ;broadcast = "false" +; Channels +; Set this to true to enable channels and the +; possibility for users to create channels from playlists +; DEFAULT: true +channel = "true" + +; Live Streams +; Set this to true to enable live streams (radio) and the +; possibility for users to add new live streams. +; DEFAULT: true +live_stream = "true" + ; Web Socket address ; Declare the web socket server address ; DEFAULT: determined automatically ;websocket_address = "ws://localhost:8100" -; Amazon base urls -; An array of Amazon sites to search. -; NOTE: This will search each of these sites in turn so don't expect it -; to be lightning fast! -; It is strongly recommended that only one of these is selected at any -; one time -; POSSIBLE VALUES: -; http://webservices.amazon.com -; http://webservices.amazon.co.uk -; http://webservices.amazon.de -; http://webservices.amazon.co.jp -; http://webservices.amazon.fr -; http://webservices.amazon.ca -; Default: http://webservices.amazon.com -;amazon_base_urls = "http://webservices.amazon.com" - -; max_amazon_results_pages -; The maximum number of results pages to pull from EACH amazon site -; NOTE: The art search pages through the results returned by your search -; up to this number of pages. As with the base_urls above, this is going -; to take more time, the more pages you ask it to process. -; Of course a good search will return only a few matches anyway. -; It is strongly recommended that you do _not_ change this value -; DEFAULT: 1 page (10 items) -max_amazon_results_pages = "1" - ; Debug ; If this is enabled Ampache will write debugging information to the log file ; DEFAULT: false -debug = "true" +;debug = "false" ; Debug Level ; This should always be set in conjunction with the ; debug option, it defines how prolific you want the -; debugging in ampache to be. values are 1-5. +; debugging in ampache to be. values are 1-5. ; 1 == Errors only ; 2 == Error + Failures (login attempts etc.) ; 3 == ?? ; 4 == ?? (Profit!) ; 5 == Information (cataloging progress etc.) ; DEFAULT: 5 -debug_level = "5" +debug_level = 5 ; Path to Log File ; This defines where you want ampache to log events to @@ -486,22 +562,28 @@ log_filename = "%name.%Y%m%d.log" ; DEFAULT: UTF-8 site_charset = "UTF-8" -; Locale Charset -; In some cases this has to be different -; in order for XHTML and other things to work -; This is disabled by default, enabled only -; if needed. It's specifically needed for Russian -; so that is the default -; DEFAULT: cp1251 -;lc_charset = cp1251 +; Locale Charset +; Local charset (mainly for file operations) if different +; from site_charset. +; This is disabled by default, enable only if needed +; (for Windows please set lc_charset to ISO8859-1) +; DEFAULT: ISO8859-1 +;lc_charset = "ISO8859-1" ; Refresh Limit -; This defines the default refresh limit in seconds for +; This defines the default refresh limit in seconds for ; pages with dynamic content, such as now playing ; DEFAULT: 60 ; Possible Values: Int > 5 refresh_limit = "60" +; Footer Statistics +; This defines whether statistics (Queries, Cache Hits, Load Time) +; are shown in the page footer. +; DEFAULT: true +; Possible values: true, false +show_footer_statistics = "true" + ;######################################################### ; Custom actions (optional) # ;######################################################### @@ -528,10 +610,10 @@ refresh_limit = "60" ;######################################################### ; LDAP filter string to use (required) -; For OpenLDAP use "uid" +; For OpenLDAP use "uid" ; For Microsoft Active Directory (MAD) use "sAMAccountName" ; DEFAULT: null -; ldap_filter = "sAMAccountName" +;ldap_filter = "(sAMAccountName=%v)" ; LDAP objectclass (required) ; OpanLDAP objectclass = "*" @@ -561,7 +643,7 @@ ldap_url = "localhost" ; MAD ldap_name_field = "displayname" ; DEFAULT: null ;ldap_email_field = "mail" -ldap_name_field = "cn" +ldap_name_field = "cn" ;######################################################### ; OpenID login info (optional) # @@ -608,19 +690,41 @@ auto_create = "true" ; DEFAULT: false ;admin_enable_required = "false" -; This setting will allow all registrants/ldap/http users -; to be auto-approved as a user. By default, they will be +; This setting will allow all registrants/ldap/http users +; to be auto-approved as a user. By default, they will be ; added as a guest and must be promoted by the admin. ; POSSIBLE VALUES: guest, user, admin ; DEFAULT: guest auto_user = "user" ; This will display the user agreement when registering -; For agreement text, edit templates/user_agreement.php +; For agreement text, edit config/registration_agreement.php ; User will need to accept the agreement before they can register ; DEFAULT: false ;user_agreement = "false" +; This disable email confirmation when registering. +; DEFAULT: false +;user_no_email_confirm = "false" + +; This will display the cookie disclaimer (EU Cookie Law) +; DEFAULT: false +cookie_disclaimer = "false" + +; The fields that will be shown on Registration page +; If a user wants to register. +; Username and email fields are forced. +; POSSIBLE VALUES: fullname,website,state,city +; DEFAULT: "fullname,website" +registration_display_fields = "fullname,website" + +; The fields that will be mandatory +; This controls which fields are mandatory for registration. +; Username and email fields are forced mandatory. +; POSSIBLE VALUES: fullname,website,state,city +; DEFAULT: fullname +registration_mandatory_fields = "fullname" + ;######################################################## ; These options control the dynamic downsampling based # ; on current usage # @@ -642,7 +746,7 @@ min_bit_rate = 48 ;###################################################### ; These are commands used to transcode non-streaming -; formats to the target file type for streaming. +; formats to the target file type for streaming. ; This can be useful in re-encoding file types that don't stream ; very well, or if your player doesn't support some file types. ; @@ -658,23 +762,75 @@ min_bit_rate = 48 ; (e.g. if you store everything in FLAC, but don't want to ever stream that.) ; transcode_TYPE = {allowed|required|false} ; DEFAULT: false -;transcode_m4a = allowed +;;; Audio +transcode_m4a = required transcode_flac = required -;transcode_mpc = required +transcode_mpc = required +transcode_ogg = required +transcode_oga = required +transcode_wav = required +transcode_wma = required +transcode_aif = required +transcode_aiff = required +transcode_ape = required +transcode_shn = required transcode_mp3 = allowed +;;; Video +transcode_avi = allowed +transcode_mkv = allowed +transcode_mpg = allowed +transcode_mpeg = allowed +transcode_m4v = allowed +transcode_mp4 = allowed +transcode_mov = allowed +transcode_wmv = allowed +transcode_ogv = allowed +transcode_divx = allowed +transcode_m2ts = allowed +transcode_webm = allowed -; Default output format +; Default audio output format ; DEFAULT: none encode_target = mp3 +; Default video output format +; DEFAULT: none +encode_video_target = webm + ; Override the default output format on a per-type basis ; encode_target_TYPE = TYPE ; DEFAULT: none -; encode_target_flac = ogg +encode_target_flac = ogg + +; Override the default TYPE transcoding behavior on a per-player basis +; transcode_player_PLAYER_TYPE = TYPE +; Valid PLAYER is: webplayer, api +; DEFAULT: none +transcode_player_webplayer_m4a = required +;transcode_player_webplayer_flac = required +;transcode_player_webplayer_mpc = required +transcode_player_webplayer_avi = required +transcode_player_webplayer_mkv = required +transcode_player_webplayer_mpg = required +transcode_player_webplayer_mpeg = required +transcode_player_webplayer_m4v = required +transcode_player_webplayer_mp4 = required +transcode_player_webplayer_mov = required +transcode_player_webplayer_wmv = required +transcode_player_webplayer_ogv = required +transcode_player_webplayer_divx = required +transcode_player_webplayer_m2ts = required + +; Override the default output format on a per-player basis +; encode_player_PLAYER_target = TYPE +; Valid PLAYER is: webplayer, api +; DEFAULT: none +;encode_player_webplayer_target = mp3 +;encode_player_api_target = mp3 ; Allow clients to override transcode settings (output type, bitrate, codec ...) ; DEFAULT: true -transcode_player_customize = "1" +transcode_player_customize = "true" ; Command configuration. Substitutions will be made as follows: ; %FILE% => filename @@ -688,30 +844,46 @@ transcode_player_customize = "1" ; equivalent to the old default, but if you find that necessary you should be ; clever enough to figure out how on your own. ; DEFAULT: none -;transcode_cmd = "ffmpeg -i %FILE%" -transcode_cmd = "ffmpeg -i %FILE%" -;transcode_cmd = "/usr/bin/neatokeen %FILE%" +;transcode_cmd = "ffmpeg" +transcode_cmd = "avconv" +;transcode_cmd = "/usr/bin/neatokeen" + +; Transcode input file argument +transcode_input = "-i %FILE%" ; Specific transcode commands ; It shouldn't be necessary in most cases, but you can override the transcode -; command for specific source formats. It still needs to accept the +; command for specific source formats. It still needs to accept the ; encoding arguments, so the easiest approach is to use your normal command as ; a clearing-house. ; transcode_cmd_TYPE = TRANSCODE_CMD -;transcode_cmd_mid = "timidity -Or -o – %FILE% | ffmpeg -f s16le -i pipe:0" +;transcode_cmd_mid = "timidity -Or -o – %FILE% | ffmpeg -f s16le -i pipe:0" ; Encoding arguments ; For each output format, you should provide the necessary arguments for -; your transcode_cmd. +; your transcode_cmd. ; encode_args_TYPE = TRANSCODE_CMD_ARGS -;encode_args_mp3 = "-vn -b:a %SAMPLE%K -c:a libmp3lame -f mp3 pipe:1" -;encode_args_ogg = "-vn -b:a %SAMPLE%K -c:a libvorbis -f ogg pipe:1" -;encode_args_m4a = "-vn -b:a %SAMPLE%K -c:a libfdk_aac -f adts pipe:1" -;encode_args_wav = "-vn -b:a %SAMPLE%K -c:a pcm_s16le -f wav pipe:1" -encode_args_ogg = "-vn -b:a max\(%SAMPLE%K\,49K\) -acodec libvorbis -vcodec libtheora -f ogg pipe:1" -encode_args_mp3 = "-vn -b:a %SAMPLE%K -acodec libmp3lame -f mp3 pipe:1" -encode_args_ogv = "-vcodec libtheora -acodec libvorbis -ar 44100 -f ogv pipe:1" -encode_args_mp4 = "-profile:0 baseline -frag_duration 2 -ar 44100 -f mp4 pipe:1" +encode_args_mp3 = "-vn -b:a %SAMPLE%K -c:a libmp3lame -f mp3 pipe:1" +encode_args_ogg = "-vn -b:a %SAMPLE%K -c:a libvorbis -f ogg pipe:1" +encode_args_m4a = "-vn -b:a %SAMPLE%K -c:a libfdk_aac -f adts pipe:1" +encode_args_wav = "-vn -b:a %SAMPLE%K -c:a pcm_s16le -f wav pipe:1" +encode_args_flv = "-b:a %SAMPLE%K -ar 44100 -ac 2 -v 0 -f flv -c:v libx264 -preset superfast -threads 0 pipe:1" +encode_args_webm = "-q %QUALITY% -f webm -c:v libvpx -maxrate %MAXBITRATE%k -preset superfast -threads 0 pipe:1" +;encode_args_webm = "-q %QUALITY% -f webm -c:v libvpx -maxrate 800k -preset superfast -threads 0 pipe:1" +encode_args_ts = "-q %QUALITY% -s %RESOLUTION% -f mpegts -c:v libx264 -c:a libmp3lame -maxrate %MAXBITRATE%k -preset superfast -threads 0 pipe:1" + +; Encoding arguments to retrieve an image from a single frame +encode_get_image = "-ss %TIME% -f image2 -vframes 1 pipe:1" + +; Encoding argument to encrust subtitle +encode_srt = "-vf \"subtitles='%SRTFILE%'\"" + +; Encode segment frame argument +encode_ss_frame = "-ss %TIME%" + +; Encode segment duration argument +encode_ss_duration = "-t %DURATION%" + ;###################################################### ; these options allow you to configure your rss-feed @@ -719,7 +891,7 @@ encode_args_mp4 = "-profile:0 baseline -frag_duration 2 -ar 44100 -f mp4 pipe:1" ; song is the information in the feed. can be multiple items. ; use_rss = false (values true | false) ;DEFAULT: use_rss = false -;use_rss = false +;use_rss = "false" ;##################################################### ;############################# @@ -735,7 +907,7 @@ encode_args_mp4 = "-profile:0 baseline -frag_duration 2 -ar 44100 -f mp4 pipe:1" ; If Ampache is behind an https reverse proxy, force use HTTPS protocol. ;Default: false -force_ssl = true +force_ssl = "true" ;############################# ; Mail Settings # @@ -747,7 +919,7 @@ force_ssl = true ;mail_type = "php" ;Mail domain. -;DEFAULT: example.com +;DEFAULT: example.com ;mail_domain = "example.com" ;This will be combined with mail_domain and used as the source address for @@ -794,7 +966,7 @@ force_ssl = true ;Enable SMTP authentication ;DEFAULT: false -;mail_auth = true +;mail_auth = "true" ;SMTP Username ;your mail auth username. diff --git a/conf/ampache.cfg.php.old b/conf/ampache.cfg.php.old new file mode 100644 index 0000000..493b760 --- /dev/null +++ b/conf/ampache.cfg.php.old @@ -0,0 +1,977 @@ +;### +;################### +; General Config # +;################### + +; This value is used to detect quickly +; if this config file is up to date +; this is compared against a value hard-coded +; into the init script +config_version = 29 + +;################### +; Path Vars # +;################### + +; The public http host of your server. +; If not set, retrieved automatically from client request. +; This setting is required for WebSocket server +; DEFAULT: "" +http_host = "DOMAINTOCHANGE" + +; The public path to your ampache install +; Do not put a trailing / on this path +; For example if your site is located at http://localhost +; than you do not need to enter anything for the web_path +; if it is located at http://localhost/music you need to +; set web_path to /music +; DEFAULT: "" +web_path = "PATHTOCHANGE" + +; The local http url of your server. +; If not set, retrieved automatically from server information. +; DEFAULT: "" +;local_web_path = "http://localhost/ampache" + +;############################## +; Session and Login Variables # +;############################## + +; Hostname of your database +; For socket authentication, set the path to socket file (e.g. /var/run/mysqld/mysqld.sock) +; DEFAULT: localhost +database_hostname = "localhost" + +; Port to use when connecting to your database +; DEFAULT: none +;database_port = 3306 + +; Name of your ampache database +; DEFAULT: ampache +database_name = "yunobase" + +; Username for your ampache database +; DEFAULT: "" +database_username = "yunouser" + +; Password for your ampache database, this can not be blank +; this is a 'forced' security precaution, the default value +; will not work (except if using socket authentication) +; DEFAULT: "" +database_password = "yunopass" + +; Cryptographic secret +; This MUST BE changed with your own secret key. Ampache-specific, just pick any random string you want. +secret_key = "abcdefghijklmnoprqstuvwyz0123456" + +; Length that a session will last expressed in seconds. Default is +; one hour. +; DEFAULT: 3600 +session_length = "3600" + +; Length that the session for a single streaming instance will last +; the default is two hours. With some clients, and long songs this can +; cause playback to stop, increase this value if you experience that +; DEFAULT: 7200 +stream_length = "7200" + +; This length defines how long a 'remember me' session and cookie will +; last, the default is 86400, same as length. It is up to the administrator +; of the box to increase this, for reference 86400 = 1 day, +; 604800 = 1 week, and 2419200 = 1 month +; DEFAULT: 604800 +remember_length = "86400" + +; Name of the Session/Cookie that will sent to the browser +; default should be fine +; DEFAULT: ampache +session_name = "ampache" + +; Lifetime of the Cookie, 0 == Forever (until browser close) , otherwise in terms of seconds +; If you want cookies to last past a browser close set this to a value in seconds. +; DEFAULT: 0 +session_cookielife = "0" + +; Is the cookie a "secure" cookie? This should only be set to 1 (true) if you are +; running a secure site (HTTPS). +; DEFAULT: 0 +session_cookiesecure = "1" + +; Auth Methods +; This defines which auth methods Auth will attempt to use and in which order. +; If auto_create isn't enabled the user must exist locally. +; DEFAULT: mysql +; VALUES: mysql,ldap,http,pam,external,openid +auth_methods = "http,mysql" + +; External authentication +; This sets the helper used for external authentication. It should conform to +; the interface used by mod_authnz_external +; DEFAULT: none +;external_authenticator = "/usr/sbin/pwauth" + +; Automatic local password updating +; Determines whether successful authentication against an external source +; will result in an update to the password stored in the database. +; A locally stored password is needed for API access. +; DEFAULT: false +;auth_password_save = "false" + +; Logout redirection target +; Defaults to our own login.php, but we can override it here if, for instance, +; we want to redirect to an SSO provider instead. +logout_redirect = "https://DOMAINTOCHANGE/yunohost/sso/?action=logout" + +;##################### +; Program Settings # +;##################### + +; File Pattern +; This defines which file types Ampache will attempt to catalog +; You can specify any file extension you want in here separating them +; with a | +; DEFAULT: mp3|mpc|m4p|m4a|aac|ogg|oga|wav|aif|aiff|rm|wma|asf|flac|opus|spx|ra|ape|shn|wv +catalog_file_pattern = "mp3|mpc|m4p|m4a|mp4|aac|ogg|rm|wma|asf|flac|spx|ra|ape|shn|wv" + +; Video Pattern +; This defines which video file types Ampache will attempt to catalog +; You can specify any file extension you want in here seperating them with +; a | but ampache may not be able to parse them +; DEAFULT: avi|mpg|mpeg|flv|m4v|mp4|webm|mkv|wmv|ogv|mov|divx|m2ts +catalog_video_pattern = "avi|mpg|flv|m4v|webm" + +; Playlist Pattern +; This defines which playlist types Ampache will attempt to catalog +; You can specify any file extension you want in here seperating them with +; a | but ampache may not be able to parse them +; DEFAULT: m3u|pls|asx|xspf +catalog_playlist_pattern = "m3u|pls|asx|xspf" + +; Prefix Pattern +; This defines which prefix Ampache will ignore when importing tags from +; your music. You may add any prefix you want seperating them with a | +; DEFAULT: The|An|A|Die|Das|Ein|Eine|Les|Le|La +catalog_prefix_pattern = "The|An|A|Die|Das|Ein|Eine|Les|Le|La" + +; Catalog disable +; This defines if catalog can be disabled without removing database entries +; WARNING: this increase sensibly sql requests and slow down Ampache a lot +; DEFAULT: false +;catalog_disable = "false" + +; Use Access List +; Toggle this on if you want ampache to pay attention to the access list +; and only allow streaming/downloading/api-rpc from known hosts api-rpc +; will not work without this on. +; NOTE: Default Behavior is DENY FROM ALL +; DEFAULT: true +access_control = "true" + +; Require Session +; If this is set to true ampache will make sure that the URL passed when +; attempting to retrieve a song contains a valid Session ID This prevents +; others from guessing URL's. This setting is ignored if you have use_auth +; disabled. +; DEFAULT: true +require_session = "true" + +; Require LocalNet Session +; If this is set to true then ampache will require that a valid session +; is passed even on hosts defined in the Local Network ACL. This setting +; has no effect if access_control is not enabled +; DEFAULT: true +require_localnet_session = "true" + +; Multiple Logins +; Added by Vlet 07/25/07 +; When this setting is enabled a user may only be logged in from a single +; IP address at any one time, this is to prevent sharing of accounts +; DEFAULT: false +;prevent_multiple_logins = "false" + +; Downsample Remote +; If this is set to true and access control is on any users who are not +; coming from a defined 'network' ACL will be automatically downsampled +; regardless of their preferences. Requires access_control to be enabled +; DEFAULT: false +;downsample_remote = "false" + +; Track User IPs +; If this is enabled Ampache will log the IP of every completed login +; it will store user,ip,time at one row per login. The results are +; displayed in Admin --> Users +; DEFAULT: false +;track_user_ip = "false" + +; User IP Cardinality +; This defines how many days worth of IP history Ampache will track +; As it is one row per login on high volume sites you will want to +; clear it every now and then. +; DEFAULT: 42 days +;user_ip_cardinality = "42" + +; Allow Zip Download +; This setting allows/disallows using zlib to zip up an entire +; playlist/album for download. Even if this is turned on you will +; still need to enabled downloading for the specific user you +; want to be able to use this function +; DEFAULT: false +;allow_zip_download = "false" + +Allow Zip Types +; This setting allows/disallows zip download of specific object types +; If empty, all supported object types can be zipped. +; Otherwise, only the given object list can be zipped. +; POSSIBLE VALUES: artist, album, playlist, search, tmp_playlist +; DEFAULT: none +;allow_zip_types = "album" + +; File Zip Comment +; This is an optional configuration option that adds a comment +; to your zip files, this only applies if you've got allow_zip_downloads +; DEFAULT: Ampache - Zip Batch Download +;file_zip_comment = "Ampache - Zip Batch Download" + +; Waveform +; This settings tells Ampache to attempt to generate a waveform +; for each song. It requires transcode and encode_args_wav settings. +; You must also set tmp_dir_path in order for this to work +; DEFAULT: false +;waveform = "false" + +; Waveform color +; The waveform color. +; DEFAULT: #FF0000 +;waveform_color = "#FF0000" + +; Temporary Directory Path +; If Waveform is enabled this must be set to tell +; Ampache which directory to save the temporary file to. Do not put a +; trailing slash or this will not work. +; DEFAULT: false +;tmp_dir_path = "false" + +; This setting throttles a persons downloading to the specified +; bytes per second. This is not a 100% guaranteed function, and +; you should really use a server based rate limiter if you want +; to do this correctly. +; DEFAULT: off +; VALUES: any whole number (in bytes per second) +;throttle_download = 10 + +; This determines the tag order for all cataloged +; music. If none of the listed tags are found then +; ampache will randomly use whatever was found. +; POSSIBLE VALUES: ape asf avi id3v1 id3v2 lyrics3 matroska mpeg quicktime riff +; vorbiscomment +; DEFAULT: id3v2 id3v1 vorbiscomment quicktime matroska ape asf avi mpeg riff +getid3_tag_order = "id3v2,id3v1,vorbiscomment,quicktime,matroska,ape,asf,avi,mpeg,riff" + +; Determines whether we try to autodetect the encoding for id3v2 tags. +; May break valid tags. +; DEFAULT: false +;getid3_detect_id3v2_encoding = "false" + +; This determines if file metadata should be write back to files +; as id3 metadata when updated. +; DEFAULT: false +;write_id3 = "false" + +; This determines if album art should be write back to files +; as id3 metadata when updated. +; DEFAULT: false +;write_id3_art = "false" + +; This determines if catalog manager users can delete medias from disk. +; DEFAULT: false +;delete_from_disk = "false" + +; This determines the order in which metadata sources are used (and in the +; case of plugins, checked) +; POSSIBLE VALUES (builtins): filename and getID3 +; POSSIBLE VALUES (plugins): MusicBrainz,TheAudioDb, plus any others you've installed. +; DEFAULT: getID3 filename +metadata_order = "getID3,filename" + +; This determines the order in which metadata sources are used (and in the +; case of plugins, checked) for video files +; POSSIBLE VALUES (builtins): filename and getID3 +; POSSIBLE VALUES (plugins): Tvdb,Tmdb,Omdb, plus any others you've installed. +; DEFAULT: filename getID3 +metadata_order_video = "filename,getID3" + +; This determines if extended metadata grabbed from external services should be deferred. +; If enabled, extended metadata is retrieved when browsing the library item. +; If disabled, extended metadata is retrieved at catalog update. +; Today, only Artist information (summary, place formed, ...) can be deferred. +; DEFAULT: true +deferred_ext_metadata = "true" + +; Some taggers use delimiters other than \0 for fields +; This list specifies possible delimiters additional to \0 +; This setting takes a regex pattern. +; DEFAULT: // / \ | , ; +additional_genre_delimiters = "[/]{2}|[/|\\\\|\|,|;]" + +; This determines if a preview image should be retrieved from video files +; It requires encode_get_image transcode settings. +; DEFAULT: false +;generate_video_preview = "true" + +; Un comment if don't want ampache to follow symlinks +; DEFAULT: false +;no_symlinks = "false" + +; Use auth? +; If this is set to "Yes" ampache will require a valid +; Username and password. If this is set to false then ampache +; will not ask you for a username and password. false is only +; recommended for internal only instances +; DEFAULT true +use_auth = "true" + +; Default Auth Level +; If use_auth is set to false then this option is used +; to determine the permission level of the 'default' users +; default is administrator. This setting only takes affect +; if use_auth is false +; POSSIBLE VALUES: user, admin, manager, guest +; DEFAULT: guest +default_auth_level = "user" + +; 5 Star Ratings +; This allows ratings for almost any object in ampache +; POSSIBLE VALUES: false true +; DEFAULT: true +ratings = "true" + +; User flags/favorites +; This allows user flags for almost any object in ampache as favorite +; POSSIBLE VALUES: false true +; DEFAULT: true +userflags = "true" + +; Direct play +; This allows user to play directly a song or album +; POSSIBLE VALUES: false true +; DEFAULT: true +directplay = "true" + +; Sociable +; This turns on / off all of the "social" features of ampache +; default is on, but if you don't care and just want music +; turn this off to disable all social features. +; DEFAULT: true +sociable = "true" + +; License +; This turns on / off all licensing features on Ampache +; DEFAULT: false +;licensing = "false" + +; This options will turn on/off Demo Mode +; If Demo mode is on you can not play songs or update your catalog +; in other words.. leave this commented out +; DEFAULT: false +;demo_mode = "false" + +; Caching +; This turns the caching mechanisms on or off, due to a large number of +; problems with people with very large catalogs and low memory settings +; this is off by default as it does significantly increase the memory +; requirments on larger catalogs. If you have the memory this can create +; a 2-3x speed improvement. +; DEFAULT: false +memory_cache = "true" + +; Memory Limit +; This defines the "Min" memory limit for PHP if your php.ini +; has a lower value set Ampache will set it up to this. If you +; set it below 16MB getid3() will not work! +; DEFAULT: 32 +;memory_limit = 32 + +; Album Art Preferred Filename +; Specify a filename to look for if you always give the same filename +; i.e. "folder.jpg" Ampache currently only supports jpg/gif and png +; Especially useful if you have a front and a back image in a folder +; comment out if ampache should search for any jpg,gif or png +; DEFAULT: folder.jpg +;album_art_preferred_filename = "folder.jpg" + +; Album Art Store on Disk +; This defines if arts should be stored on disk instead of database. +; DEFAULT: false +;album_art_store_disk = "false" + +; Local Metadata Directory +; This define a local metadata directory with write access where to store +; heavy data if enabled (album arts, ...) +; DEFAULT: none +;local_metadata_dir = "/metadata" + +; Maximal upload size +; Specify the maximal allowed upload size for images, in bytes. +; DEFAULT: 1048576 +;max_upload_size = 1048576 + +; Album Art Minimum Width +; Specify the minimum width for arts (in pixel). +; DEFAULT: none +;album_art_min_width = 100 + +; Album Art Maximum Width +; Specify the maximum width for arts (in pixel). +; DEFAULT: none +;album_art_max_width = 1024 + +; Album Art Minimum Height +; Specify the minimum height for arts (in pixel). +; DEFAULT: none +;album_art_min_height = 100 + +; Album Art Maximum Height +; Specify the maximum height for arts (in pixel). +; DEFAULT: none +;album_art_max_height = 1024 + +; Resize Images * Requires PHP-GD * +; Set this to true if you want Ampache to resize the Album +; art on the fly, this increases load time and CPU usage +; and also requires the PHP-GD library. This is very useful +; If you have high-quality album art and a small upload cap +; DEFAULT: false +;resize_images = "false" + +; Statistical Graphs * Requires PHP-GD * +; Set this to true if you want Ampache to generate statistical +; graphs on usages / users. +; DEFAULT: false +;statistical_graphs = "false" + +; Art Gather Order +; Simply arrange the following in the order you would like +; ampache to search. If you want to disable one of the search +; methods simply leave it out. DB should be left as the first +; method unless you want it to overwrite what's already in the +; database +; POSSIBLE VALUES (builtins): db tags folder lastfm musicbrainz google +; POSSIBLE VALUES (plugins): Amazon,TheAudioDb,Tmdb,Omdb,Flickr +; DEFAULT: db,tags,folder,musicbrainz,lastfm,google +art_order = "db,tags,folder,musicbrainz,lastfm,google" + +; Recommendations +; Set this to true to enable display of similar artists or albums +; while browsing. Requires Last.FM. +; DEFAULT: false +;show_similar = "false" + +; Concerts +; Set this to true to enable display of artist concerts +; Requires Last.FM. +; DEFAULT: false +;show_concerts = "false" + +; Last.FM API Key +; Set this to your Last.FM api key to actually use Last.FM for +; recommendations and metadata. +lastfm_api_key = "d5df942424c71b754e54ce1832505ae2" + +; Wanted +; Set this to true to enable display missing albums and the +; possibility for users to mark it as wanted. +; DEFAULT: false +wanted = "true" + +; Wanted types +; Set the allowed types of wanted releases (album,compilation,single,ep,live,remix,promotion,official) +; DEFAULT: album,official +wanted_types = "album,official" + +; Wanted Auto Accept +; Mark wanted requests as accepted by default (no content manager agreement required) +; DEFAULT: false +;wanted_auto_accept = "false" + +; EchoNest API key +; EchoNest provides several music services. Currently used for missing song 30 seconds preview. +;echonest_api_key = "" + +; Labels +; Use labels to browse artists per label membership. +; DEFAULT: false +;label = "false" + +; Broadcasts +; Allow users to broadcast music. +; This feature requires advanced server configuration, please take a look on the wiki for more information. +; DEFAULT: false +;broadcast = "false" + +; Channels +; Set this to true to enable channels and the +; possibility for users to create channels from playlists +; DEFAULT: true +channel = "true" + +; Live Streams +; Set this to true to enable live streams (radio) and the +; possibility for users to add new live streams. +; DEFAULT: true +live_stream = "true" + +; Web Socket address +; Declare the web socket server address +; DEFAULT: determined automatically +;websocket_address = "ws://localhost:8100" + +; Debug +; If this is enabled Ampache will write debugging information to the log file +; DEFAULT: false +debug = "true" + +; Debug Level +; This should always be set in conjunction with the +; debug option, it defines how prolific you want the +; debugging in ampache to be. values are 1-5. +; 1 == Errors only +; 2 == Error + Failures (login attempts etc.) +; 3 == ?? +; 4 == ?? (Profit!) +; 5 == Information (cataloging progress etc.) +; DEFAULT: 5 +debug_level = "5" + +; Path to Log File +; This defines where you want ampache to log events to +; this will only happen if debug is turned on. Do not +; include trailing slash. You will need to make sure that +; the specified directory exists and your HTTP server has +; write access. +; DEFAULT: NULL +log_path = "/var/www/ampache/log" + +; Log filename pattern +; This defines where the log file name pattern. +; %name.%Y%m%d.log will create a different log file every day. +; DEFAULT: %name.%Y%m%d.log +log_filename = "%name.%Y%m%d.log" + +; Charset of generated HTML pages +; Default of UTF-8 should work for most people +; DEFAULT: UTF-8 +site_charset = "UTF-8" + +; Locale Charset +; Local charset (mainly for file operations) if different +; from site_charset. +; This is disabled by default, enable only if needed +; (for Windows please set lc_charset to ISO8859-1) +; DEFAULT: ISO8859-1 +;lc_charset = "ISO8859-1" + +; Refresh Limit +; This defines the default refresh limit in seconds for +; pages with dynamic content, such as now playing +; DEFAULT: 60 +; Possible Values: Int > 5 +refresh_limit = "60" + +; Footer Statistics +; This defines whether statistics (Queries, Cache Hits, Load Time) +; are shown in the page footer. +; DEFAULT: true +; Possible values: true, false +show_footer_statistics = "true" + +;######################################################### +; Custom actions (optional) # +;######################################################### + +; Your custom play action title +;custom_play_action_title_0 = "" +; Your custom play action icon name (stored as /images/icon_[your_image].png) +;custom_play_action_icon_0 = "" +; Your custom action script, where: +; - %f: the media file path +; - %c: the excepted codec target (mp3, ogg, ...) +; - %a: the artist name +; - %A: the album name +; - %t: the song title +;custom_play_action_run_0 = "" + +; Example for Karaoke playing +;custom_play_action_title_0 = "Karaoke" +;custom_play_action_icon_0 = "microphone" +;custom_play_action_run_0 = "sox \"%f\" -p oops | ffmpeg -i pipe:0 -f %c pipe:1" + +;######################################################### +; LDAP login info (optional) # +;######################################################### + +; LDAP filter string to use (required) +; For OpenLDAP use "uid" +; For Microsoft Active Directory (MAD) use "sAMAccountName" +; DEFAULT: null +;ldap_filter = "(sAMAccountName=%v)" + +; LDAP objectclass (required) +; OpanLDAP objectclass = "*" +; MAD objectclass = "organizationalPerson" +; DEFAULT null +ldap_objectclass = "posixAccount" + +; Initial credentials to bind with for searching (optional) +; DEFAULT: null +;ldap_username = "" +;ldap_password = "" + +; Require that the user is in a specific group (optional) +; DEFAULT: null +;ldap_require_group = "cn=yourgroup,ou=yourorg,dc=yoursubdomain,dc=yourdomain,dc=yourtld" + +; This is the search dn used to find users (required) +; DEFAULT: null +ldap_search_dn = "dc=yunohost,dc=org" + +; This is the address of your ldap server (required) +; DEFAULT: null +ldap_url = "localhost" + +; Attributes where additional user information is stored (optional) +; OpenLDAP ldap_name_field = "cn" +; MAD ldap_name_field = "displayname" +; DEFAULT: null +;ldap_email_field = "mail" +ldap_name_field = "cn" + +;######################################################### +; OpenID login info (optional) # +;######################################################### + +; Requires specific OpenID Provider Authentication Policy +; DEFAULT: null +; VALUES: PAPE_AUTH_MULTI_FACTOR_PHYSICAL,PAPE_AUTH_MULTI_FACTOR,PAPE_AUTH_PHISHING_RESISTANT +;openid_required_pape = "" + +;######################################################### +; Public Registration settings, defaults to disabled # +;######################################################### + +; This setting will silently create an ampache account +; for anyone who can login using ldap (or any other login +; extension). The default is to create new users as guests +; see auto_user config option if you would like to change this +; DEFAULT: false +auto_create = "true" + +; This setting turns on/off public registration. It is +; recommended you leave this off, as it will allow anyone to +; sign up for an account on your server. +; REMEMBER: don't forget to set the mail from address further down in the config. +; DEFAULT: false +;allow_public_registration = "false" + +; Require Captcha Text on Image confirmation +; Turning this on requires the user to correctly +; type in the letters in the image created by Captcha +; Default is off because its very hard to detect if it failed +; to draw, or they failed to enter it. +; DEFAULT: false +;captcha_public_reg = "false" + +; This setting turns on/off admin notification of registration. +; DEFAULT: false +;admin_notify_reg = "false" + +; This setting determines whether the user will be created as a disabled user. +; If this is on, an administrator will need to manually enable the account +; before it's usable. +; DEFAULT: false +;admin_enable_required = "false" + +; This setting will allow all registrants/ldap/http users +; to be auto-approved as a user. By default, they will be +; added as a guest and must be promoted by the admin. +; POSSIBLE VALUES: guest, user, admin +; DEFAULT: guest +auto_user = "user" + +; This will display the user agreement when registering +; For agreement text, edit config/registration_agreement.php +; User will need to accept the agreement before they can register +; DEFAULT: false +;user_agreement = "false" + +; This disable email confirmation when registering. +; DEFAULT: false +;user_no_email_confirm = "false" + +; This will display the cookie disclaimer (EU Cookie Law) +; DEFAULT: false +;cookie_disclaimer = "false" + +; The fields that will be shown on Registration page +; If a user wants to register. +; Username and email fields are forced. +; POSSIBLE VALUES: fullname,website,state,city +; DEFAULT: "fullname,website" +registration_display_fields = "fullname,website" + +; The fields that will be mandatory +; This controls which fields are mandatory for registration. +; Username and email fields are forced mandatory. +; POSSIBLE VALUES: fullname,website,state,city +; DEFAULT: fullname +registration_mandatory_fields = "fullname" + +;######################################################## +; These options control the dynamic downsampling based # +; on current usage # +; *Note* Transcoding must be enabled and working # +;######################################################## + +; Attempt to optimize bandwidth by dynamically lowering the bit rate of new +; streams. Since the bit rate is only adjusted at the beginning of a song, the +; actual cumulative bitrate for concurrent streams can be up to around +; double the configured value. It also only applies to streams that are +; transcoded. +; DEFAULT: none +max_bit_rate = "576" + +; New dynamically downsampled streams will be denied if they are forced below +; this value. +; DEFAULT: 8 +min_bit_rate = "48" + +;###################################################### +; These are commands used to transcode non-streaming +; formats to the target file type for streaming. +; This can be useful in re-encoding file types that don't stream +; very well, or if your player doesn't support some file types. +; +; 'Downsampling' will also use these commands. +; +; To state the bleeding obvious, any programs referenced in the transcode +; commands must be installed, in the web server's search path (or referenced +; by their full path), and executable by the web server. + +; Input type selection +; TYPE is the extension. 'allowed' certifies that transcoding works properly for +; this input format. 'required' further forbids the direct streaming of a format +; (e.g. if you store everything in FLAC, but don't want to ever stream that.) +; transcode_TYPE = {allowed|required|false} +; DEFAULT: false +;;; Audio +;transcode_m4a = allowed +transcode_flac = "required" +;transcode_mpc = required +;transcode_ogg = required +;transcode_oga = required +;transcode_wav = required +;transcode_wma = required +;transcode_aif = required +;transcode_aiff = required +;transcode_ape = required +;transcode_shn = required +transcode_mp3 = "allowed" +;;; Video +;transcode_avi = allowed +;transcode_mkv = allowed +;transcode_mpg = allowed +;transcode_mpeg = allowed +;transcode_m4v = allowed +;transcode_mp4 = allowed +;transcode_mov = allowed +;transcode_wmv = allowed +;transcode_ogv = allowed +;transcode_divx = allowed +;transcode_m2ts = allowed +;transcode_webm = allowed + +; Default audio output format +; DEFAULT: none +encode_target = "mp3" + +; Default video output format +; DEFAULT: none +;encode_video_target = webm + +; Override the default output format on a per-type basis +; encode_target_TYPE = TYPE +; DEFAULT: none +;encode_target_flac = ogg + +; Override the default TYPE transcoding behavior on a per-player basis +; transcode_player_PLAYER_TYPE = TYPE +; Valid PLAYER is: webplayer, api +; DEFAULT: none +;transcode_player_webplayer_m4a = required +;transcode_player_webplayer_flac = required +;transcode_player_webplayer_mpc = required + +; Override the default output format on a per-player basis +; encode_player_PLAYER_target = TYPE +; Valid PLAYER is: webplayer, api +; DEFAULT: none +;encode_player_webplayer_target = mp3 +;encode_player_api_target = mp3 + +; Allow clients to override transcode settings (output type, bitrate, codec ...) +; DEFAULT: true +transcode_player_customize = "1" + +; Command configuration. Substitutions will be made as follows: +; %FILE% => filename +; %SAMPLE% => target sample rate +; You can do fancy things like VBR, but consider whether the consequences are +; acceptable in your environment. + +; Master transcode command +; transcode_cmd should be a single command that supports multiple file types, +; such as ffmpeg or avconv. It's still possible to make a configuration that's +; equivalent to the old default, but if you find that necessary you should be +; clever enough to figure out how on your own. +; DEFAULT: none +transcode_cmd = "ffmpeg -i %FILE%" +;transcode_cmd = "avconv" +;transcode_cmd = "/usr/bin/neatokeen" + +; Transcode input file argument +transcode_input = "-i %FILE%" + +; Specific transcode commands +; It shouldn't be necessary in most cases, but you can override the transcode +; command for specific source formats. It still needs to accept the +; encoding arguments, so the easiest approach is to use your normal command as +; a clearing-house. +; transcode_cmd_TYPE = TRANSCODE_CMD +;transcode_cmd_mid = "timidity -Or -o – %FILE% | ffmpeg -f s16le -i pipe:0" + +; Encoding arguments +; For each output format, you should provide the necessary arguments for +; your transcode_cmd. +; encode_args_TYPE = TRANSCODE_CMD_ARGS +encode_args_mp3 = "-vn -b:a %SAMPLE%K -acodec libmp3lame -f mp3 pipe:1" +encode_args_ogg = "-vn -b:a max\(%SAMPLE%K\,49K\) -acodec libvorbis -vcodec libtheora -f ogg pipe:1" +encode_args_m4a = "-vn -b:a %SAMPLE%K -c:a libfdk_aac -f adts pipe:1" +encode_args_wav = "-vn -b:a %SAMPLE%K -c:a pcm_s16le -f wav pipe:1" +encode_args_flv = "-b:a %SAMPLE%K -ar 44100 -ac 2 -v 0 -f flv -c:v libx264 -preset superfast -threads 0 pipe:1" +encode_args_webm = "-q %QUALITY% -f webm -c:v libvpx -maxrate %MAXBITRATE%k -preset superfast -threads 0 pipe:1" +encode_args_ts = "-q %QUALITY% -s %RESOLUTION% -f mpegts -c:v libx264 -c:a libmp3lame -maxrate %MAXBITRATE%k -preset superfast -threads 0 pipe:1" + +; Encoding arguments to retrieve an image from a single frame +encode_get_image = "-ss %TIME% -f image2 -vframes 1 pipe:1" + +; Encoding argument to encrust subtitle +encode_srt = "-vf \"subtitles='%SRTFILE%'\"" + +; Encode segment frame argument +encode_ss_frame = "-ss %TIME%" + +; Encode segment duration argument +encode_ss_duration = "-t %DURATION%" + + +;###################################################### +; these options allow you to configure your rss-feed +; layout. rss exists of two parts, main and song main is the information about the feed +; song is the information in the feed. can be multiple items. +; use_rss = false (values true | false) +;DEFAULT: use_rss = false +;use_rss = "false" +;##################################################### + +;############################# +; Proxy Settings (optional) # +;############################# +; If Ampache is behind an http proxy, specifiy the hostname or IP address +; port, proxyusername, and proxypassword here. +;DEFAULT: not in use +;proxy_host = "192.168.0.1" +;proxy_port = "8080" +;proxy_user = "" +;proxy_pass = "" + +; If Ampache is behind an https reverse proxy, force use HTTPS protocol. +;Default: false +force_ssl = "1" + +;############################# +; Mail Settings # +;############################# + +;Method used to send mail +;POSSIBLE VALUES: smtp sendmail php +;DEFAULT: php +;mail_type = "php" + +;Mail domain. +;DEFAULT: example.com +;mail_domain = "example.com" + +;This will be combined with mail_domain and used as the source address for +;emails generated by Ampache. For example, setting this to 'me' will set the +;sender to 'me@example.com'. +;DEFAULT: info +;mail_user = "info" + +;A name to go with the email address. +;DEFAULT: Ampache +;mail_name = "Ampache" + +;How strictly email addresses should be checked. +;easy does a regex match, strict actually performs some SMTP transactions +;to see if we can send to this address. +;POSSIBLE VALUES: strict easy none +; DEFAULT: strict +;mail_check = "strict" + + +;############################ +; sendmail Settings # +;############################ + +;DEFAULT: /usr/sbin/sendmail +;sendmail_path = "/usr/sbin/sendmail" + +;############################# +; SMTP Settings # +;############################# + +;Mail server (hostname or IP address) +;DEFAULT: localhost +;mail_host = "localhost" + +; SMTP port +;DEFAULT: 25 +;mail_port = 25 + +;Secure SMTP +;POSSIBLE VALUES: ssl tls +;DEFAULT: none +;mail_secure_smtp = tls + +;Enable SMTP authentication +;DEFAULT: false +;mail_auth = "true" + +;SMTP Username +;your mail auth username. +;mail_auth_user = "" + +; SMTP Password +; your mail auth password. +;mail_auth_pass = "" + +;############################# +; Multibyte Settings # +;############################# +; See http://php.net/manual/mbstring.supported-encodings.php +; If you want ID3v1 encoding detection to work, you should uncomment this line +; so that the ordering is sane. +; DEFAULT: auto +;mb_detect_order = "ASCII,UTF-8,EUC-JP,ISO-2022-JP,SJIS,JIS" + +slideshow_time = 0 + diff --git a/conf/nginx.conf b/conf/nginx.conf index 21dbfbc..8417a81 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -14,6 +14,7 @@ location PATHTOCHANGE { include fastcgi_params; fastcgi_param REMOTE_USER $remote_user; fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param SCRIPT_FILENAME $request_filename; } rewrite ^PATHTOCHANGE/play/ssid/(\w+)/type/(\w+)/oid/([0-9]+)/uid/([0-9]+)/name/(.*)$ PATHTOCHANGE/play/index.php?ssid=$1&type=$2&oid=$3&uid=$4&name=$5 last; diff --git a/scripts/install b/scripts/install index 2701bc4..c78524f 100644 --- a/scripts/install +++ b/scripts/install @@ -5,6 +5,8 @@ domain=$1 path=$2 admin_ampache=$3 +debianversionname=$(lsb_release -a | grep Codename | awk -F' ' '{print $2}') + # Check domain/path availability sudo yunohost app checkurl $domain$path -a ampache if [[ ! $? -eq 0 ]]; then @@ -64,9 +66,13 @@ echo "127.0.0.1 $domain #yunoampache" | sudo tee -a /etc/hosts sleep 1 curl -kL -X POST http://$domain$path/update.php?action=update > /dev/null 2>&1 sleep 5 +[ "$debianversionname" == "wheezy" ] && \ +sudo sed -i 's/;transcode_cmd = "ffmpeg"/transcode_cmd = "ffmpeg"/g' /var/www/ampache/config/ampache.cfg.php && \ +sudo sed -i 's/^transcode_cmd = "avconv"/;transcode_cmd = "avconv"/g' /var/www/ampache/config/ampache.cfg.php sudo yunohost app setting ampache skipped_uris -d sudo yunohost app setting ampache skipped_uris -v "/rest" sudo yunohost app ssowatconf -sudo sed -i '/yunoampache/d' /etc/hosts +sudo sed '/yunoampache/d' /etc/hosts > /tmp/hosts.tmp +sudo cp /tmp/hosts.tmp /etc/hosts ; sudo rm -f /tmp/hosts.tmp mysql -u $db_user -p$db_pwd $db_user < /tmp/admin.sql sudo rm /tmp/admin.sql diff --git a/sources/.gitattributes b/sources/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/sources/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/sources/.gitignore b/sources/.gitignore new file mode 100644 index 0000000..39e94cc --- /dev/null +++ b/sources/.gitignore @@ -0,0 +1,12 @@ +config/ampache.cfg.php +config/ampache-doped.cfg.php +rest/.htaccess +play/.htaccess +*.phpproj +*.sln +*.v11.suo +*.suo +logs +/nbproject/private/ +.pc +/tmp \ No newline at end of file diff --git a/sources/.maintenance.example b/sources/.maintenance.example new file mode 100644 index 0000000..3cf4310 --- /dev/null +++ b/sources/.maintenance.example @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/sources/.php_cs b/sources/.php_cs new file mode 100644 index 0000000..8d80bfc --- /dev/null +++ b/sources/.php_cs @@ -0,0 +1,16 @@ +exclude('modules') + ->exclude('nbproject') + ->in(__DIR__) + ->in(__DIR__ . '/modules/localplay') + ->in(__DIR__ . '/modules/catalog') + ->in(__DIR__ . '/modules/ampacheapi') +; + +return Symfony\CS\Config\Config::create() + ->finder($finder) +; \ No newline at end of file diff --git a/sources/.scrutinizer.yml b/sources/.scrutinizer.yml new file mode 100644 index 0000000..5de5f40 --- /dev/null +++ b/sources/.scrutinizer.yml @@ -0,0 +1,59 @@ +filter: + excluded_paths: + - 'modules/*' + paths: { } + +tools: + php_mess_detector: + enabled: true + filter: + excluded_paths: + - 'modules/*' + - 'themes/*' + paths: { } + php_code_sniffer: + enabled: false + php_pdepend: + enabled: true + configuration_file: null + suffixes: + - php + excluded_dirs: { } + filter: + excluded_paths: + - 'modules/*' + paths: { } + php_analyzer: + enabled: true + extensions: + - php + dependency_paths: + - 'modules/*' + filter: + excluded_paths: + - 'modules/*' + - 'themes/*' + paths: { } + path_configs: { } + php_changetracking: + enabled: true + bug_patterns: + - '\bfix(?:es|ed)?\b' + feature_patterns: + - '\badd(?:s|ed)?\b' + - '\bimplement(?:s|ed)?\b' + filter: + excluded_paths: + - 'modules/*' + paths: { } + php_loc: + enabled: true + excluded_dirs: + - 'modules/*' + php_cpd: + enabled: true + excluded_dirs: { } + filter: + excluded_paths: + - 'modules/*' + paths: { } \ No newline at end of file diff --git a/sources/.tgitconfig b/sources/.tgitconfig new file mode 100644 index 0000000..8a07fe8 --- /dev/null +++ b/sources/.tgitconfig @@ -0,0 +1,6 @@ +[bugtraq] + url = "https://github.com/ampache/ampache/issues/#BUGID#" + label = GitHub Issue Number + warnifnoissue = false + number = true + logregex = "#\\d+\n\\d+" diff --git a/sources/.travis.yml b/sources/.travis.yml new file mode 100644 index 0000000..6f51e77 --- /dev/null +++ b/sources/.travis.yml @@ -0,0 +1,17 @@ +language: php + +php: + - 5.4 + - 5.5 + - 5.6 + +before_install: + - wget http://cs.sensiolabs.org/get/php-cs-fixer.phar + +before_script: + - chmod +x scripts/tests/syntax.sh + - chmod +x scripts/tests/codestyle.sh + +script: + - scripts/tests/syntax.sh + - scripts/tests/codestyle.sh \ No newline at end of file diff --git a/sources/.tx/config b/sources/.tx/config new file mode 100644 index 0000000..a74e069 --- /dev/null +++ b/sources/.tx/config @@ -0,0 +1,8 @@ +[main] +host = https://www.transifex.com + +[project_slug.resource_slug] +file_filter = locale/_/LC_MESSAGES/messages.po +source_file = locale/base/messages.pot +source_lang = en_US +type = PO diff --git a/sources/CNAME b/sources/CNAME new file mode 100644 index 0000000..d5cfb4c --- /dev/null +++ b/sources/CNAME @@ -0,0 +1 @@ +ampache.org \ No newline at end of file diff --git a/sources/CONTRIBUTING.md b/sources/CONTRIBUTING.md new file mode 100644 index 0000000..249c9a2 --- /dev/null +++ b/sources/CONTRIBUTING.md @@ -0,0 +1,6 @@ +## Contribution +Please read [Development section](https://github.com/ampache/ampache/wiki#development). + +## Bug report +Be sure the bug is not already fixed in `develop` branch or already reported in current open issues. +Please add [some logs](https://github.com/ampache/ampache/wiki/Troubleshooting#enable-logging) with your new issue. diff --git a/sources/README.md b/sources/README.md index 9c68a48..73b9418 100755 --- a/sources/README.md +++ b/sources/README.md @@ -22,11 +22,13 @@ Recommended Version Currently, the recommended version is [git HEAD](https://github.com/ampache/ampache/archive/master.tar.gz). [![Build Status](https://api.travis-ci.org/ampache/ampache.png?branch=master)](https://travis-ci.org/ampache/ampache) -[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/ampache/ampache/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/ampache/ampache/?branch=master) Latest changes but unstable is [develop branch](https://github.com/ampache/ampache/archive/develop.tar.gz). [![Build Status](https://api.travis-ci.org/ampache/ampache.png?branch=develop)](https://travis-ci.org/ampache/ampache) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/ampache/ampache/badges/quality-score.png?b=develop)](https://scrutinizer-ci.com/g/ampache/ampache/?branch=develop) +[![Codacy Badge](https://www.codacy.com/project/badge/b28cdb9e9ee2431c7cb9c23d5438cb80)](https://www.codacy.com/app/afterster_2222/ampache) +[![Code Climate](https://codeclimate.com/github/ampache/ampache/badges/gpa.svg)](https://codeclimate.com/github/ampache/ampache) +[![SensioLabsInsight](https://insight.sensiolabs.com/projects/ee067d0e-3432-4062-a969-01b4ee037f48/mini.png)](https://insight.sensiolabs.com/projects/ee067d0e-3432-4062-a969-01b4ee037f48) Requirements ------------ @@ -38,7 +40,7 @@ receives the most testing: * nginx * IIS -* PHP 5.3 or greater. +* PHP 5.4 or greater. * PHP modules: * PDO @@ -46,6 +48,8 @@ receives the most testing: * hash * session * json + * simplexml (This is not strictly necessary, but may result in a better experience.) + * curl (This is not strictly necessary, but may result in a better experience.) * MySQL 5.x @@ -59,8 +63,8 @@ Upgrading If you are upgrading from an older version of Ampache we recommend moving the old directory out of the way, extracting the new copy in -its place and then copying the old config file into config/. All -database updates will be handled by Ampache. +its place and then copying the old config/ampache.cfg.php, /rest/.htaccess, +and /play/.htaccess files if any. All database updates will be handled by Ampache. License ------- @@ -99,6 +103,15 @@ Ampache includes some external modules that carry their own licensing. * [MediaTable] (https://github.com/edenspiekermann/MediaTable): MIT * [Responsive Elements] (https://github.com/kumailht/responsive-elements): MIT * [Bootstrap] (http://getbootstrap.com): MIT +* [jQuery Knob] (https://github.com/aterrien/jQuery-Knob): MIT +* [jQuery File Upload] (https://github.com/blueimp/jQuery-File-Upload): MIT +* [jsTree] (http://www.jstree.com): MIT +* [php-tmdb-api] (https://github.com/wtfzdotnet/php-tmdb-api) : MIT +* [TvDb] (https://github.com/Moinax/TvDb) : MIT +* [jQuery DateTimePicker] (https://github.com/xdan/datetimepicker) : MIT +* [pChart] (http://www.pchart.net) : GPL v3 +* [ZipStream-PHP] (https://github.com/maennchen/ZipStream-PHP) : [ZipStream-PHP license] (modules/zipstream/COPYING) +* [SabreDAV] (https://github.com/fruux/sabre-dav) : New BSD Translations @@ -109,19 +122,24 @@ following languages. If you are interested in updating an existing translation or adding a new one please see /locale/base/TRANSLATIONS for more instructions. -* English (en_US) -* German (de_DE) -* Spanish (es_ES) -* Dutch (nl_NL) -* Norwegian (nb_NO) -* UK English (en_GB) -* Italian (it_IT) -* French (fr_FR) -* Swedish (sv_SE) -* Japanese (ja_JP) -* Catalan (ca_ES) -* Russian (ru_RU) -* Czech (cs_CZ) +* English (en_US) +* Arabic (ar_SA) +* Catalan (ca_ES) +* Catalan (ca_ES) +* Czech (cs_CZ) +* Dutch (nl_NL) +* French (fr_FR) +* German (de_DE) +* Greek (el_GR) +* Italian (it_IT) +* Japanese (ja_JP) +* Norwegian (nb_NO) +* Persian (fa_IR) +* Polish (pl_PL) +* Russian (ru_RU) +* Spanish (es_ES) +* Swedish (sv_SE) +* UK English (en_GB) Credits ------- @@ -137,6 +155,7 @@ more features, encounter bugs, etc. * [Public Repository](http://github.com/ampache) * IRC: chat.freenode.net #ampache +* [Forum](https://groups.google.com/forum/#!forum/ampache) * [Issue Tracker](https://github.com/ampache/ampache/issues) * [Documentation](https://github.com/ampache/ampache/wiki) diff --git a/sources/admin/access.php b/sources/admin/access.php index 6146992..5c48f78 100644 --- a/sources/admin/access.php +++ b/sources/admin/access.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/admin/catalog.php b/sources/admin/catalog.php index 0bf5994..61372dd 100644 --- a/sources/admin/catalog.php +++ b/sources/admin/catalog.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -30,74 +30,57 @@ if (!Access::check('interface','100')) { UI::show_header(); +$catalogs = $_REQUEST['catalogs']; +// If only one catalog, check it is ready. +if (is_array($catalogs) && count($catalogs) == 1 && $_REQUEST['action'] !== 'delete_catalog' && $_REQUEST['action'] !== 'show_delete_catalog') { + // If not ready, display the data to make it ready / stop the action. + $catalog = Catalog::create_from_id($catalogs[0]); + if (!$catalog->isReady()) { + if (!isset($_REQUEST['perform_ready'])) { + $catalog->show_ready_process(); + UI::show_footer(); + exit; + } else { + $catalog->perform_ready(); + } + } +} +$sse_catalogs = urlencode(serialize($catalogs)); + /* Big switch statement to handle various actions */ switch ($_REQUEST['action']) { case 'add_to_all_catalogs': - $_REQUEST['catalogs'] = Catalog::get_catalogs(); + $sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=add_to_all_catalogs"; + sse_worker($sse_url); + show_confirmation(T_('Catalog Update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); + break; case 'add_to_catalog': - toggle_visible('ajax-loading'); - ob_end_flush(); if (AmpConfig::get('demo_mode')) { break; } - if ($_REQUEST['catalogs']) { - foreach ($_REQUEST['catalogs'] as $catalog_id) { - $catalog = Catalog::create_from_id($catalog_id); - $catalog->add_to_catalog($_POST); - } - } - $url = AmpConfig::get('web_path') . '/admin/catalog.php'; - $title = T_('Catalog Updated'); - $body = ''; - show_confirmation($title, $body, $url); - toggle_visible('ajax-loading'); + + $sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=add_to_catalog&catalogs=" . $sse_catalogs; + sse_worker($sse_url); + show_confirmation(T_('Catalog Update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); break; case 'update_all_catalogs': - $_REQUEST['catalogs'] = Catalog::get_catalogs(); + $sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=update_all_catalogs"; + sse_worker($sse_url); + show_confirmation(T_('Catalog Update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); + break; case 'update_catalog': - toggle_visible('ajax-loading'); - ob_end_flush(); - /* If they are in demo mode stop here */ - if (AmpConfig::get('demo_mode')) { break; } + if (AmpConfig::get('demo_mode')) { break; } - if (isset($_REQUEST['catalogs'])) { - foreach ($_REQUEST['catalogs'] as $catalog_id) { - $catalog = Catalog::create_from_id($catalog_id); - $catalog->verify_catalog(); - } - } - $url = AmpConfig::get('web_path') . '/admin/catalog.php'; - $title = T_('Catalog Updated'); - $body = ''; - show_confirmation($title,$body,$url); - toggle_visible('ajax-loading'); + $sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=update_catalog&catalogs=" . $sse_catalogs; + sse_worker($sse_url); + show_confirmation(T_('Catalog Update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); break; case 'full_service': - toggle_visible('ajax-loading'); - ob_end_flush(); - /* Make sure they aren't in demo mode */ if (AmpConfig::get('demo_mode')) { UI::access_denied(); break; } - if (!$_REQUEST['catalogs']) { - $_REQUEST['catalogs'] = Catalog::get_catalogs(); - } - - /* This runs the clean/verify/add in that order */ - foreach ($_REQUEST['catalogs'] as $catalog_id) { - $catalog = Catalog::create_from_id($catalog_id); - $catalog->clean_catalog(); - $catalog->count = 0; - $catalog->verify_catalog(); - $catalog->count = 0; - $catalog->add_to_catalog(); - } - Dba::optimize_tables(); - $url = AmpConfig::get('web_path') . '/admin/catalog.php'; - $title = T_('Catalog Updated'); - $body = ''; - show_confirmation($title,$body,$url); - toggle_visible('ajax-loading'); + $sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=full_service&catalogs=" . $sse_catalogs; + sse_worker($sse_url); + show_confirmation(T_('Catalog Update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); break; case 'delete_catalog': - /* Make sure they aren't in demo mode */ if (AmpConfig::get('demo_mode')) { break; } if (!Core::form_verify('delete_catalog')) { @@ -105,15 +88,21 @@ switch ($_REQUEST['action']) { exit; } + $deleted = true; /* Delete the sucker, we don't need to check perms as thats done above */ - Catalog::delete($_GET['catalog_id']); + foreach ($catalogs as $catalog_id) { + $deleted = Catalog::delete($catalog_id); + if (!$deleted) break; + } $next_url = AmpConfig::get('web_path') . '/admin/catalog.php'; - show_confirmation(T_('Catalog Deleted'), T_('The Catalog and all associated records have been deleted'),$next_url); + if ($deleted) { + show_confirmation(T_('Catalog Deleted'), T_('The Catalog and all associated records have been deleted'), $next_url); + } else { + show_confirmation(T_('Error'), T_('Cannot delete the catalog'), $next_url); + } break; case 'show_delete_catalog': - $catalog_id = scrub_in($_GET['catalog_id']); - - $next_url = AmpConfig::get('web_path') . '/admin/catalog.php?action=delete_catalog&catalog_id=' . scrub_out($catalog_id); + $next_url = AmpConfig::get('web_path') . '/admin/catalog.php?action=delete_catalog&catalogs[]=' . implode(',', $catalogs); show_confirmation(T_('Catalog Delete'), T_('Confirm Deletion Request'),$next_url,1,'delete_catalog'); break; case 'enable_disabled': @@ -134,27 +123,14 @@ switch ($_REQUEST['action']) { show_confirmation($title,$body,$url); break; case 'clean_all_catalogs': - $_REQUEST['catalogs'] = Catalog::get_catalogs(); + $sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=clean_all_catalogs"; + sse_worker($sse_url); + show_confirmation(T_('Catalog Clean started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); + break; case 'clean_catalog': - toggle_visible('ajax-loading'); - ob_end_flush(); - /* If they are in demo mode stop them here */ - if (AmpConfig::get('demo_mode')) { break; } - - // Make sure they checked something - if (isset($_REQUEST['catalogs'])) { - foreach ($_REQUEST['catalogs'] as $catalog_id) { - $catalog = Catalog::create_from_id($catalog_id); - $catalog->clean_catalog(); - } // end foreach catalogs - Dba::optimize_tables(); - } - - $url = AmpConfig::get('web_path') . '/admin/catalog.php'; - $title = T_('Catalog Cleaned'); - $body = ''; - show_confirmation($title,$body,$url); - toggle_visible('ajax-loading'); + $sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=clean_catalog&catalogs=" . $sse_catalogs; + sse_worker($sse_url); + show_confirmation(T_('Catalog Clean started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); break; case 'update_catalog_settings': /* No Demo Here! */ @@ -171,23 +147,9 @@ switch ($_REQUEST['action']) { case 'update_from': if (AmpConfig::get('demo_mode')) { break; } - // First see if we need to do an add - if ($_POST['add_path'] != '/' AND strlen($_POST['add_path'])) { - if ($catalog_id = Catalog_local::get_from_path($_POST['add_path'])) { - $catalog = Catalog::create_from_id($catalog_id); - $catalog->add_to_catalog(array('subdirectory'=>$_POST['add_path'])); - } - } // end if add - - // Now check for an update - if ($_POST['update_path'] != '/' AND strlen($_POST['update_path'])) { - if ($catalog_id = Catalog_local::get_from_path($_POST['update_path'])) { - $songs = Song::get_from_path($_POST['update_path']); - foreach ($songs as $song_id) { Catalog::update_single_item('song',$song_id); } - } - } // end if update - - echo T_("Done."); + $sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=update_from&add_path=" . scrub_in($_POST['add_path']) . "&update_path=" . $_POST['update_path']; + sse_worker($sse_url); + show_confirmation(T_('Subdirectory update started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); break; case 'add_catalog': /* Wah Demo! */ @@ -218,19 +180,10 @@ switch ($_REQUEST['action']) { break; } - $catalog = Catalog::create_from_id($catalog_id); - - // Run our initial add - $catalog->add_to_catalog($_POST); - - UI::show_box_top(T_('Catalog Created'), 'box box_catalog_created'); - echo "

" . T_('Catalog Created') . "

"; - Error::display('general'); - Error::display('catalog_add'); - UI::show_box_bottom(); - - show_confirmation('','', AmpConfig::get('web_path').'/admin/catalog.php'); + $sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=add_catalog&catalog_id=" . $catalog_id . "&options=" . urlencode(serialize($_POST)); + sse_worker($sse_url); + show_confirmation(T_('Catalog Creation started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); } else { require AmpConfig::get('prefix') . '/templates/show_add_catalog.inc.php'; } @@ -275,23 +228,10 @@ switch ($_REQUEST['action']) { $catalog->format(); require_once AmpConfig::get('prefix') . '/templates/show_edit_catalog.inc.php'; break; - case 'gather_album_art': - toggle_visible('ajax-loading'); - ob_end_flush(); - - $catalogs = $_REQUEST['catalogs'] ? $_REQUEST['catalogs'] : Catalog::get_catalogs(); - - // Iterate throught the catalogs and gather as needed - foreach ($catalogs as $catalog_id) { - $catalog = Catalog::create_from_id($catalog_id); - require AmpConfig::get('prefix') . '/templates/show_gather_art.inc.php'; - flush(); - $catalog->gather_art(); - } - $url = AmpConfig::get('web_path') . '/admin/catalog.php'; - $title = T_('Album Art Search Finished'); - $body = ''; - show_confirmation($title,$body,$url); + case 'gather_media_art': + $sse_url = AmpConfig::get('web_path') . "/server/sse.server.php?worker=catalog&action=gather_media_art&catalogs=" . $sse_catalogs; + sse_worker($sse_url); + show_confirmation(T_('Media Art Search started...'), '', AmpConfig::get('web_path') . '/admin/catalog.php', 0, 'confirmation', false); break; case 'show_catalogs': default: diff --git a/sources/admin/duplicates.php b/sources/admin/duplicates.php index ab03ed6..69c812b 100644 --- a/sources/admin/duplicates.php +++ b/sources/admin/duplicates.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/admin/export.php b/sources/admin/export.php index 59e4f1a..bd9e5d1 100644 --- a/sources/admin/export.php +++ b/sources/admin/export.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/admin/index.php b/sources/admin/index.php index fcd9b56..1d63666 100644 --- a/sources/admin/index.php +++ b/sources/admin/index.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/admin/license.php b/sources/admin/license.php new file mode 100644 index 0000000..aa22a2f --- /dev/null +++ b/sources/admin/license.php @@ -0,0 +1,65 @@ +id) { + $license->update($_POST); + } + $text = T_('License Updated'); + } else { + License::create($_POST); + $text = T_('License Created'); + } + show_confirmation($text,'',AmpConfig::get('web_path').'/admin/license.php'); + break; + case 'show_edit': + $license = new License($_REQUEST['license_id']); + case 'show_create': + require_once AmpConfig::get('prefix') . '/templates/show_edit_license.inc.php'; + break; + case 'delete': + License::delete($_REQUEST['license_id']); + show_confirmation(T_('License Deleted'),'',AmpConfig::get('web_path').'/admin/license.php'); + break; + default: + $browse = new Browse(); + $browse->set_type('license'); + $browse->set_simple_browse(true); + $license_ids = $browse->get_objects(); + $browse->show_objects($license_ids); + $browse->store(); + break; +} + +UI::show_footer(); diff --git a/sources/admin/mail.php b/sources/admin/mail.php index 46df45c..15f5e76 100644 --- a/sources/admin/mail.php +++ b/sources/admin/mail.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -39,7 +39,10 @@ switch ($_REQUEST['action']) { // Multi-byte Character Mail if (function_exists('mb_language')) { - ini_set("mbstring.internal_encoding","UTF-8"); + $ini_default_charset = version_compare(PHP_VERSION, '5.6', '<') ? 'mbstring.internal_encoding' : 'default_charset'; + if (ini_get($ini_default_charset)) { + ini_set($ini_default_charset, "UTF-8"); + } mb_language("uni"); } diff --git a/sources/admin/modules.php b/sources/admin/modules.php index f5657f8..7407410 100644 --- a/sources/admin/modules.php +++ b/sources/admin/modules.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -48,10 +48,14 @@ switch ($_REQUEST['action']) { Preference::update('localplay_level',$GLOBALS['user']->id,'100'); Preference::update('localplay_controller',$GLOBALS['user']->id,$localplay->type); - header("Location:" . AmpConfig::get('web_path') . '/admin/modules.php?action=show_localplay'); + /* Show Confirmation */ + $url = AmpConfig::get('web_path') . '/admin/modules.php?action=show_localplay'; + $title = T_('Localplay Installed'); + $body = ''; + show_confirmation($title ,$body, $url); break; case 'install_catalog_type': - $type = scrub_in($_REQUEST['type']); + $type = (string) scrub_in($_REQUEST['type']); $catalog = Catalog::create_catalog_type($type); if ($catalog == null) { Error::add('general', T_('Install Failed, Catalog Error')); @@ -68,21 +72,21 @@ switch ($_REQUEST['action']) { show_confirmation($title ,$body, $url); break; case 'confirm_uninstall_localplay': - $type = scrub_in($_REQUEST['type']); + $type = (string) scrub_in($_REQUEST['type']); $url = AmpConfig::get('web_path') . '/admin/modules.php?action=uninstall_localplay&type=' . $type; $title = T_('Are you sure you want to remove this plugin?'); $body = ''; show_confirmation($title,$body,$url,1); break; case 'confirm_uninstall_catalog_type': - $type = scrub_in($_REQUEST['type']); + $type = (string) scrub_in($_REQUEST['type']); $url = AmpConfig::get('web_path') . '/admin/modules.php?action=uninstall_catalog_type&type=' . $type; $title = T_('Are you sure you want to remove this plugin?'); $body = ''; show_confirmation($title,$body,$url,1); break; case 'uninstall_localplay': - $type = scrub_in($_REQUEST['type']); + $type = (string) scrub_in($_REQUEST['type']); $localplay = new Localplay($type); $localplay->uninstall(); @@ -94,7 +98,7 @@ switch ($_REQUEST['action']) { show_confirmation($title,$body,$url); break; case 'uninstall_catalog_type': - $type = scrub_in($_REQUEST['type']); + $type = (string) scrub_in($_REQUEST['type']); $catalog = Catalog::create_catalog_type($type); if ($catalog == null) { diff --git a/sources/admin/shout.php b/sources/admin/shout.php index 04a1061..50c0221 100644 --- a/sources/admin/shout.php +++ b/sources/admin/shout.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -32,8 +32,10 @@ UI::show_header(); // Switch on the incomming action switch ($_REQUEST['action']) { case 'edit_shout': - $shout_id = $_POST['shout_id']; - $update = Shoutbox::update($_POST); + $shout = new Shoutbox($_REQUEST['shout_id']); + if ($shout->id) { + $shout->update($_POST); + } show_confirmation(T_('Shoutbox Post Updated'),'',AmpConfig::get('web_path').'/admin/shout.php'); break; case 'show_edit': diff --git a/sources/admin/system.php b/sources/admin/system.php index d55f108..ea009d9 100644 --- a/sources/admin/system.php +++ b/sources/admin/system.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -39,9 +39,13 @@ switch ($_REQUEST['action']) { $current = parse_ini_file(AmpConfig::get('prefix') . '/config/ampache.cfg.php'); $final = generate_config($current); $browser = new Horde_Browser(); - $browser->downloadHeaders('ampache.cfg.php','text/plain',false,filesize(AmpConfig::get('prefix') . '/config/ampache.cfg.php.dist')); + $browser->downloadHeaders('ampache.cfg.php', 'text/plain',false,filesize(AmpConfig::get('prefix') . '/config/ampache.cfg.php.dist')); echo $final; exit; + case 'write_config': + write_config(AmpConfig::get('prefix') . '/config/ampache.cfg.php'); + header('Location: '. AmpConfig::get('web_path') . '/index.php'); + exit; case 'reset_db_charset': Dba::reset_db_charset(); show_confirmation(T_('Database Charset Updated'), T_('Your Database and associated tables have been updated to match your currently configured charset'), AmpConfig::get('web_path').'/admin/system.php?action=show_debug'); @@ -53,6 +57,14 @@ switch ($_REQUEST['action']) { } require_once AmpConfig::get('prefix') . '/templates/show_debug.inc.php'; break; + case 'clear_cache': + switch ($_REQUEST['type']) { + case 'song' : Song::clear_cache(); break; + case 'artist' : Artist::clear_cache(); break; + case 'album' : Album::clear_cache(); break; + } + show_confirmation(T_('Cache cleared'), T_('Your cache has been cleared successfully.'), AmpConfig::get('web_path').'/admin/system.php?action=show_debug'); + break; default: // Rien a faire break; diff --git a/sources/admin/users.php b/sources/admin/users.php index 7068d4b..5bd3e24 100644 --- a/sources/admin/users.php +++ b/sources/admin/users.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -40,14 +40,17 @@ switch ($_REQUEST['action']) { } /* Clean up the variables */ - $user_id = intval($_POST['user_id']); - $username = scrub_in($_POST['username']); - $fullname = scrub_in($_POST['fullname']); - $email = scrub_in($_POST['email']); - $website = scrub_in($_POST['website']); - $access = scrub_in($_POST['access']); - $pass1 = $_POST['password_1']; - $pass2 = $_POST['password_2']; + $user_id = intval($_POST['user_id']); + $username = scrub_in($_POST['username']); + $fullname = scrub_in($_POST['fullname']); + $email = scrub_in($_POST['email']); + $website = scrub_in($_POST['website']); + $access = scrub_in($_POST['access']); + $pass1 = $_POST['password_1']; + $pass2 = $_POST['password_2']; + $state = scrub_in($_POST['state']); + $city = scrub_in($_POST['city']); + $fullname_public = isset($_POST['fullname_public']); /* Setup the temp user */ $client = new User($user_id); @@ -55,16 +58,27 @@ switch ($_REQUEST['action']) { /* Verify Input */ if (empty($username)) { Error::add('username', T_("Error Username Required")); + } else { + if ($username != $client->username) { + if (!User::check_username($username)) { + Error::add('username', T_("Error Username already exists")); + } + } } if ($pass1 !== $pass2 && !empty($pass1)) { Error::add('password', T_("Error Passwords don't match")); } - /* If we've got an error then break! */ + // Check the mail for correct address formation. + if (!Mailer::validate_address($email)) { + Error::add('email', T_('Invalid email address')); + } + + /* If we've got an error then show edit form! */ if (Error::occurred()) { - $_REQUEST['action'] = 'show_edit'; + require_once AmpConfig::get('prefix') . '/templates/show_edit_user.inc.php'; break; - } // if we've had an oops! + } if ($access != $client->access) { $client->update_access($access); @@ -81,9 +95,18 @@ switch ($_REQUEST['action']) { if ($fullname != $client->fullname) { $client->update_fullname($fullname); } + if ($fullname_public != $client->fullname_public) { + $client->update_fullname_public($fullname_public); + } if ($pass1 == $pass2 && strlen($pass1)) { $client->update_password($pass1); } + if ($state != $client->state) { + $client->update_state($state); + } + if ($city != $client->city) { + $client->update_city($city); + } $client->upload_avatar(); show_confirmation(T_('User Updated'), $client->fullname . "(" . $client->username . ")" . T_('updated'), AmpConfig::get('web_path'). '/admin/users.php'); @@ -96,13 +119,15 @@ switch ($_REQUEST['action']) { exit; } - $username = scrub_in($_POST['username']); - $fullname = scrub_in($_POST['fullname']); - $email = scrub_in($_POST['email']); + $username = scrub_in($_POST['username']); + $fullname = scrub_in($_POST['fullname']); + $email = scrub_in($_POST['email']); $website = scrub_in($_POST['website']); - $access = scrub_in($_POST['access']); - $pass1 = $_POST['password_1']; - $pass2 = $_POST['password_2']; + $access = scrub_in($_POST['access']); + $pass1 = $_POST['password_1']; + $pass2 = $_POST['password_2']; + $state = (string) scrub_in($_POST['state']); + $city = (string) scrub_in($_POST['city']); if ($pass1 !== $pass2 || !strlen($pass1)) { Error::add('password', T_("Error Passwords don't match")); @@ -117,20 +142,25 @@ switch ($_REQUEST['action']) { Error::add('username', T_('Error Username already exists')); } - if (!Error::occurred()) { - /* Attempt to create the user */ - $user_id = User::create($username, $fullname, $email, $website, $pass1, $access); - if (!$user_id) { - Error::add('general', T_("Error: Insert Failed")); - } + // Check the mail for correct address formation. + if (!Mailer::validate_address($email)) { + Error::add('email', T_('Invalid email address')); + } - $user = new User($user_id); - $user->upload_avatar(); - } // if no errors - else { - $_REQUEST['action'] = 'show_add_user'; + /* If we've got an error then show add form! */ + if (Error::occurred()) { + require_once AmpConfig::get('prefix') . '/templates/show_add_user.inc.php'; break; } + + /* Attempt to create the user */ + $user_id = User::create($username, $fullname, $email, $website, $pass1, $access, $state, $city); + if (!$user_id) { + Error::add('general', T_("Error: Insert Failed")); + } + $user = new User($user_id); + $user->upload_avatar(); + if ($access == 5) { $access = T_('Guest');} elseif ($access == 25) { $access = T_('User');} elseif ($access == 100) { $access = T_('Admin');} /* HINT: %1 Username, %2 Access num */ @@ -139,6 +169,9 @@ switch ($_REQUEST['action']) { case 'enable': $client = new User($_REQUEST['user_id']); $client->enable(); + if (!AmpConfig::get('user_no_email_confirm')) { + Registration::send_account_enabled($client->username, $client->fullname, $client->email); + } show_confirmation(T_('User Enabled'),$client->fullname . ' (' . $client->username . ')', AmpConfig::get('web_path'). '/admin/users.php'); break; case 'disable': @@ -239,7 +272,7 @@ switch ($_REQUEST['action']) { $browse = new Browse(); $browse->reset_filters(); $browse->set_type('user'); - $browse->set_simple_browse(1); + $browse->set_simple_browse(true); $browse->set_sort('name','ASC'); $user_ids = $browse->get_objects(); $browse->show_objects($user_ids); diff --git a/sources/albums.php b/sources/albums.php index 6949003..5b189c3 100644 --- a/sources/albums.php +++ b/sources/albums.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -26,144 +26,33 @@ require_once AmpConfig::get('prefix') . '/templates/header.inc.php'; /* Switch on Action */ switch ($_REQUEST['action']) { - case 'clear_art': - if (!$GLOBALS['user']->has_access('75')) { UI::access_denied(); } - $art = new Art($_GET['album_id'],'album'); - $art->reset(); - show_confirmation(T_('Album Art Cleared'), T_('Album Art information has been removed from the database'),"/albums.php?action=show&album=" . $art->uid); - break; - // Upload album art - case 'upload_art': + case 'delete': + if (AmpConfig::get('demo_mode')) { break; } - // we didn't find anything - if (empty($_FILES['file']['tmp_name'])) { - show_confirmation(T_('Album Art Not Located'), T_('Album Art could not be located at this time. This may be due to write access error, or the file is not received correctly.'),"/albums.php?action=show&album=" . $_REQUEST['album_id']); - break; - } + $album_id = scrub_in($_REQUEST['album_id']); + show_confirmation( + T_('Album Deletion'), + T_('Are you sure you want to permanently delete this album?'), + AmpConfig::get('web_path')."/albums.php?action=confirm_delete&album_id=" . $album_id, + 1, + 'delete_album' + ); + break; + case 'confirm_delete': + if (AmpConfig::get('demo_mode')) { break; } $album = new Album($_REQUEST['album_id']); - // Pull the image information - $data = array('file'=>$_FILES['file']['tmp_name']); - $image_data = Art::get_from_source($data, 'album'); - - // If we got something back insert it - if ($image_data) { - $art = new Art($album->id,'album'); - $art->insert($image_data,$_FILES['file']['type']); - show_confirmation(T_('Album Art Inserted'),'',"/albums.php?action=show&album=" . $album->id); - } - // Else it failed - else { - show_confirmation(T_('Album Art Not Located'), T_('Album Art could not be located at this time. This may be due to write access error, or the file is not received correctly.'),"/albums.php?action=show&album=" . $album->id); + if (!Catalog::can_remove($album)) { + debug_event('album', 'Unauthorized to remove the album `.' . $album->id . '`.', 1); + UI::access_denied(); + exit; } - break; - case 'find_art': - // If not a user then kick em out - if (!Access::check('interface','25')) { UI::access_denied(); exit; } - - // Prevent the script from timing out - set_time_limit(0); - - // get the Album information - $album = new Album($_GET['album_id']); - $album->format(); - $art = new Art($album->id,'album'); - $images = array(); - $cover_url = array(); - - // If we've got an upload ignore the rest and just insert it - if (!empty($_FILES['file']['tmp_name'])) { - $path_info = pathinfo($_FILES['file']['name']); - $upload['file'] = $_FILES['file']['tmp_name']; - $upload['mime'] = 'image/' . $path_info['extension']; - $image_data = Art::get_from_source($upload, 'album'); - - if ($image_data) { - $art->insert($image_data,$upload['0']['mime']); - show_confirmation(T_('Album Art Inserted'),'',"/albums.php?action=show&album=" . $_REQUEST['album_id']); - break; - - } // if image data - - } // if it's an upload - - // Build the options for our search - if (isset($_REQUEST['artist_name'])) { - $artist = scrub_in($_REQUEST['artist_name']); - } elseif ($album->artist_count == '1') { - $artist = $album->f_artist_name; + if ($album->remove_from_disk()) { + show_confirmation(T_('Album Deletion'), T_('Album has been deleted.'), AmpConfig::get('web_path')); } else { - $artist = ""; + show_confirmation(T_('Album Deletion'), T_('Cannot delete this album.'), AmpConfig::get('web_path')); } - if (isset($_REQUEST['album_name'])) { - $album_name = scrub_in($_REQUEST['album_name']); - } else { - $album_name = $album->full_name; - } - - $options['artist'] = $artist; - $options['album_name'] = $album_name; - $options['keyword'] = trim($artist . " " . $album_name); - - // Attempt to find the art. - $images = $art->gather($options); - - if (!empty($_REQUEST['cover'])) { - $path_info = pathinfo($_REQUEST['cover']); - $cover_url[0]['url'] = scrub_in($_REQUEST['cover']); - $cover_url[0]['mime'] = 'image/' . $path_info['extension']; - } - $images = array_merge($cover_url,$images); - - // If we've found anything then go for it! - if (count($images)) { - // We don't want to store raw's in here so we need to strip them out into a separate array - foreach ($images as $index=>$image) { - if ($image['raw']) { - unset($images[$index]['raw']); - } - } // end foreach - // Store the results for further use - $_SESSION['form']['images'] = $images; - require_once AmpConfig::get('prefix') . '/templates/show_album_art.inc.php'; - } - // Else nothing - else { - show_confirmation(T_('Album Art Not Located'), T_('Album Art could not be located at this time. This may be due to write access error, or the file is not received correctly.'),"/albums.php?action=show&album=" . $album->id); - } - - $albumname = $album->name; - $artistname = $artist; - - // Remember the last typed entry, if there was one - if (!empty($_REQUEST['album_name'])) { $albumname = scrub_in($_REQUEST['album_name']); } - if (!empty($_REQUEST['artist_name'])) { $artistname = scrub_in($_REQUEST['artist_name']); } - - require_once AmpConfig::get('prefix') . '/templates/show_get_albumart.inc.php'; - - break; - case 'select_art': - - /* Check to see if we have the image url still */ - $image_id = $_REQUEST['image']; - $album_id = $_REQUEST['album_id']; - - // Prevent the script from timing out - set_time_limit(0); - - $album = new Album($album_id); - $album_groups = $album->get_group_disks_ids(); - - $image = Art::get_from_source($_SESSION['form']['images'][$image_id], 'album'); - $mime = $_SESSION['form']['images'][$image_id]['mime']; - - foreach ($album_groups as $a_id) { - $art = new Art($a_id, 'album'); - $art->insert($image, $mime); - } - - header("Location:" . AmpConfig::get('web_path') . "/albums.php?action=show&album=" . $album_id); break; case 'update_from_tags': // Make sure they are a 'power' user at least @@ -193,7 +82,7 @@ switch ($_REQUEST['action']) { if (isset($_GET['order'])) { $songs = explode(";", $_GET['order']); - $track = 1; + $track = $_GET['offset'] ? (intval($_GET['offset']) + 1) : 1; foreach ($songs as $song_id) { if ($song_id != '') { Song::update_track($track, $song_id); diff --git a/sources/artists.php b/sources/artists.php index c6d86c8..7fb38ff 100644 --- a/sources/artists.php +++ b/sources/artists.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -28,10 +28,42 @@ UI::show_header(); * Display Switch */ switch ($_REQUEST['action']) { + case 'delete': + if (AmpConfig::get('demo_mode')) { break; } + + $artist_id = scrub_in($_REQUEST['artist_id']); + show_confirmation( + T_('Artist Deletion'), + T_('Are you sure you want to permanently delete this artist?'), + AmpConfig::get('web_path')."/artists.php?action=confirm_delete&artist_id=" . $artist_id, + 1, + 'delete_artist' + ); + break; + case 'confirm_delete': + if (AmpConfig::get('demo_mode')) { break; } + + $artist = new Artist($_REQUEST['artist_id']); + if (!Catalog::can_remove($artist)) { + debug_event('artist', 'Unauthorized to remove the artist `.' . $artist->id . '`.', 1); + UI::access_denied(); + exit; + } + + if ($artist->remove_from_disk()) { + show_confirmation(T_('Artist Deletion'), T_('Artist has been deleted.'), AmpConfig::get('web_path')); + } else { + show_confirmation(T_('Artist Deletion'), T_('Cannot delete this artist.'), AmpConfig::get('web_path')); + } + break; case 'show': $artist = new Artist($_REQUEST['artist']); $artist->format(); - $object_ids = $artist->get_albums($_REQUEST['catalog']); + if (AmpConfig::get('album_release_type')) { + $multi_object_ids = $artist->get_albums($_REQUEST['catalog'], false, true); + } else { + $object_ids = $artist->get_albums($_REQUEST['catalog']); + } $object_type = 'album'; require_once AmpConfig::get('prefix') . '/templates/show_artist.inc.php'; break; diff --git a/sources/arts.php b/sources/arts.php new file mode 100644 index 0000000..65aaffe --- /dev/null +++ b/sources/arts.php @@ -0,0 +1,169 @@ +get_user_owner() != $GLOBALS['user']->id)) { UI::access_denied(); exit; } + +/* Switch on Action */ +switch ($_REQUEST['action']) { + case 'clear_art': + $art = new Art($object_id, $object_type); + $art->reset(); + show_confirmation(T_('Art Cleared'), T_('Art information has been removed from the database'), $burl); + break; + // Upload art + case 'upload_art': + // we didn't find anything + if (empty($_FILES['file']['tmp_name'])) { + show_confirmation(T_('Art Not Located'), T_('Art could not be located at this time. This may be due to write access error, or the file is not received correctly.'), $burl); + break; + } + + // Pull the image information + $data = array('file'=>$_FILES['file']['tmp_name']); + $image_data = Art::get_from_source($data, $object_type); + + // If we got something back insert it + if ($image_data) { + $art = new Art($object_id, $object_type); + $art->insert($image_data,$_FILES['file']['type']); + show_confirmation(T_('Art Inserted'), '', $burl); + } + // Else it failed + else { + show_confirmation(T_('Art Not Located'), T_('Art could not be located at this time. This may be due to write access error, or the file is not received correctly.'), $burl); + } + + break; + case 'find_art': + // Prevent the script from timing out + set_time_limit(0); + + $item->format(); + $art = new Art($object_id, $object_type); + $images = array(); + $cover_url = array(); + + // If we've got an upload ignore the rest and just insert it + if (!empty($_FILES['file']['tmp_name'])) { + $path_info = pathinfo($_FILES['file']['name']); + $upload['file'] = $_FILES['file']['tmp_name']; + $upload['mime'] = 'image/' . $path_info['extension']; + $image_data = Art::get_from_source($upload, $object_type); + + if ($image_data) { + $art->insert($image_data,$upload['0']['mime']); + show_confirmation(T_('Art Inserted'), '', $burl); + break; + + } // if image data + + } // if it's an upload + + $keywords = $item->get_keywords(); + $keyword = ''; + $options = array(); + foreach ($keywords as $key => $word) { + if (isset($_REQUEST['option_' . $key])) { + $word['value'] = $_REQUEST['option_' . $key]; + } + $options[$key] = $word['value']; + if ($word['important']) { + if (!empty($word['value'])) { + $keyword .= ' ' . $word['value']; + } + } + } + $options['keyword'] = trim($keyword); + + // Attempt to find the art. + $images = $art->gather($options); + + if (!empty($_REQUEST['cover'])) { + $path_info = pathinfo($_REQUEST['cover']); + $cover_url[0]['url'] = scrub_in($_REQUEST['cover']); + $cover_url[0]['mime'] = 'image/' . $path_info['extension']; + } + $images = array_merge($cover_url, $images); + + // If we've found anything then go for it! + if (count($images)) { + // We don't want to store raw's in here so we need to strip them out into a separate array + foreach ($images as $index=>$image) { + if ($image['raw']) { + unset($images[$index]['raw']); + } + } // end foreach + // Store the results for further use + $_SESSION['form']['images'] = $images; + require_once AmpConfig::get('prefix') . '/templates/show_arts.inc.php'; + } + // Else nothing + else { + show_confirmation(T_('Art Not Located'), T_('Art could not be located at this time. This may be due to write access error, or the file is not received correctly.'), $burl); + } + + require_once AmpConfig::get('prefix') . '/templates/show_get_art.inc.php'; + + break; + case 'select_art': + + /* Check to see if we have the image url still */ + $image_id = $_REQUEST['image']; + + // Prevent the script from timing out + set_time_limit(0); + + $image = Art::get_from_source($_SESSION['form']['images'][$image_id], 'album'); + $mime = $_SESSION['form']['images'][$image_id]['mime']; + + // Special case for albums, I'm not sure if we should keep it, remove it or find a generic way + if ($object_type == 'album') { + $album = new $object_type($object_id); + $album_groups = $album->get_group_disks_ids(); + foreach ($album_groups as $a_id) { + $art = new Art($a_id, $object_type); + $art->insert($image, $mime); + } + } else { + $art = new Art($object_id, $object_type); + $art->insert($image, $mime); + } + + header("Location:" . $burl); + break; +} + +UI::show_footer(); diff --git a/sources/batch.php b/sources/batch.php index d4a9d95..14e0a9d 100644 --- a/sources/batch.php +++ b/sources/batch.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -21,7 +21,16 @@ */ if (!defined('NO_SESSION')) { - require_once 'lib/init.php'; + if (isset($_REQUEST['ssid'])) { + define('NO_SESSION', 1); + require_once 'lib/init.php'; + if (!Session::exists('stream', $_REQUEST['ssid'])) { + UI::access_denied(); + exit; + } + } else { + require_once 'lib/init.php'; + } } ob_end_clean(); @@ -35,72 +44,78 @@ if (!defined('NO_SESSION') && !Access::check_function('batch_download')) { set_time_limit(0); $media_ids = array(); -$name = "Unknown.zip"; +$default_name = "Unknown.zip"; +$object_type = scrub_in($_REQUEST['action']); +$name = $default_name; -switch ($_REQUEST['action']) { - case 'tmp_playlist': - $media_ids = $GLOBALS['user']->playlist->get_items(); - $name = $GLOBALS['user']->username . ' - Playlist'; - break; - case 'playlist': - $playlist = new Playlist($_REQUEST['id']); - $media_ids = $playlist->get_songs(); - $name = $playlist->name; - break; - case 'smartplaylist': - $search = new Search('song', $_REQUEST['id']); - $sql = $search->to_sql(); - $sql = $sql['base'] . ' ' . $sql['table_sql'] . ' WHERE ' . - $sql['where_sql']; - $db_results = Dba::read($sql); - while ($row = Dba::fetch_assoc($db_results)) { - $media_ids[] = $row['id']; +if ($object_type == 'browse') { + $object_type = $_REQUEST['type']; +} + +if (!check_can_zip($object_type)) { + debug_event('batch', 'Object type `' . $object_type . '` is not allowed to be zipped.', 1); + UI::access_denied(); + exit; +} + +if (Core::is_playable_item($_REQUEST['action'])) { + $id = $_REQUEST['id']; + if (!is_array($id)) { + $id = array($id); + } + $media_ids = array(); + foreach ($id as $i) { + $libitem = new $object_type($i); + if ($libitem->id) { + $libitem->format(); + $name = $libitem->get_fullname(); + $media_ids = array_merge($media_ids, $libitem->get_medias()); } - $name = $search->name; - break; - case 'album': - foreach ($_REQUEST['id'] as $a) { - $album = new Album($a); - if (empty($name)) { - $name = $album->name; - } - $asongs = $album->get_songs(); - foreach ($asongs as $song_id) { - $media_ids[] = $song_id; - } - } - break; - case 'artist': - $artist = new Artist($_REQUEST['id']); - $media_ids = $artist->get_songs(); - $name = $artist->name; - break; - case 'browse': - $id = scrub_in($_REQUEST['browse_id']); - $browse = new Browse($id); - $browse_media_ids = $browse->get_saved(); - foreach ($browse_media_ids as $media_id) { - switch ($_REQUEST['type']) { - case 'album': - $album = new Album($media_id); - $media_ids = array_merge($media_ids, $album->get_songs()); - break; - case 'song': - $media_ids[] = $media_id; - break; - case 'video': - $media_ids[] = array('Video', $media_id); - break; - } // switch on type - } // foreach media_id - $name = 'Batch-' . date("dmY",time()); - default: - // Rien a faire - break; -} // action switch + } +} else { + switch ($_REQUEST['action']) { + case 'tmp_playlist': + $media_ids = $GLOBALS['user']->playlist->get_items(); + $name = $GLOBALS['user']->username . ' - Playlist'; + break; + case 'browse': + $id = intval(scrub_in($_REQUEST['browse_id'])); + $browse = new Browse($id); + $browse_media_ids = $browse->get_saved(); + foreach ($browse_media_ids as $media_id) { + switch ($object_type) { + case 'album': + $album = new Album($media_id); + $media_ids = array_merge($media_ids, $album->get_songs()); + break; + case 'song': + $media_ids[] = $media_id; + break; + case 'video': + $media_ids[] = array('object_type' => 'Video', 'object_id' => $media_id); + break; + } // switch on type + } // foreach media_id + $name = 'Batch-' . date("dmY",time()); + default: + // Rien a faire + break; + } // action switch +} + +if (!User::stream_control($media_ids)) { + debug_event('UI::access_denied', 'Stream control failed for user ' . $GLOBALS['user']->username, '3'); + UI::access_denied(); + exit; +} + +// Write/close session data to release session lock for this script. +// This to allow other pages from the same session to be processed +// Do NOT change any session variable after this call +session_write_close(); // Take whatever we've got and send the zip -$song_files = get_song_files($media_ids); +$song_files = get_media_files($media_ids); if (is_array($song_files['0'])) { set_memory_limit($song_files['1']+32); send_zip($name,$song_files['0']); diff --git a/sources/bin/.htaccess b/sources/bin/.htaccess index 896fbc5..cfd4051 100644 --- a/sources/bin/.htaccess +++ b/sources/bin/.htaccess @@ -1,2 +1,10 @@ -Order deny,allow -Deny from all \ No newline at end of file +# Apache 2.4 + + Require all denied + + +# Apache 2.2 + + Order deny,allow + Deny from all + \ No newline at end of file diff --git a/sources/bin/broadcast.inc b/sources/bin/broadcast.inc new file mode 100644 index 0000000..0d600d0 --- /dev/null +++ b/sources/bin/broadcast.inc @@ -0,0 +1,45 @@ + diff --git a/sources/bin/calculate_art_size.inc b/sources/bin/calculate_art_size.inc new file mode 100644 index 0000000..a6cc134 --- /dev/null +++ b/sources/bin/calculate_art_size.inc @@ -0,0 +1,48 @@ + diff --git a/sources/bin/catalog_update.inc b/sources/bin/catalog_update.inc index 5807f3f..0e2c044 100644 --- a/sources/bin/catalog_update.inc +++ b/sources/bin/catalog_update.inc @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -29,38 +29,48 @@ require_once $prefix . '/lib/init.php'; ob_end_flush(); -$catclean = 0; //All off by default +$tmpmemlimoff = 0; //All off by default +$catclean = 0; $catverify = 0; $catadd = 0; $artadd = 0; $plimp = 0; +$optimizetables = 0; if (count($_SERVER['argv']) == 1) { - $operations_string = "\n\t". T_('- All Catalog Operations'); + $operations_string = "\n\t- ". T_('Doing all catalog operations'); } if (count($_SERVER['argv']) > 1) { for ($x = 1; $x < count($_SERVER['argv']); $x++) { - if ($_SERVER['argv'][$x] == "-c") { - $operations_string .= "\n\t" . T_('- Catalog Clean'); + if ($_SERVER['argv'][$x] == "-m") { + $operations_string .= "\n\t- " . T_('Temporary deactivate PHP memory limit'); + $tmpmemlimoff = 1; + } + elseif ($_SERVER['argv'][$x] == "-c") { + $operations_string .= "\n\t- " . T_('Cleaning catalog/s'); $catclean = 1; } elseif ($_SERVER['argv'][$x] == "-v") { - $operations_string .= "\n\t" . T_('- Catalog Verify'); + $operations_string .= "\n\t- " . T_('Verifying catalog/s'); $catverify = 1; } elseif ($_SERVER['argv'][$x] == "-a") { - $operations_string .= "\n\t" . T_('- Catalog Add'); + $operations_string .= "\n\t- " . T_('Adding new media to catalog/s'); $catadd = 1; } elseif ($_SERVER['argv'][$x] == "-g") { - $operations_string .= "\n\t" . T_('- Catalog Art Gather'); - $artadd = 1; + $operations_string .= "\n\t- " . T_('Gathering new media art'); + $artadd = 1; } elseif ($_SERVER['argv'][$x] == "-i") { - $operations_string .= "\n\t" . T_('- Playlist Import'); - $plimp = 1; + $operations_string .= "\n\t- " . T_('Importing playlist/s'); + $plimp = 1; + } + elseif ($_SERVER['argv'][$x] == "-o") { + $operations_string .= "\n\t- " . T_('Database table optimization'); + $optimizetables = 1; } else { if ($where) $where .= " OR "; @@ -70,19 +80,43 @@ if (count($_SERVER['argv']) > 1) { } if (count($_SERVER['argv']) != 1 AND $artadd != 1 && $catclean != 1 && $catverify != 1 && $catadd != 1) { - usage(); - exit; + usage(); + exit; } -if ($artadd == 0 && $catclean == 0 && $catverify == 0 && $catadd == 0) { //didn't pass any clean/verify/add arguments - $catclean = 1; //set them all to on +if ($catclean == 0 && $catverify == 0 && $catadd == 0 && $artadd == 0 && $optimizetables == 0) { //didn't pass any clean/verify/add arguments + $catclean = 1; //set them all to on $catverify = 1; $catadd = 1; $artadd = 1; + $optimizetables = 1; +} +echo "\n----------------------------------------------------------\n"; +echo T_("Starting catalog operations...") . $operations_string . "\n"; +echo "----------------------------------------------------------\n\n"; + +// -------- Options before the catalog actions loop +if ($tmpmemlimoff == 1) { + // Temporarily deactivate PHP memory limit + echo "\033[31m- " . T_("Deactivated PHP memory limit") . " -\033[0m\n"; + ini_set('memory_limit','-1'); + echo "------------------\n\n"; } -echo T_("Starting Catalog Operations...") . $operations_string . "\n"; - +$options = array(); // for $catadd +if ($artadd == 1) { + echo "- " . T_("Gathering art") . " - \n"; + $options['gather_art'] = true; + } else { + $options['gather_art'] = false; +} +if ($plimp == 1) { + echo "- " . T_("Parsing playlists") . " - \n"; + $options['parse_playlist'] = true; + } else { + $options['parse_playlist'] = false; +} +// -------- if ($where) $where = "($where) AND catalog_type='local'"; else $where = "catalog_type='local'"; $sql = "SELECT id FROM catalog"; @@ -94,51 +128,49 @@ ob_start("ob_html_strip",'1024',true); while ($row = Dba::fetch_row($db_results)) { $catalog = Catalog::create_from_id($row['0']); - printf(T_('Reading: %s'), $catalog->name); - ob_flush(); - echo "\n"; + printf(T_('Reading catalog: %s'), $catalog->name); + ob_flush(); + echo "\n"; + if ($catclean == 1) { // Clean out dead files - echo T_("- Starting Clean - "); + echo "- " . T_("Start cleaning orphaned media entries") . " - \n"; echo "\n"; $catalog->clean_catalog(); echo "------------------\n\n"; } - if ($catverify == 1) { // Verify Existing - echo T_("- Starting Verify - "); + echo "- " . T_("Start verifying media related to catalog entries") . " - \n"; echo "\n"; $catalog->verify_catalog($row['0']); echo "-------------------\n\n"; } - if ($catadd == 1) { // Look for new files - echo T_("- Starting Add - "); + echo "- " . T_("Start adding new media") . " - \n"; echo "\n"; - $options = array(); - if ($artadd == 1) { - $options['gather_art'] = true; - } - if ($plimp == 1) { - $options['parse_playlist'] = true; - } $catalog->add_to_catalog($options); echo "----------------\n\n"; - } elseif ($artadd == 1) { - // Look for album art - echo T_('Starting Album Art Search'); - echo "\n"; + } + elseif ($artadd == 1) { + // Look for media art + echo "- " . T_('Start searching new media art') . " - \n"; + echo "\n"; $catalog->gather_art(); echo "----------------\n\n"; - } + } } + if ($optimizetables == 1) { + // Optimize Database Tables + echo "- " . T_('Optimizing database tables') . " - \n"; + echo "\n"; + Dba::optimize_tables(); + echo "------------------\n\n"; + } -Dba::optimize_tables(); - -ob_end_flush(); -echo "\n"; +ob_end_flush(); +echo "\n"; function ob_html_strip($string) { @@ -154,22 +186,37 @@ function ob_html_strip($string) { } // ob_html_strip function usage() { - echo T_("- Catalog Update -"); echo "\n"; - echo T_("Usage: catalog_update.inc [CATALOG NAME] [-c|-v|-a|-g|-t|-i]"); - echo "\n\t"; - echo T_("Default behavior is to do all except playlist import"); + echo "----------------------------------------------------------\n\t\t"; + echo T_("- Catalog Update Help -"); + echo "\n\033[32m"; + echo T_("Usage: catalog_update.inc [CATALOG NAME] [-m|-c|-v|-a|-g|-i|-o]") . "\033[0m (\033[31m!\033[0m)"; + echo "\033[0m\n"; + echo "----------------------------------------------------------"; + echo "\n"; + echo T_("Default behavior is to do all except temporarily deactivate the php memory limit"); + echo "\n"; + echo "----------------------------------------------------------"; + echo "\n-m\t"; + echo T_('Temporarily deactivates PHP memory limit.') . " (\033[31m1\033[0m)"; echo "\n-c\t"; - echo T_('Clean Catalogs'); + echo T_('Cleans catalogs from orphaned entries.'); echo "\n-v\t"; - echo T_('Verify Catalogs'); + echo T_('Verifies catalog entries and updates them if related files have new information.'); echo "\n-a\t"; - echo T_('Add to Catalogs'); - echo "\n-i\t"; - echo T_('Import Playlists'); + echo T_('Adds new media to catalogs.'); echo "\n-g\t"; - echo T_('Gather Art'); + echo T_('Gathers media Art.'); + echo "\n-i\t"; + echo T_('Imports playlists.'); + echo "\n-o\t"; + echo T_('Optimizes Database Tables.') . " (\033[31m2\033[0m)"; echo "\n"; + echo "----------------------------------------------------------\033[31m\n"; + echo "1. " . T_('Use this option at your own risk! Your system could crash or become unresponsive due to huge memory consumption!') . "\n"; + echo "2. " . T_('Depending on your systems performance, this option could need a long time to finish and extremely slow down other database processes if you have big catalogs!') . "\n"; + echo "! " . T_('The switches [-m|-i|-o] can only be used, if one of the other switches [-c|-v|-a|-g] is used.'); + echo "\033[0m\n"; echo "----------------------------------------------------------"; echo "\n"; } diff --git a/sources/bin/channel_run.inc b/sources/bin/channel_run.inc index 8c691ea..dca4f14 100644 --- a/sources/bin/channel_run.inc +++ b/sources/bin/channel_run.inc @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -23,6 +23,8 @@ define('NO_SESSION','1'); define('CLI', 1); +$chunk_buffer = ''; +$nb_chunks_remainder = 0; $path = dirname(__FILE__); $prefix = realpath($path . '/../'); require_once $prefix . '/lib/init.php'; @@ -42,11 +44,11 @@ if ($cargv > 1) { if ($_SERVER['argv'][$x] == "-c" && ($x + 1) < $cargv) { $chanid = intval($_SERVER['argv'][++$x]); $operations_string .= "\n\t" . T_('- Channel ' . $chanid); - } + } elseif ($_SERVER['argv'][$x] == "-v") { $operations_string .= "\n\t" . T_('- Verbose'); $verbose = true; - } + } elseif ($_SERVER['argv'][$x] == "-p" && ($x + 1) < $cargv) { $port = intval($_SERVER['argv'][++$x]); $operations_string .= "\n\t" . T_('- Port ' . $port); @@ -56,7 +58,7 @@ if ($cargv > 1) { if ($chanid <= 0) { usage(); - exit; + exit; } // Transcode is mandatory to have consistent stream codec @@ -66,7 +68,7 @@ if ($transcode_cfg == 'never') { die('Cannot start channel, transcoding is mandatory to work.'); } -echo T_("Starting Channel...") . $operations_string . "\n"; +echo T_("Starting Channel...") . $operations_string . "\n"; $channel = new Channel($chanid); if (!$channel->id) { @@ -106,7 +108,7 @@ echo T_("Listening on ") . $address . ':' . $port . "\n"; $stream_clients = array(); $client_socks = array(); -$last_stream = 0; +$last_stream = microtime(true); while(true) { //prepare readable sockets @@ -114,7 +116,6 @@ while(true) if (count($client_socks) < $channel->max_listeners) { $read_socks[] = $server; } - //echo "b\n";ob_flush(); //start reading and use a large timeout if(stream_select ( $read_socks, $write, $except, 1)) @@ -123,7 +124,7 @@ while(true) if (in_array($server, $read_socks)) { $new_client = stream_socket_accept($server); - + if ($new_client) { debug_event('channel', 'Connection accepted from ' . stream_socket_get_name($new_client, true) . '.', '5'); @@ -133,288 +134,120 @@ while(true) echo "New client connected.\n"; ob_flush(); } - + //delete the server socket from the read sockets unset($read_socks[array_search($server, $read_socks)]); } - + // Get new message from existing client foreach($read_socks as $sock) { - $data = fread($sock, 1024); - if(!$data) - { - client_disconnect($channel, $client_socks, $stream_clients, $sock); - continue; - } - - $headers = explode("\n", $data); - - if (count($headers) > 0) { - $cmd = explode(" ", $headers[0]); - if ($cmd['0'] == 'GET') { - switch ($cmd['1']) { - case '/stream.' . $channel->stream_type: - $options = array( - 'socket' => $sock, - 'length' => 0 - ); - - for ($i = 1; $i < count($headers); $i++) { - $headerpart = explode(":", $headers[$i], 2); - $header = strtolower(trim($headerpart[0])); - $value = trim($headerpart[1]); - switch ($header) { - case 'icy-metadata': - $options['metadata'] = ($value == '1'); - $options['metadata_lastsent'] = 0; - $options['metadata_lastsong'] = 0; - break; - } - } - - // Stream request - if ($options['metadata']) { - //fwrite($sock, "ICY 200 OK\r\n"); - fwrite($sock, "HTTP/1.0 200 OK\r\n"); - } else { - fwrite($sock, "HTTP/1.1 200 OK\r\n"); - fwrite($sock, "Cache-Control: no-store, no-cache, must-revalidate\r\n"); - } - fwrite($sock, "Content-Type: " . Song::type_to_mime($transcode_to) . "\r\n"); - fwrite($sock, "Accept-Ranges: none\r\n"); - - $genre = $channel->get_genre(); - // Send Shoutcast metadata on demand - if ($options['metadata']) { - fwrite($sock, "icy-notice1: " . AmpConfig::get('title') . "\r\n"); - fwrite($sock, "icy-name: " . $channel->name . "\r\n"); - if (!empty($genre)) { - fwrite($sock, "icy-genre: " . $genre . "\r\n"); - } - fwrite($sock, "icy-url: " . $channel->url . "\r\n"); - fwrite($sock, "icy-pub: " . ($channel->is_private) ? '0' : '1' . "\r\n"); - if ($channel->bitrate) { - fwrite($sock, "icy-br: " . strval($channel->bitrate) . "\r\n"); - } - fwrite($sock, "icy-metaint: " . strval($metadata_interval) . "\r\n"); - } - // Send additional Icecast metadata - fwrite($sock, "x-audiocast-server-url: " . $channel->url . "\r\n"); - fwrite($sock, "x-audiocast-name: " . $channel->name . "\r\n"); - fwrite($sock, "x-audiocast-description: " . $channel->description . "\r\n"); - fwrite($sock, "x-audiocast-url: " . $channel->url . "\r\n"); - if (!empty($genre)) { - fwrite($sock, "x-audiocast-genre: " . $genre . "\r\n"); - } - fwrite($sock, "x-audiocast-bitrate: " . strval($channel->bitrate) . "\r\n"); - fwrite($sock, "x-audiocast-public: " . (($channel->is_private) ? "0" : "1") . "\r\n"); - - fwrite($sock, "\r\n"); - - // Add to stream clients list - $key = array_search($sock, $read_socks); - $stream_clients[$key] = $options; - break; - - case '/': - case '/status.xsl': - // Stream request - fwrite($sock, "HTTP/1.0 200 OK\r\n"); - fwrite($sock, "Cache-Control: no-store, no-cache, must-revalidate\r\n"); - fwrite($sock, "Content-Type: text/html\r\n"); - fwrite($sock, "\r\n"); - - // Create xsl structure - - // Header - $xsl = ""; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "Icecast Streaming Media Server - Ampache" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "
" . "\n"; - - // Content - $xsl .= "
" . "\n"; - $xsl .= "
" . "\n"; - $xsl .= "\"\"" . "\n"; - $xsl .= "
" . "\n"; - $xsl .= "
" . "\n"; - $xsl .= "
" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "

Mount Point /stream." . $channel->stream_type . "

" . "\n"; - $xsl .= "stream_type .".m3u\">M3U" . "\n"; - $xsl .= "
" . "\n"; - $xsl .= "
" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $genre = $channel->get_genre(); - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $currentsong = ""; - if ($channel->media) { - $currentsong = $channel->media->f_artist . " - " . $channel->media->f_title; - } - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "
Stream Title:" . $channel->name . "
Stream Description:" . $channel->description . "
Content Type:" . Song::type_to_mime($channel->stream_type) . "
Mount Start:" . date("c", $channel->start_date) . "
Bitrate:" . $channel->bitrate . "
Current Listeners:" . $channel->listeners . "
Peak Listeners:" . $channel->peak_listeners . "
Stream Genre:" . $genre . "
Stream URL:url . "\" target=\"_blank\">" . $channel->url . "
Current Song:" . $currentsong . "
" . "\n"; - $xsl .= "
" . "\n"; - $xsl .= "
" . "\n"; - $xsl .= "\"\"" . "\n"; - $xsl .= "
" . "\n"; - $xsl .= "
" . "\n"; - $xsl .= "

" . "\n"; - - // Footer - $xsl .= "
" . "\n"; - $xsl .= "Support Icecast development at www.icecast.org" . "\n"; - $xsl .= "
" . "\n"; - $xsl .= "
" . "\n"; - $xsl .= "" . "\n"; - $xsl .= "" . "\n"; - - fwrite($sock, $xsl); - - fclose($sock); - unset($client_socks[array_search($sock, $client_socks)]); - break; - - case '/style.css': - case '/favicon.ico': - case '/images/corner_bottomleft.jpg': - case '/images/corner_bottomright.jpg': - case '/images/corner_topleft.jpg': - case '/images/corner_topright.jpg': - case '/images/icecast.png': - case '/images/key.png': - case '/images/tunein.png': - // Get read file data - $fpath = AmpConfig::get('prefix') . '/channel' . $cmd['1']; - $pinfo = pathinfo($fpath); - - $content_type = 'text/html'; - switch ($pinfo['extension']) { - case 'css': - $content_type = "text/css"; - break; - case 'jpg': - $content_type = "image/jpeg"; - break; - case 'png': - $content_type = "image/png"; - break; - case 'ico': - $content_type = "image/vnd.microsoft.icon"; - break; - } - fwrite($sock, "HTTP/1.0 200 OK\r\n"); - fwrite($sock, "Content-Type: " . $content_type . "\r\n"); - $fdata = file_get_contents($fpath); - fwrite($sock, "Content-Length: " . strlen($fdata) . "\r\n"); - fwrite($sock, "\r\n"); - fwrite($sock, $fdata); - fclose($sock); - unset($client_socks[array_search($sock, $client_socks)]); - break; - case '/stream.' . $channel->stream_type . '.m3u': - fwrite($sock, "HTTP/1.0 200 OK\r\n"); - fwrite($sock, "Cache-control: public\r\n"); - fwrite($sock, "Content-Disposition: filename=stream." . $channel->stream_type . ".m3u\r\n"); - fwrite($sock, "Content-Type: audio/x-mpegurl\r\n"); - fwrite($sock, "\r\n"); - - fwrite($sock, $channel->get_stream_url() . "\n"); - - fclose($sock); - unset($client_socks[array_search($sock, $client_socks)]); - break; - default: - debug_event('channel', 'Unknown request. Closing connection.', '3'); - fclose($sock); - unset($client_socks[array_search($sock, $client_socks)]); - break; - } - } - } // Handle data parse + http_serve($channel, $client_socks, $stream_clients, $read_socks, $sock); } } - + if ($channel->bitrate) { + $time_offset = microtime(true) - $last_stream; - //$mtime = ($last_stream > 0 && $time_offset < 1000000) ? $time_offset : 1; - $mtime = 1; - if ($last_stream > 0 && $time_offset < 1) { - usleep(1000000 - ($time_offset * 1000000)); - } elseif ($last_stream > 0) { - //$mtime = $time_offset; + + //debug_event('channel', 'time_offset : '. $time_offset, '5'); + //debug_event('channel', 'last_stream: '.$last_stream, '5'); + + if ($time_offset < 1) + usleep(1000000 - ($time_offset * 1000000)); // always at least 1 second between cycles + + $last_stream = microtime(true); + $mtime = ($time_offset > 1) ? $time_offset : 1; + $nb_chunks = ceil(($mtime * ($channel->bitrate+1/100*$channel->bitrate) * 1000 / 8) / $channel->chunk_size); // channel->bitrate+1% ... leave some headroom for metadata / headers + + // we only send full blocks, save remainder and apply when appropriate: allows more granular/arbitrary average bitrates + if ($nb_chunks - ($mtime * ($channel->bitrate+1/100*$channel->bitrate) * 1000 / 8 / $channel->chunk_size) > 0) + $nb_chunks_remainder += $nb_chunks - ($mtime * $channel->bitrate * 1000 / 8 / $channel->chunk_size); + if ($nb_chunks >= 1 && $nb_chunks_remainder >= 1){ + $nb_chunks -= 1; + $nb_chunks_remainder -= 1; + //debug_event('channel', 'REMAINDER: '.$nb_chunks_remainder, '5'); } - $nb_chunks = ceil(($mtime * $channel->bitrate * 1000) / 4096); + //debug_event('channel', 'mtime '.$mtime, '5'); + //debug_event('channel', 'nb_chunks: '.$nb_chunks, '5'); + } else { $nb_chunks = 1; } - + // Get multiple chunks according to bitrate to return enough data per second (because sleep with socket select) for ($c = 0; $c < $nb_chunks; $c++) { + $chunk = $channel->get_chunk(); $chunklen = strlen($chunk); + $chunk_buffer .= $chunk; + + //buffer maintenance + while (strlen($chunk_buffer) > (15 * $nb_chunks * $channel->chunk_size) ){ // buffer 15 seconds + + if (strtolower($channel->stream_type) == "ogg" && strtohex(substr($chunk_buffer, 0, 4)) == "4F676753") { //maintain ogg chunk alignment --- "4F676753" == "OggS" + // read OggS segment length + $hex = strtohex(substr($chunk_buffer, 0, 27)); + $ogg_nr_of_segments = hexdec(substr($hex, 26*2, 2)); + $hex .= strtohex(substr($chunk_buffer, 27, $ogg_nr_of_segments)); + $ogg_sum_segm_laces = 0; + for($segm = 0; $segm < $ogg_nr_of_segments; $segm++){ + $ogg_sum_segm_laces += hexdec(substr($hex, 27*2 + $segm*2, 2)); + } + //$naive = strpos(substr($chunk_buffer, 4), 'OggS') + 4; // naive search for next header + //remove 1 whole OggS chunk + $chunk_buffer = substr($chunk_buffer, 27 + $ogg_nr_of_segments + $ogg_sum_segm_laces); + //debug_event('channel', '$new chunk buffer : '.substr($chunk_buffer,0,300) . ' $hex: '.strtohex(substr($chunk_buffer,0,600)) . ' $ogg_nr_of_segments: ' .$ogg_nr_of_segments . ' bytes cut off: '.(27 + $ogg_nr_of_segments + $ogg_sum_segm_laces) . ' naive: ' .$naive, '5'); + } elseif (strtolower($channel->stream_type) == "ogg") { + debug_event('channel', 'Ogg alignament broken! Trying repair...', '5'); + $manual_search = strpos($chunk_buffer, 'OggS'); + $chunk_buffer = substr($chunk_buffer, $manual_search); + } else { // no chunk alignment required + $chunk_buffer = substr($chunk_buffer, $chunklen); + } + //debug_event('channel', 'remvd chunk from buffer ', '5'); + } + if ($chunklen > 0) { foreach($stream_clients as $key => $client) { $sock = $client['socket']; + $clchunk = $chunk; + if(!is_resource($sock)) { client_disconnect($channel, $client_socks, $stream_clients, $sock); continue; } - - $clchunk = $chunk; + + if ($client['isnew'] == 1){ + $client['isnew'] = 0; + //fwrite($sock, $channel->header_chunk); + //debug_event('channel', 'IS NEW' . $channel->header_chunk, '5'); + $clchunk_buffer = $channel->header_chunk . $chunk_buffer; + if ($client['metadata']){ //stub + //if (strtolower($channel->stream_type) == "ogg") + while(strlen($clchunk_buffer) > $metadata_interval){ + fwrite($sock, substr($clchunk_buffer, 0, $metadata_interval) . chr(0x00)); + $clchunk_buffer = substr($clchunk_buffer, $metadata_interval); + } + fwrite($sock, $clchunk_buffer); + $client['metadata_lastsent'] = 0; + $client['length'] += strlen($clchunk_buffer); + } else { + //fwrite($sock, $channel->header_chunk); + $buffer_bytes_written = fwrite($sock, $clchunk_buffer); + while ($buffer_bytes_written != strlen($clchunk_buffer)){ + debug_event('channel', 'I HERPED WHEN I SHOULD HAVE DERPED!', '5'); + //debug_event('channel', 'chunk_buffer bytes written:' .$buffer_bytes_written .'strlen $chunk_buffer: '.strlen($chunk_buffer), '5'); + $clchunk_buffer = substr($clchunk_buffer, $buffer_bytes_written); + $buffer_bytes_written = fwrite($sock, $clchunk_buffer); + } + } + $stream_clients[$key] = $client; + continue; + } + // Check if we need to insert metadata information if ($client['metadata']) { $chkmdlen = ($client['length'] + $chunklen) - $client['metadata_lastsent']; @@ -437,24 +270,23 @@ while(true) $clchunk = substr($chunk, $subpos); } } + if (strlen($clchunk) > 0) { fwrite($sock, $clchunk); $client['length'] += strlen($clchunk); } - $stream_clients[$key] = $client; //debug_event('channel', 'Client stream current length: ' . $client['length'], '5'); } } else { $channel->update_listeners(0); + debug_event('channel', 'No more data, stream ended.', 5); die('No more data, stream ended.'); } - - $last_stream = microtime(true); } } -ob_end_flush(); +ob_end_flush(); echo "\n"; function client_disconnect($channel, &$client_socks, &$stream_clients, $sock) @@ -474,7 +306,7 @@ function usage() echo T_("- Channel Listening -"); echo "\n"; echo T_("Usage: channel_run.inc [-c {CHANNEL ID}|-p {PORT}|-v]"); - echo "\n\t"; + echo "\n\t"; echo "\n-c {CHANNEL ID}\t"; echo T_('Channel id to start'); echo "\n-p {PORT}\t"; @@ -486,4 +318,261 @@ function usage() echo "\n"; } +function http_serve($channel, &$client_socks, &$stream_clients, &$read_socks, $sock) +{ + $data = fread($sock, 1024); + if(!$data) + { + client_disconnect($channel, $client_socks, $stream_clients, $sock); + return; + } + + $headers = explode("\n", $data); + + if (count($headers) > 0) { + $cmd = explode(" ", $headers[0]); + if ($cmd['0'] == 'GET') { + switch ($cmd['1']) { + case '/stream.' . $channel->stream_type: + $options = array( + 'socket' => $sock, + 'length' => 0, + 'isnew' => 1 + ); + + //debug_event('channel', 'HTTP HEADERS: '.$data,'5'); + for ($i = 1; $i < count($headers); $i++) { + $headerpart = explode(":", $headers[$i], 2); + $header = strtolower(trim($headerpart[0])); + $value = trim($headerpart[1]); + switch ($header) { + case 'icy-metadata': + $options['metadata'] = ($value == '1'); + $options['metadata_lastsent'] = 0; + $options['metadata_lastsong'] = 0; + break; + } + } + + // Stream request + if ($options['metadata']) { + //$http = "ICY 200 OK\r\n"); + $http = "HTTP/1.0 200 OK\r\n"; + } else { + $http = "HTTP/1.1 200 OK\r\n"; + $http .= "Cache-Control: no-store, no-cache, must-revalidate\r\n"; + } + $http .= "Content-Type: " . Song::type_to_mime($channel->stream_type) . "\r\n"; + $http .= "Accept-Ranges: none\r\n"; + + $genre = $channel->get_genre(); + // Send Shoutcast metadata on demand + //if ($options['metadata']) { + $http .= "icy-notice1: " . AmpConfig::get('site_title') . "\r\n"; + $http .= "icy-name: " . $channel->name . "\r\n"; + if (!empty($genre)) { + $http .= "icy-genre: " . $genre . "\r\n"; + } + $http .= "icy-url: " . $channel->url . "\r\n"; + $http .= "icy-pub: " . (($channel->is_private) ? "0" : "1") . "\r\n"; + if ($channel->bitrate) { + $http .= "icy-br: " . strval($channel->bitrate) . "\r\n"; + } + global $metadata_interval; + $http .= "icy-metaint: " . strval($metadata_interval) . "\r\n"; + //} + // Send additional Icecast metadata + $http .= "x-audiocast-server-url: " . $channel->url . "\r\n"; + $http .= "x-audiocast-name: " . $channel->name . "\r\n"; + $http .= "x-audiocast-description: " . $channel->description . "\r\n"; + $http .= "x-audiocast-url: " . $channel->url . "\r\n"; + if (!empty($genre)) { + $http .= "x-audiocast-genre: " . $genre . "\r\n"; + } + $http .= "x-audiocast-bitrate: " . strval($channel->bitrate) . "\r\n"; + $http .= "x-audiocast-public: " . (($channel->is_private) ? "0" : "1") . "\r\n"; + + $http .= "\r\n"; + + fwrite($sock, $http); + + // Add to stream clients list + $key = array_search($sock, $read_socks); + $stream_clients[$key] = $options; + break; + + case '/': + case '/status.xsl': + // Stream request + fwrite($sock, "HTTP/1.0 200 OK\r\n"); + fwrite($sock, "Cache-Control: no-store, no-cache, must-revalidate\r\n"); + fwrite($sock, "Content-Type: text/html\r\n"); + fwrite($sock, "\r\n"); + + // Create xsl structure + + // Header + $xsl = ""; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "Icecast Streaming Media Server - Ampache" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "
" . "\n"; + + // Content + $xsl .= "
" . "\n"; + $xsl .= "
" . "\n"; + $xsl .= "\"\"" . "\n"; + $xsl .= "
" . "\n"; + $xsl .= "
" . "\n"; + $xsl .= "
" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "

Mount Point /stream." . $channel->stream_type . "

" . "\n"; + $xsl .= "stream_type .".m3u\">M3U" . "\n"; + $xsl .= "
" . "\n"; + $xsl .= "
" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $genre = $channel->get_genre(); + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $currentsong = ""; + if ($channel->media) { + $currentsong = $channel->media->f_artist . " - " . $channel->media->f_title; + } + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "
Stream Title:" . $channel->name . "
Stream Description:" . $channel->description . "
Content Type:" . Song::type_to_mime($channel->stream_type) . "
Mount Start:" . date("c", $channel->start_date) . "
Bitrate:" . $channel->bitrate . "
Current Listeners:" . $channel->listeners . "
Peak Listeners:" . $channel->peak_listeners . "
Stream Genre:" . $genre . "
Stream URL:url . "\" target=\"_blank\">" . $channel->url . "
Current Song:" . $currentsong . "
" . "\n"; + $xsl .= "
" . "\n"; + $xsl .= "
" . "\n"; + $xsl .= "\"\"" . "\n"; + $xsl .= "
" . "\n"; + $xsl .= "
" . "\n"; + $xsl .= "

" . "\n"; + + // Footer + $xsl .= "
" . "\n"; + $xsl .= "Support Ampache at www.ampache.org" . "\n"; + $xsl .= "
" . "\n"; + $xsl .= "
" . "\n"; + $xsl .= "" . "\n"; + $xsl .= "" . "\n"; + + fwrite($sock, $xsl); + + fclose($sock); + unset($client_socks[array_search($sock, $client_socks)]); + break; + + case '/style.css': + case '/favicon.ico': + case '/images/corner_bottomleft.jpg': + case '/images/corner_bottomright.jpg': + case '/images/corner_topleft.jpg': + case '/images/corner_topright.jpg': + case '/images/icecast.png': + case '/images/key.png': + case '/images/tunein.png': + // Get read file data + $fpath = AmpConfig::get('prefix') . '/channel' . $cmd['1']; + $pinfo = pathinfo($fpath); + + $content_type = 'text/html'; + switch ($pinfo['extension']) { + case 'css': + $content_type = "text/css"; + break; + case 'jpg': + $content_type = "image/jpeg"; + break; + case 'png': + $content_type = "image/png"; + break; + case 'ico': + $content_type = "image/vnd.microsoft.icon"; + break; + } + fwrite($sock, "HTTP/1.0 200 OK\r\n"); + fwrite($sock, "Content-Type: " . $content_type . "\r\n"); + $fdata = file_get_contents($fpath); + fwrite($sock, "Content-Length: " . strlen($fdata) . "\r\n"); + fwrite($sock, "\r\n"); + fwrite($sock, $fdata); + fclose($sock); + unset($client_socks[array_search($sock, $client_socks)]); + break; + case '/stream.' . $channel->stream_type . '.m3u': + fwrite($sock, "HTTP/1.0 200 OK\r\n"); + fwrite($sock, "Cache-control: public\r\n"); + fwrite($sock, "Content-Disposition: filename=stream." . $channel->stream_type . ".m3u\r\n"); + fwrite($sock, "Content-Type: audio/x-mpegurl\r\n"); + fwrite($sock, "\r\n"); + + fwrite($sock, $channel->get_stream_url() . "\n"); + + fclose($sock); + unset($client_socks[array_search($sock, $client_socks)]); + break; + default: + debug_event('channel', 'Unknown request. Closing connection.', '3'); + fclose($sock); + unset($client_socks[array_search($sock, $client_socks)]); + break; + } + } + } +} + +function strtohex($x) { + $s=''; + foreach(str_split($x) as $c) $s.=sprintf("%02X",ord($c)); + return($s); +} + ?> diff --git a/sources/bin/delete_disabled.inc b/sources/bin/delete_disabled.inc index 38601c2..f8932bf 100644 --- a/sources/bin/delete_disabled.inc +++ b/sources/bin/delete_disabled.inc @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/bin/dump_album_art.inc b/sources/bin/dump_album_art.inc index 8238975..e385eec 100644 --- a/sources/bin/dump_album_art.inc +++ b/sources/bin/dump_album_art.inc @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/bin/fix_filenames.inc b/sources/bin/fix_filenames.inc index 5aef3b3..8727235 100644 --- a/sources/bin/fix_filenames.inc +++ b/sources/bin/fix_filenames.inc @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -75,7 +75,7 @@ while ($row = Dba::fetch_assoc($db_results)) { } // end of the catalogs -echo T_('Finished checking filenames for valid chacters'); +echo T_('Finished checking file names for valid characters'); echo "\n"; /************************************************** @@ -191,13 +191,13 @@ function charset_rename_file($full_file,$translated_filename) { $results = copy($full_file,$translated_filename); if (!$results) { - echo T_('Error: Copy Failed, not deleteing old file'); + echo T_('Error: Copy Failed, not deleting old file'); echo "\n"; return false; } - $old_sum = filesize($full_file); - $new_sum = filesize($translated_filename); + $old_sum = Core::get_filesize($full_file); + $new_sum = Core::get_filesize($translated_filename); if ($old_sum != $new_sum OR !$new_sum) { printf (T_('Error: Size Inconsistency, not deleting %s'), $full_file); diff --git a/sources/bin/install/.htaccess b/sources/bin/install/.htaccess deleted file mode 100644 index 3a42882..0000000 --- a/sources/bin/install/.htaccess +++ /dev/null @@ -1 +0,0 @@ -Deny from all diff --git a/sources/bin/install/add_user.inc b/sources/bin/install/add_user.inc index 570afe3..dd1bae1 100644 --- a/sources/bin/install/add_user.inc +++ b/sources/bin/install/add_user.inc @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/bin/install/install_db.inc b/sources/bin/install/install_db.inc index bd1fb9f..2ec1816 100644 --- a/sources/bin/install/install_db.inc +++ b/sources/bin/install/install_db.inc @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/bin/install/update_db.inc b/sources/bin/install/update_db.inc index 997558d..e32f22b 100644 --- a/sources/bin/install/update_db.inc +++ b/sources/bin/install/update_db.inc @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/bin/migrate_config.inc b/sources/bin/migrate_config.inc index 55af8b8..4060a8f 100644 --- a/sources/bin/migrate_config.inc +++ b/sources/bin/migrate_config.inc @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/bin/print_tags.inc b/sources/bin/print_tags.inc index 3890975..243fc6e 100644 --- a/sources/bin/print_tags.inc +++ b/sources/bin/print_tags.inc @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -53,7 +53,7 @@ $catalog = Catalog::create_from_id($results['id']); $dir_pattern = $catalog->sort_pattern; $file_pattern = $catalog->rename_pattern; -$info = new vainfo($filename, '', '', '', $dir_pattern, $file_pattern); +$info = new vainfo($filename, array('music'), '', '', '', $dir_pattern, $file_pattern); if(isset($dir_pattern) || isset($file_pattern)) { printf(T_('Using: %s AND %s for file pattern matching'), $dir_pattern, $file_pattern); print "\n"; diff --git a/sources/bin/sort_files.inc b/sources/bin/sort_files.inc index ff449c7..1bf993f 100644 --- a/sources/bin/sort_files.inc +++ b/sources/bin/sort_files.inc @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -65,7 +65,6 @@ while ($r = Dba::fetch_row($db_results)) { foreach ($songs as $song) { /* Find this poor song a home */ $song->format(); - $song->format_pattern(); $directory = sort_find_home($song,$catalog->sort_pattern,$catalog->path); $filename = $song->f_file; $fullpath = $directory . "/" . $filename; @@ -268,12 +267,12 @@ function sort_move_file($song,$fullname) { /* Look for the folder art and copy that as well */ if (!AmpConfig::get('album_art_preferred_filename') OR strstr(AmpConfig::get('album_art_preferred_filename'),"%")) { - $folder_art = $directory . '/folder.jpg'; - $old_art = $old_dir . '/folder.jpg'; + $folder_art = $directory . DIRECTORY_SEPARATOR . 'folder.jpg'; + $old_art = $old_dir . DIRECTORY_SEPARATOR . 'folder.jpg'; } else { - $folder_art = $directory . "/" . sort_clean_name(AmpConfig::get('album_art_preferred_filename')); - $old_art = $old_dir . "/" . sort_clean_name(AmpConfig::get('album_art_preferred_filename')); + $folder_art = $directory . DIRECTORY_SEPARATOR . sort_clean_name(AmpConfig::get('album_art_preferred_filename')); + $old_art = $old_dir . DIRECTORY_SEPARATOR . sort_clean_name(AmpConfig::get('album_art_preferred_filename')); } debug_event('copy_art','Copied ' . $old_art . ' to ' . $folder_art,'5'); @@ -282,8 +281,8 @@ function sort_move_file($song,$fullname) { if (!$results) { printf (T_('Error: Unable to copy file to %s'), $fullname); echo "\n"; return false; } /* Check the filesize */ - $new_sum = filesize($fullname); - $old_sum = filesize($song->file); + $new_sum = Core::get_filesize($fullname); + $old_sum = Core::get_filesize($song->file); if ($new_sum != $old_sum OR !$new_sum) { printf (T_('Error: Size Inconsistency, not deleting %s'), $song->file); diff --git a/sources/bin/websocket_run.inc b/sources/bin/websocket_run.inc index 4bf2d7b..56718d2 100644 --- a/sources/bin/websocket_run.inc +++ b/sources/bin/websocket_run.inc @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/bin/write_playlists.inc b/sources/bin/write_playlists.inc index b952ee6..c585186 100644 --- a/sources/bin/write_playlists.inc +++ b/sources/bin/write_playlists.inc @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -35,7 +35,7 @@ else { // Make sure the output dir is valid and writeable if (!is_writeable($dirname)) { - printf (T_('Error: Directory %s not writeable'), $dirname); + printf (T_('Error: Directory %s is not writable'), $dirname); echo "\n"; } diff --git a/sources/broadcast.php b/sources/broadcast.php index 2328216..d81d06c 100644 --- a/sources/broadcast.php +++ b/sources/broadcast.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -22,12 +22,16 @@ require_once 'lib/init.php'; +if (!AmpConfig::get('broadcast')) { + UI::access_denied(); + exit; +} + UI::show_header(); /* Switch on the action passed in */ switch ($_REQUEST['action']) { case 'show_delete': - UI::show_header(); $id = $_REQUEST['id']; $next_url = AmpConfig::get('web_path') . '/broadcast.php?action=delete&id=' . scrub_out($id); @@ -40,7 +44,6 @@ switch ($_REQUEST['action']) { exit; } - UI::show_header(); $id = $_REQUEST['id']; $broadcast = new Broadcast($id); if ($broadcast->delete()) { diff --git a/sources/browse.php b/sources/browse.php index 01a8c1b..8cfd61f 100644 --- a/sources/browse.php +++ b/sources/browse.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -49,6 +49,14 @@ switch ($_REQUEST['action']) { case 'song': case 'channel': case 'broadcast': + case 'tvshow': + case 'tvshow_season': + case 'tvshow_episode': + case 'movie': + case 'clip': + case 'personal_video': + case 'label': + case 'pvmsg': $browse->set_type($_REQUEST['action']); $browse->set_simple_browse(true); break; @@ -56,6 +64,9 @@ switch ($_REQUEST['action']) { UI::show_header(); +// Browser is able to save page on current session. Only applied to main menus. +$browse->set_update_session(true); + switch ($_REQUEST['action']) { case 'file': break; @@ -65,20 +76,22 @@ switch ($_REQUEST['action']) { $browse->set_filter('catalog_enabled', '1'); } $browse->set_sort('name','ASC'); + $browse->update_browse_from_session(); // Update current index depending on what is in session. $browse->show_objects(); break; case 'tag': //FIXME: This whole thing is ugly, even though it works. $browse->set_sort('count','ASC'); // This one's a doozy + $browse_type = isset($_REQUEST['type']) ? $_REQUEST['type'] : 'song'; $browse->set_simple_browse(false); - $browse->save_objects(Tag::get_tags(/*AmpConfig::get('offset_limit')*/)); // Should add a pager? + $browse->save_objects(Tag::get_tags($browse_type, 0, 'name')); // Should add a pager? $object_ids = $browse->get_saved(); $keys = array_keys($object_ids); Tag::build_cache($keys); UI::show_box_top(T_('Tag Cloud'), 'box box_tag_cloud'); $browse2 = new Browse(); - $browse2->set_type('song'); + $browse2->set_type($browse_type); $browse2->store(); require_once AmpConfig::get('prefix') . '/templates/show_tagcloud.inc.php'; UI::show_box_bottom(); @@ -91,6 +104,7 @@ switch ($_REQUEST['action']) { $browse->set_filter('catalog_enabled', '1'); } $browse->set_sort('name','ASC'); + $browse->update_browse_from_session(); $browse->show_objects(); break; case 'song': @@ -99,6 +113,7 @@ switch ($_REQUEST['action']) { $browse->set_filter('catalog_enabled', '1'); } $browse->set_sort('title','ASC'); + $browse->update_browse_from_session(); $browse->show_objects(); break; case 'live_stream': @@ -106,6 +121,7 @@ switch ($_REQUEST['action']) { $browse->set_filter('catalog_enabled', '1'); } $browse->set_sort('name','ASC'); + $browse->update_browse_from_session(); $browse->show_objects(); break; case 'catalog': @@ -114,19 +130,23 @@ switch ($_REQUEST['action']) { case 'playlist': $browse->set_sort('type','ASC'); $browse->set_filter('playlist_type','1'); + $browse->update_browse_from_session(); $browse->show_objects(); break; case 'smartplaylist': $browse->set_sort('type', 'ASC'); $browse->set_filter('playlist_type','1'); + $browse->update_browse_from_session(); $browse->show_objects(); break; case 'channel': $browse->set_sort('id', 'ASC'); + $browse->update_browse_from_session(); $browse->show_objects(); break; case 'broadcast': $browse->set_sort('id', 'ASC'); + $browse->update_browse_from_session(); $browse->show_objects(); break; case 'video': @@ -134,8 +154,54 @@ switch ($_REQUEST['action']) { $browse->set_filter('catalog_enabled', '1'); } $browse->set_sort('title','ASC'); + $browse->update_browse_from_session(); $browse->show_objects(); break; + case 'tvshow': + if (AmpConfig::get('catalog_disable')) { + $browse->set_filter('catalog_enabled', '1'); + } + $browse->set_sort('name','ASC'); + $browse->update_browse_from_session(); + $browse->show_objects(); + break; + case 'tvshow_season': + if (AmpConfig::get('catalog_disable')) { + $browse->set_filter('catalog_enabled', '1'); + } + $browse->set_sort('season_number','ASC'); + $browse->update_browse_from_session(); + $browse->show_objects(); + break; + case 'tvshow_episode': + case 'movie': + case 'clip': + case 'personal_video': + if (AmpConfig::get('catalog_disable')) { + $browse->set_filter('catalog_enabled', '1'); + } + $browse->update_browse_from_session(); + $browse->show_objects(); + break; + case 'label': + if (AmpConfig::get('catalog_disable')) { + $browse->set_filter('catalog_enabled', '1'); + } + $browse->set_sort('name','ASC'); + $browse->update_browse_from_session(); + $browse->show_objects(); + break; + case 'pvmsg': + $browse->set_sort('creation_date','DESC'); + $folder = $_REQUEST['folder']; + if ($folder === "sent") { + $browse->set_filter('user', $GLOBALS['user']->id); + } else { + $browse->set_filter('to_user', $GLOBALS['user']->id); + } + $browse->update_browse_from_session(); + $browse->show_objects(); + break; default: break; diff --git a/sources/channel.php b/sources/channel.php index c58854d..ddaa125 100644 --- a/sources/channel.php +++ b/sources/channel.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -22,13 +22,16 @@ require_once 'lib/init.php'; +if (!AmpConfig::get('channel')) { + UI::access_denied(); + exit; +} + UI::show_header(); /* Switch on the action passed in */ switch ($_REQUEST['action']) { case 'show_create': - UI::show_header(); - $type = Channel::format_type($_REQUEST['type']); if (!empty($type) && !empty($_REQUEST['id'])) { $object = new $type($_REQUEST['id']); @@ -50,7 +53,6 @@ switch ($_REQUEST['action']) { exit; } - UI::show_header(); $created = Channel::create($_REQUEST['name'], $_REQUEST['description'], $_REQUEST['url'], $_REQUEST['type'], $_REQUEST['id'], $_REQUEST['interface'], $_REQUEST['port'], $_REQUEST['admin_password'], $_REQUEST['private'] ?: 0, $_REQUEST['max_listeners'], $_REQUEST['random'] ?: 0, $_REQUEST['loop'] ?: 0, $_REQUEST['stream_type'], $_REQUEST['bitrate']); if (!$created) { @@ -62,7 +64,6 @@ switch ($_REQUEST['action']) { UI::show_footer(); exit; case 'show_delete': - UI::show_header(); $id = $_REQUEST['id']; $next_url = AmpConfig::get('web_path') . '/channel.php?action=delete&id=' . scrub_out($id); @@ -75,7 +76,6 @@ switch ($_REQUEST['action']) { exit; } - UI::show_header(); $id = $_REQUEST['id']; $channel = new Channel($id); if ($channel->delete()) { diff --git a/sources/channel/.htaccess b/sources/channel/.htaccess.dist similarity index 57% rename from sources/channel/.htaccess rename to sources/channel/.htaccess.dist index 0eb7988..e60d35c 100644 --- a/sources/channel/.htaccess +++ b/sources/channel/.htaccess.dist @@ -2,5 +2,5 @@ RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-s - RewriteRule ^([0-9]+)/(.*)$ index.php?channel=$1&target=$2 [PT,L,QSA] + RewriteRule ^([0-9]+)/(.*)$ /channel/index.php?channel=$1&target=$2 [PT,L,QSA] \ No newline at end of file diff --git a/sources/channel/index.php b/sources/channel/index.php index 4f061a1..0a9386b 100644 --- a/sources/channel/index.php +++ b/sources/channel/index.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/channel/style.css b/sources/channel/style.css index 7a10708..cae79e4 100644 --- a/sources/channel/style.css +++ b/sources/channel/style.css @@ -76,10 +76,6 @@ html, body { margin-bottom: 10px; background: url(images/icecast.png) no-repeat left center; } -.main iframe { - width: 100%; - border: 0; -} .news { font-family: Verdana, sans-serif; text-decoration: none; diff --git a/sources/config/.gitignore b/sources/config/.gitignore deleted file mode 100644 index 654ab29..0000000 --- a/sources/config/.gitignore +++ /dev/null @@ -1 +0,0 @@ -ampache.cfg.php diff --git a/sources/config/.htaccess b/sources/config/.htaccess index 896fbc5..cfd4051 100644 --- a/sources/config/.htaccess +++ b/sources/config/.htaccess @@ -1,2 +1,10 @@ -Order deny,allow -Deny from all \ No newline at end of file +# Apache 2.4 + + Require all denied + + +# Apache 2.2 + + Order deny,allow + Deny from all + \ No newline at end of file diff --git a/sources/config/ampache.cfg.php.dist b/sources/config/ampache.cfg.php.dist index 07a0102..c27949a 100644 --- a/sources/config/ampache.cfg.php.dist +++ b/sources/config/ampache.cfg.php.dist @@ -4,35 +4,41 @@ ;################### ; This value is used to detect quickly -; if this config file is up to date +; if this config file is up to date ; this is compared against a value hard-coded ; into the init script -config_version = 16 +config_version = 29 ;################### ; Path Vars # ;################### -; The http host of your server. +; The public http host of your server. ; If not set, retrieved automatically from client request. ; This setting is required for WebSocket server ; DEFAULT: "" ;http_host = "localhost" -; The path to your ampache install -; Do not put a trailing / on this path +; The public path to your ampache install +; Do not put a trailing / on this path ; For example if your site is located at http://localhost ; than you do not need to enter anything for the web_path -; if it is located at http://localhost/music you need to +; if it is located at http://localhost/music you need to ; set web_path to /music ; DEFAULT: "" ;web_path = "" +; The local http url of your server. +; If not set, retrieved automatically from server information. +; DEFAULT: "" +;local_web_path = "http://localhost/ampache" + ;############################## ; Session and Login Variables # ;############################## ; Hostname of your database +; For socket authentication, set the path to socket file (e.g. /var/run/mysqld/mysqld.sock) ; DEFAULT: localhost database_hostname = localhost @@ -50,27 +56,31 @@ database_username = username ; Password for your ampache database, this can not be blank ; this is a 'forced' security precaution, the default value -; will not work +; will not work (except if using socket authentication) ; DEFAULT: "" database_password = password +; Cryptographic secret +; This MUST BE changed with your own secret key. Ampache-specific, just pick any random string you want. +secret_key = "abcdefghijklmnoprqstuvwyz0123456" + ; Length that a session will last expressed in seconds. Default is -; one hour. +; one hour. ; DEFAULT: 3600 session_length = 3600 ; Length that the session for a single streaming instance will last -; the default is two hours. With some clients, and long songs this can +; the default is two hours. With some clients, and long songs this can ; cause playback to stop, increase this value if you experience that ; DEFAULT: 7200 stream_length = 7200 -; This length defines how long a 'remember me' session and cookie will -; last, the default is 7200, same as length. It is up to the administrator -; of the box to increase this, for reference 86400 = 1 day -; 604800 = 1 week and 2419200 = 1 month -; DEFAULT: 86400 -remember_length = 86400 +; This length defines how long a 'remember me' session and cookie will +; last, the default is 86400, same as length. It is up to the administrator +; of the box to increase this, for reference 86400 = 1 day, +; 604800 = 1 week, and 2419200 = 1 month +; DEFAULT: 604800 +remember_length = 604800 ; Name of the Session/Cookie that will sent to the browser ; default should be fine @@ -78,16 +88,16 @@ remember_length = 86400 session_name = ampache ; Lifetime of the Cookie, 0 == Forever (until browser close) , otherwise in terms of seconds -; If you want cookies to last past a browser close set this to a value in seconds. +; If you want cookies to last past a browser close set this to a value in seconds. ; DEFAULT: 0 session_cookielife = 0 ; Is the cookie a "secure" cookie? This should only be set to 1 (true) if you are -; running a secure site (HTTPS). +; running a secure site (HTTPS). ; DEFAULT: 0 session_cookiesecure = 0 -; Auth Methods +; Auth Methods ; This defines which auth methods Auth will attempt to use and in which order. ; If auto_create isn't enabled the user must exist locally. ; DEFAULT: mysql @@ -108,7 +118,7 @@ auth_methods = "mysql" ;auth_password_save = "false" ; Logout redirection target -; Defaults to our own login.php, but we can override it here if, for instance, +; Defaults to our own login.php, but we can override it here if, for instance, ; we want to redirect to an SSO provider instead. ; logout_redirect = "http://sso.example.com/logout" @@ -120,15 +130,15 @@ auth_methods = "mysql" ; This defines which file types Ampache will attempt to catalog ; You can specify any file extension you want in here separating them ; with a | -; DEFAULT: mp3|mpc|m4p|m4a|mp4|aac|ogg|rm|wma|asf|flac|spx|ra|ape|shn|wv -catalog_file_pattern = "mp3|mpc|m4p|m4a|mp4|aac|ogg|rm|wma|asf|flac|spx|ra|ape|shn|wv" +; DEFAULT: mp3|mpc|m4p|m4a|aac|ogg|oga|wav|aif|aiff|rm|wma|asf|flac|opus|spx|ra|ape|shn|wv +catalog_file_pattern = "mp3|mpc|m4p|m4a|aac|ogg|oga|wav|aif|aiff|rm|wma|asf|flac|opus|spx|ra|ape|shn|wv" ; Video Pattern ; This defines which video file types Ampache will attempt to catalog ; You can specify any file extension you want in here seperating them with ; a | but ampache may not be able to parse them -; DEAFULT: avi|mpg|flv|m4v|webm -catalog_video_pattern = "avi|mpg|flv|m4v|webm" +; DEAFULT: avi|mpg|mpeg|flv|m4v|mp4|webm|mkv|wmv|ogv|mov|divx|m2ts +catalog_video_pattern = "avi|mpg|mpeg|flv|m4v|mp4|webm|mkv|wmv|ogv|mov|divx|m2ts" ; Playlist Pattern ; This defines which playlist types Ampache will attempt to catalog @@ -149,11 +159,11 @@ catalog_prefix_pattern = "The|An|A|Die|Das|Ein|Eine|Les|Le|La" ; DEFAULT: false ;catalog_disable = "false" -; Use Access List +; Use Access List ; Toggle this on if you want ampache to pay attention to the access list -; and only allow streaming/downloading/api-rpc from known hosts api-rpc +; and only allow streaming/downloading/api-rpc from known hosts api-rpc ; will not work without this on. -; NOTE: Default Behavior is DENY FROM ALL +; NOTE: Default Behavior is DENY FROM ALL ; DEFAULT: true access_control = "true" @@ -161,7 +171,7 @@ access_control = "true" ; If this is set to true ampache will make sure that the URL passed when ; attempting to retrieve a song contains a valid Session ID This prevents ; others from guessing URL's. This setting is ignored if you have use_auth -; disabled. +; disabled. ; DEFAULT: true require_session = "true" @@ -170,7 +180,7 @@ require_session = "true" ; is passed even on hosts defined in the Local Network ACL. This setting ; has no effect if access_control is not enabled ; DEFAULT: true -require_localnet_session = "true" +require_localnet_session = "true" ; Multiple Logins ; Added by Vlet 07/25/07 @@ -188,62 +198,63 @@ require_localnet_session = "true" ; Track User IPs ; If this is enabled Ampache will log the IP of every completed login -; it will store user,ip,time at one row per login. The results are +; it will store user,ip,time at one row per login. The results are ; displayed in Admin --> Users ; DEFAULT: false ;track_user_ip = "false" ; User IP Cardinality ; This defines how many days worth of IP history Ampache will track -; As it is one row per login on high volume sites you will want to -; clear it every now and then. +; As it is one row per login on high volume sites you will want to +; clear it every now and then. ; DEFAULT: 42 days ;user_ip_cardinality = "42" ; Allow Zip Download ; This setting allows/disallows using zlib to zip up an entire ; playlist/album for download. Even if this is turned on you will -; still need to enabled downloading for the specific user you +; still need to enabled downloading for the specific user you ; want to be able to use this function ; DEFAULT: false ;allow_zip_download = "false" -; File Zip Download -; This settings tells Ampache to attempt to save the zip file -; to the filesystem instead of creating it in memory, you must -; also set tmp_dir_path in order for this to work -; DEFAULT: false -;file_zip_download = "false" +Allow Zip Types +; This setting allows/disallows zip download of specific object types +; If empty, all supported object types can be zipped. +; Otherwise, only the given object list can be zipped. +; POSSIBLE VALUES: artist, album, playlist, search, tmp_playlist +; DEFAULT: none +;allow_zip_types = "album" ; File Zip Comment ; This is an optional configuration option that adds a comment ; to your zip files, this only applies if you've got allow_zip_downloads ; DEFAULT: Ampache - Zip Batch Download -;file_zip_comment = "Ampache - Zip Batch Download" +;file_zip_comment = "Ampache - Zip Batch Download" ; Waveform ; This settings tells Ampache to attempt to generate a waveform ; for each song. It requires transcode and encode_args_wav settings. ; You must also set tmp_dir_path in order for this to work ; DEFAULT: false -;waveform = "false" +;waveform = "false" ; Waveform color ; The waveform color. ; DEFAULT: #FF0000 -;waveform_color = "#FF0000" +;waveform_color = "#FF0000" ; Temporary Directory Path -; If File Zip Download or Waveform is enabled this must be set to tell +; If Waveform is enabled this must be set to tell ; Ampache which directory to save the temporary file to. Do not put a ; trailing slash or this will not work. ; DEFAULT: false ;tmp_dir_path = "false" ; This setting throttles a persons downloading to the specified -; bytes per second. This is not a 100% guaranteed function, and +; bytes per second. This is not a 100% guaranteed function, and ; you should really use a server based rate limiter if you want -; to do this correctly. +; to do this correctly. ; DEFAULT: off ; VALUES: any whole number (in bytes per second) ;throttle_download = 10 @@ -261,13 +272,52 @@ getid3_tag_order = "id3v2,id3v1,vorbiscomment,quicktime,matroska,ape,asf,avi,mpe ; DEFAULT: false ;getid3_detect_id3v2_encoding = "false" +; This determines if file metadata should be write back to files +; as id3 metadata when updated. +; DEFAULT: false +;write_id3 = "false" + +; This determines if album art should be write back to files +; as id3 metadata when updated. +; DEFAULT: false +;write_id3_art = "false" + +; This determines if catalog manager users can delete medias from disk. +; DEFAULT: false +;delete_from_disk = "false" + ; This determines the order in which metadata sources are used (and in the ; case of plugins, checked) ; POSSIBLE VALUES (builtins): filename and getID3 -; POSSIBLE VALUES (plugins): MusicBrainz, plus any others you've installed. +; POSSIBLE VALUES (plugins): MusicBrainz,TheAudioDb, plus any others you've installed. ; DEFAULT: getID3 filename metadata_order = "getID3,filename" +; This determines the order in which metadata sources are used (and in the +; case of plugins, checked) for video files +; POSSIBLE VALUES (builtins): filename and getID3 +; POSSIBLE VALUES (plugins): Tvdb,Tmdb,Omdb, plus any others you've installed. +; DEFAULT: filename getID3 +metadata_order_video = "filename,getID3" + +; This determines if extended metadata grabbed from external services should be deferred. +; If enabled, extended metadata is retrieved when browsing the library item. +; If disabled, extended metadata is retrieved at catalog update. +; Today, only Artist information (summary, place formed, ...) can be deferred. +; DEFAULT: true +deferred_ext_metadata = "true" + +; Some taggers use delimiters other than \0 for fields +; This list specifies possible delimiters additional to \0 +; This setting takes a regex pattern. +; DEFAULT: // / \ | , ; +additional_genre_delimiters = "[/]{2}|[/|\\\\|\|,|;]" + +; This determines if a preview image should be retrieved from video files +; It requires encode_get_image transcode settings. +; DEFAULT: false +;generate_video_preview = "true" + ; Un comment if don't want ampache to follow symlinks ; DEFAULT: false ;no_symlinks = "false" @@ -284,10 +334,10 @@ use_auth = "true" ; If use_auth is set to false then this option is used ; to determine the permission level of the 'default' users ; default is administrator. This setting only takes affect -; if use_auth if false +; if use_auth is false ; POSSIBLE VALUES: user, admin, manager, guest -; DEFAULT: admin -default_auth_level = "admin" +; DEFAULT: guest +default_auth_level = "guest" ; 5 Star Ratings ; This allows ratings for almost any object in ampache @@ -310,14 +360,14 @@ directplay = "true" ; Sociable ; This turns on / off all of the "social" features of ampache ; default is on, but if you don't care and just want music -; turn this off to disable all social features. +; turn this off to disable all social features. ; DEFAULT: true sociable = "true" -; Notify -; This turns on / off all Ampache notifications -; DEFAULT: true -notify = "true" +; License +; This turns on / off all licensing features on Ampache +; DEFAULT: false +;licensing = "false" ; This options will turn on/off Demo Mode ; If Demo mode is on you can not play songs or update your catalog @@ -332,7 +382,7 @@ notify = "true" ; requirments on larger catalogs. If you have the memory this can create ; a 2-3x speed improvement. ; DEFAULT: false -;memory_cache = false +;memory_cache = "false" ; Memory Limit ; This defines the "Min" memory limit for PHP if your php.ini @@ -347,7 +397,43 @@ notify = "true" ; Especially useful if you have a front and a back image in a folder ; comment out if ampache should search for any jpg,gif or png ; DEFAULT: folder.jpg -;album_art_preferred_filename = "folder.jpg" +;album_art_preferred_filename = "folder.jpg" + +; Album Art Store on Disk +; This defines if arts should be stored on disk instead of database. +; DEFAULT: false +;album_art_store_disk = "false" + +; Local Metadata Directory +; This define a local metadata directory with write access where to store +; heavy data if enabled (album arts, ...) +; DEFAULT: none +;local_metadata_dir = "/metadata" + +; Maximal upload size +; Specify the maximal allowed upload size for images, in bytes. +; DEFAULT: 1048576 +;max_upload_size = 1048576 + +; Album Art Minimum Width +; Specify the minimum width for arts (in pixel). +; DEFAULT: none +;album_art_min_width = 100 + +; Album Art Maximum Width +; Specify the maximum width for arts (in pixel). +; DEFAULT: none +;album_art_max_width = 1024 + +; Album Art Minimum Height +; Specify the minimum height for arts (in pixel). +; DEFAULT: none +;album_art_min_height = 100 + +; Album Art Maximum Height +; Specify the maximum height for arts (in pixel). +; DEFAULT: none +;album_art_max_height = 1024 ; Resize Images * Requires PHP-GD * ; Set this to true if you want Ampache to resize the Album @@ -357,25 +443,23 @@ notify = "true" ; DEFAULT: false ;resize_images = "false" +; Statistical Graphs * Requires PHP-GD * +; Set this to true if you want Ampache to generate statistical +; graphs on usages / users. +; DEFAULT: false +;statistical_graphs = "false" + ; Art Gather Order ; Simply arrange the following in the order you would like ; ampache to search. If you want to disable one of the search ; methods simply leave it out. DB should be left as the first ; method unless you want it to overwrite what's already in the ; database -; POSSIBLE VALUES: db tags folder amazon lastfm musicbrainz google +; POSSIBLE VALUES (builtins): db tags folder lastfm musicbrainz google +; POSSIBLE VALUES (plugins): Amazon,TheAudioDb,Tmdb,Omdb,Flickr ; DEFAULT: db,tags,folder,musicbrainz,lastfm,google art_order = "db,tags,folder,musicbrainz,lastfm,google" -; Amazon Developer Key -; These are needed in order to actually use the amazon album art -; Your public key is your 'Access Key ID' -; Your private key is your 'Secret Access Key' -; DEFAULT: false -;amazon_developer_public_key = "" -;amazon_developer_private_key = "" -;amazon_developer_associate_tag = "" - ; Recommendations ; Set this to true to enable display of similar artists or albums ; while browsing. Requires Last.FM. @@ -390,14 +474,14 @@ art_order = "db,tags,folder,musicbrainz,lastfm,google" ; Last.FM API Key ; Set this to your Last.FM api key to actually use Last.FM for -; recommendations. -;lastfm_api_key = "" +; recommendations and metadata. +lastfm_api_key = "d5df942424c71b754e54ce1832505ae2" ; Wanted ; Set this to true to enable display missing albums and the ; possibility for users to mark it as wanted. ; DEFAULT: false -;wanted = "false" +wanted = "true" ; Wanted types ; Set the allowed types of wanted releases (album,compilation,single,ep,live,remix,promotion,official) @@ -413,43 +497,34 @@ wanted_types = "album,official" ; EchoNest provides several music services. Currently used for missing song 30 seconds preview. ;echonest_api_key = "" +; Labels +; Use labels to browse artists per label membership. +; DEFAULT: false +;label = "false" + ; Broadcasts ; Allow users to broadcast music. ; This feature requires advanced server configuration, please take a look on the wiki for more information. ; DEFAULT: false ;broadcast = "false" +; Channels +; Set this to true to enable channels and the +; possibility for users to create channels from playlists +; DEFAULT: true +channel = "true" + +; Live Streams +; Set this to true to enable live streams (radio) and the +; possibility for users to add new live streams. +; DEFAULT: true +live_stream = "true" + ; Web Socket address ; Declare the web socket server address ; DEFAULT: determined automatically ;websocket_address = "ws://localhost:8100" -; Amazon base urls -; An array of Amazon sites to search. -; NOTE: This will search each of these sites in turn so don't expect it -; to be lightning fast! -; It is strongly recommended that only one of these is selected at any -; one time -; POSSIBLE VALUES: -; http://webservices.amazon.com -; http://webservices.amazon.co.uk -; http://webservices.amazon.de -; http://webservices.amazon.co.jp -; http://webservices.amazon.fr -; http://webservices.amazon.ca -; Default: http://webservices.amazon.com -amazon_base_urls = "http://webservices.amazon.com" - -; max_amazon_results_pages -; The maximum number of results pages to pull from EACH amazon site -; NOTE: The art search pages through the results returned by your search -; up to this number of pages. As with the base_urls above, this is going -; to take more time, the more pages you ask it to process. -; Of course a good search will return only a few matches anyway. -; It is strongly recommended that you do _not_ change this value -; DEFAULT: 1 page (10 items) -max_amazon_results_pages = 1 - ; Debug ; If this is enabled Ampache will write debugging information to the log file ; DEFAULT: false @@ -458,7 +533,7 @@ max_amazon_results_pages = 1 ; Debug Level ; This should always be set in conjunction with the ; debug option, it defines how prolific you want the -; debugging in ampache to be. values are 1-5. +; debugging in ampache to be. values are 1-5. ; 1 == Errors only ; 2 == Error + Failures (login attempts etc.) ; 3 == ?? @@ -487,7 +562,7 @@ log_filename = "%name.%Y%m%d.log" ; DEFAULT: UTF-8 site_charset = UTF-8 -; Locale Charset +; Locale Charset ; Local charset (mainly for file operations) if different ; from site_charset. ; This is disabled by default, enable only if needed @@ -496,12 +571,19 @@ site_charset = UTF-8 ;lc_charset = "ISO8859-1" ; Refresh Limit -; This defines the default refresh limit in seconds for +; This defines the default refresh limit in seconds for ; pages with dynamic content, such as now playing ; DEFAULT: 60 ; Possible Values: Int > 5 refresh_limit = "60" +; Footer Statistics +; This defines whether statistics (Queries, Cache Hits, Load Time) +; are shown in the page footer. +; DEFAULT: true +; Possible values: true, false +show_footer_statistics = "true" + ;######################################################### ; Custom actions (optional) # ;######################################################### @@ -528,10 +610,10 @@ refresh_limit = "60" ;######################################################### ; LDAP filter string to use (required) -; For OpenLDAP use "uid" +; For OpenLDAP use "uid" ; For Microsoft Active Directory (MAD) use "sAMAccountName" ; DEFAULT: null -; ldap_filter = "sAMAccountName" +;ldap_filter = "(sAMAccountName=%v)" ; LDAP objectclass (required) ; OpanLDAP objectclass = "*" @@ -608,15 +690,15 @@ refresh_limit = "60" ; DEFAULT: false ;admin_enable_required = "false" -; This setting will allow all registrants/ldap/http users -; to be auto-approved as a user. By default, they will be +; This setting will allow all registrants/ldap/http users +; to be auto-approved as a user. By default, they will be ; added as a guest and must be promoted by the admin. ; POSSIBLE VALUES: guest, user, admin ; DEFAULT: guest ;auto_user = "guest" ; This will display the user agreement when registering -; For agreement text, edit templates/user_agreement.php +; For agreement text, edit config/registration_agreement.php ; User will need to accept the agreement before they can register ; DEFAULT: false ;user_agreement = "false" @@ -625,6 +707,24 @@ refresh_limit = "60" ; DEFAULT: false ;user_no_email_confirm = "false" +; This will display the cookie disclaimer (EU Cookie Law) +; DEFAULT: false +;cookie_disclaimer = "false" + +; The fields that will be shown on Registration page +; If a user wants to register. +; Username and email fields are forced. +; POSSIBLE VALUES: fullname,website,state,city +; DEFAULT: "fullname,website" +registration_display_fields = "fullname,website" + +; The fields that will be mandatory +; This controls which fields are mandatory for registration. +; Username and email fields are forced mandatory. +; POSSIBLE VALUES: fullname,website,state,city +; DEFAULT: fullname +registration_mandatory_fields = "fullname" + ;######################################################## ; These options control the dynamic downsampling based # ; on current usage # @@ -646,7 +746,7 @@ refresh_limit = "60" ;###################################################### ; These are commands used to transcode non-streaming -; formats to the target file type for streaming. +; formats to the target file type for streaming. ; This can be useful in re-encoding file types that don't stream ; very well, or if your player doesn't support some file types. ; @@ -662,25 +762,64 @@ refresh_limit = "60" ; (e.g. if you store everything in FLAC, but don't want to ever stream that.) ; transcode_TYPE = {allowed|required|false} ; DEFAULT: false +;;; Audio ;transcode_m4a = allowed ;transcode_flac = required ;transcode_mpc = required ;transcode_ogg = required +;transcode_oga = required ;transcode_wav = required +;transcode_wma = required +;transcode_aif = required +;transcode_aiff = required +;transcode_ape = required +;transcode_shn = required ;transcode_mp3 = allowed +;;; Video +;transcode_avi = allowed +;transcode_mkv = allowed +;transcode_mpg = allowed +;transcode_mpeg = allowed +;transcode_m4v = allowed +;transcode_mp4 = allowed +;transcode_mov = allowed +;transcode_wmv = allowed +;transcode_ogv = allowed +;transcode_divx = allowed +;transcode_m2ts = allowed +;transcode_webm = allowed -; Default output format +; Default audio output format ; DEFAULT: none ;encode_target = mp3 +; Default video output format +; DEFAULT: none +;encode_video_target = webm + ; Override the default output format on a per-type basis ; encode_target_TYPE = TYPE ; DEFAULT: none ;encode_target_flac = ogg +; Override the default TYPE transcoding behavior on a per-player basis +; transcode_player_PLAYER_TYPE = TYPE +; Valid PLAYER is: webplayer, api +; DEFAULT: none +;transcode_player_webplayer_m4a = required +;transcode_player_webplayer_flac = required +;transcode_player_webplayer_mpc = required + +; Override the default output format on a per-player basis +; encode_player_PLAYER_target = TYPE +; Valid PLAYER is: webplayer, api +; DEFAULT: none +;encode_player_webplayer_target = mp3 +;encode_player_api_target = mp3 + ; Allow clients to override transcode settings (output type, bitrate, codec ...) ; DEFAULT: true -transcode_player_customize = true +transcode_player_customize = "true" ; Command configuration. Substitutions will be made as follows: ; %FILE% => filename @@ -694,13 +833,16 @@ transcode_player_customize = true ; equivalent to the old default, but if you find that necessary you should be ; clever enough to figure out how on your own. ; DEFAULT: none -;transcode_cmd = "ffmpeg -i %FILE%" -;transcode_cmd = "avconv -i %FILE%" -;transcode_cmd = "/usr/bin/neatokeen %FILE%" +;transcode_cmd = "ffmpeg" +;transcode_cmd = "avconv" +;transcode_cmd = "/usr/bin/neatokeen" + +; Transcode input file argument +transcode_input = "-i %FILE%" ; Specific transcode commands ; It shouldn't be necessary in most cases, but you can override the transcode -; command for specific source formats. It still needs to accept the +; command for specific source formats. It still needs to accept the ; encoding arguments, so the easiest approach is to use your normal command as ; a clearing-house. ; transcode_cmd_TYPE = TRANSCODE_CMD @@ -708,12 +850,28 @@ transcode_player_customize = true ; Encoding arguments ; For each output format, you should provide the necessary arguments for -; your transcode_cmd. +; your transcode_cmd. ; encode_args_TYPE = TRANSCODE_CMD_ARGS -;encode_args_mp3 = "-vn -b:a %SAMPLE%K -c:a libmp3lame -f mp3 pipe:1" -;encode_args_ogg = "-vn -b:a %SAMPLE%K -c:a libvorbis -f ogg pipe:1" -;encode_args_m4a = "-vn -b:a %SAMPLE%K -c:a libfdk_aac -f adts pipe:1" -;encode_args_wav = "-vn -b:a %SAMPLE%K -c:a pcm_s16le -f wav pipe:1" +encode_args_mp3 = "-vn -b:a %SAMPLE%K -c:a libmp3lame -f mp3 pipe:1" +encode_args_ogg = "-vn -b:a %SAMPLE%K -c:a libvorbis -f ogg pipe:1" +encode_args_m4a = "-vn -b:a %SAMPLE%K -c:a libfdk_aac -f adts pipe:1" +encode_args_wav = "-vn -b:a %SAMPLE%K -c:a pcm_s16le -f wav pipe:1" +encode_args_flv = "-b:a %SAMPLE%K -ar 44100 -ac 2 -v 0 -f flv -c:v libx264 -preset superfast -threads 0 pipe:1" +encode_args_webm = "-q %QUALITY% -f webm -c:v libvpx -maxrate %MAXBITRATE%k -preset superfast -threads 0 pipe:1" +encode_args_ts = "-q %QUALITY% -s %RESOLUTION% -f mpegts -c:v libx264 -c:a libmp3lame -maxrate %MAXBITRATE%k -preset superfast -threads 0 pipe:1" + +; Encoding arguments to retrieve an image from a single frame +encode_get_image = "-ss %TIME% -f image2 -vframes 1 pipe:1" + +; Encoding argument to encrust subtitle +encode_srt = "-vf \"subtitles='%SRTFILE%'\"" + +; Encode segment frame argument +encode_ss_frame = "-ss %TIME%" + +; Encode segment duration argument +encode_ss_duration = "-t %DURATION%" + ;###################################################### ; these options allow you to configure your rss-feed @@ -721,7 +879,7 @@ transcode_player_customize = true ; song is the information in the feed. can be multiple items. ; use_rss = false (values true | false) ;DEFAULT: use_rss = false -;use_rss = false +;use_rss = "false" ;##################################################### ;############################# @@ -737,7 +895,7 @@ transcode_player_customize = true ; If Ampache is behind an https reverse proxy, force use HTTPS protocol. ;Default: false -;force_ssl = true +;force_ssl = "true" ;############################# ; Mail Settings # @@ -749,7 +907,7 @@ transcode_player_customize = true ;mail_type = "php" ;Mail domain. -;DEFAULT: example.com +;DEFAULT: example.com ;mail_domain = "example.com" ;This will be combined with mail_domain and used as the source address for @@ -796,7 +954,7 @@ transcode_player_customize = true ;Enable SMTP authentication ;DEFAULT: false -;mail_auth = true +;mail_auth = "true" ;SMTP Username ;your mail auth username. diff --git a/sources/cookie_disclaimer.php b/sources/cookie_disclaimer.php new file mode 100644 index 0000000..d46ec1f --- /dev/null +++ b/sources/cookie_disclaimer.php @@ -0,0 +1,29 @@ + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-s + RewriteRule ^(.+)$ /index.php?action=$1 [PT,L,QSA] + \ No newline at end of file diff --git a/sources/daap/index.php b/sources/daap/index.php new file mode 100644 index 0000000..00c9fa8 --- /dev/null +++ b/sources/daap/index.php @@ -0,0 +1,67 @@ += 7.0) +debug_event('daap', 'Request headers: '. print_r($headers, true), '5'); + +// Get the list of possible methods for the Plex API +$methods = get_class_methods('daap_api'); +// Define list of internal functions that should be skipped +$internal_functions = array('apiOutput', 'create_dictionary', 'createError', 'output_body', 'output_header', 'follow_stream'); + +Daap_Api::create_dictionary(); + +$params = array_filter(explode('/', $action), 'strlen'); +if (count($params) > 0) { + // Recurse through them and see if we're calling one of them + for ($i = count($params); $i > 0; $i--) { + $act = strtolower(implode('_', array_slice($params, 0, $i))); + $act = str_replace("-", "_", $act); + foreach ($methods as $method) { + if (in_array($method, $internal_functions)) { continue; } + + // If the method is the same as the action being called + // Then let's call this function! + if ($act == $method) { + call_user_func(array('daap_api', $method), array_slice($params, $i, count($params) - $i)); + // We only allow a single function to be called, and we assume it's cleaned up! + exit(); + } + + } // end foreach methods in API + } +} + +Daap_Api::createError(404); diff --git a/sources/democratic.php b/sources/democratic.php index a86d596..498f780 100644 --- a/sources/democratic.php +++ b/sources/democratic.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/docs/ACKNOWLEDGEMENTS b/sources/docs/ACKNOWLEDGEMENTS index f2d4f0b..cd454b6 100644 --- a/sources/docs/ACKNOWLEDGEMENTS +++ b/sources/docs/ACKNOWLEDGEMENTS @@ -22,4 +22,12 @@ Acknowledgements * Randy Perkins * Ben Shields * Afterster -* SUTJael \ No newline at end of file +* SUTJael +* Psy-Virus +* John Moore (jcwmoore) +* René Bigler (Razrael) +* Kaivo +* Ernest Wagner (wagnered) +* lotan +* brownl +* Deathcow \ No newline at end of file diff --git a/sources/docs/CHANGELOG.md b/sources/docs/CHANGELOG.md index e06a605..d2a223b 100755 --- a/sources/docs/CHANGELOG.md +++ b/sources/docs/CHANGELOG.md @@ -1,7 +1,207 @@ CHANGELOG ========= -3.7 +3.8.0 +---------- +- Added Portuguese (Brasil) language (thanks Ione Souza Junior) +- Updated PHPMailer version to 5.2.10 +- Fixed user stats clear +- Added user, followers and last shouts XML API functions +- Fixed transcoded process end on some systems (thanks nan4k7) +- Added ogg channel streaming support (thanks Deathcow) +- Fixed sql connection close before stream (thanks fufroma) +- Added support for several ldap filters (thanks T-Rock) +- Fixed 'Add to existing playlist' button on web player (thanks RyanCopley) +- Added 'add to existing playlist' link on album page (thanks RyanCopley) +- Added option to hide user fullname from other users +- Added playlist track information in Apache XML API (thanks RyanCopley) +- Fixed playlist remove song in Apache XML API (thanks RyanCopley) +- Fixed SubSonic API ifModifiedSince information +- Added Podcast links to albums / artists +- Added Piwik and Google Analytics plugins +- Added Apache 2.4 access control declaration in htaccess files +- Fixed performance issues on user preferences +- Added artist search by year and place +- Fixed search by comment (thanks malkavi) +- Added Paypal and Flattr plugins +- Added .maintenance page +- Fixed captcha +- Added private messages between users +- Fixed SubSonic API rating information on albums and songs +- Added latest artists and shouts RSS feeds +- Fixed tag cloud ordering +- Added Label entities associated to artists / users +- Added WebDAV backend +- Fixed SubSonic API requests with musicFolderId parameter (thanks dhsc19) +- Added footer text edition setting +- Added uploaded artist list on user page +- Added custom Ampache login logo and favicon support +- Added edition support on shared objects (thanks dhsc19) +- Fixed share feature on videos (thanks RobertoCarlo) +- Removed album year display from album name if unset +- Fixed Subsonic API Album/Artist song's link (thanks dhsc19 and daneren2005) +- Added mysql database socket authentication support on web setup (thanks AsavarTzeth) +- Fixed artist art url for mobile use (thanks dhsc19) +- Added Shoutbox home plugin +- Added catalog favorites home plugin +- Fixed search by rating (thanks iamnumbersix) +- Added UPnP localplay (thanks SeregaPru) +- Changed preferences to return the global value if preference is missing for the searched user +- Fixed special chars in songs names and tags (thanks SeregaPru) +- Fixed Subsonic API playlist edition/delation (thanks dhsc19) +- Fixed integer default value in Apache XML API +- Fixed image thumb on webplayer and search preview (thanks RobertoCarlo and eephyne) +- Fixed proxy setting on all external http requests (thanks brendankearney) +- Added QRCode view of user API key +- Fixed http status code on Subsonic API streams when using curl (thanks nicklan) +- Added Server-Sent Events on catalog actions +- Added option to enable/disable channel and live stream features +- Removed official PHP 5.3 support +- Added option to show/hide footer statistics (thanks brownl) +- Added delete from disk option on user uploaded files +- Added installation type and players helper at installation process +- Added tv_episode tag on quicktime files (thanks wagnered) +- Added new option to disable deferred extended metadata, e.g. artist details +- Added Subsonic API getAvatar function +- Fixed unsynced lyrics tags +- Fixed ldap_filter setting deactivation on ampache.cfg.php update (thanks Rouzax) +- Added Subsonic API similar artists & songs functions +- Added Subsonic API getLyrics function +- Fixed disk number and album artist metadata on quicktime files (thanks JoeDat) +- Fixed Ampache API playlist_add_song function +- Added ability to store images on disk +- Added new setting to define album art min and max width/height +- Fixed Subsonic API getAlbum returned artist id on songs +- Fixed Subsonic API cover art when PHP-GD unavailable +- Fixed localplay playlist refresh on volume changes (thanks essagl) +- Fixed web player equalizer option if visualizer is not enabled (thanks brownl) +- Fixed asx file mime type (thanks thinca) +- Added song genre parsing options (thanks Razrael and lotan) +- Added sort on languages list (thanks brownl) +- Added placeholder text to search box (thanks brownl) +- Added web player Play Next feature (thanks tan-ce) +- Fixed Plex backend administration page uri (thanks a9k) +- Fixed expired shared objects clean (thanks eephyne) +- Added missing artist search results (thanks bliptec) +- Fixed song genre id parsing (thanks lotan) +- Added Scrobble method to Subsonic API +- Added an option to add tags to child without overwriting +- Added image dimension info to image tables (thanks tsquare66) +- Replaced ArchiveLib by StreamZip-PHP to avoid temporary zip file +- Added Year field in song details and edition +- Added Subsonic API create/delete user, jukebox control and search auto suggestion +- Added few optional install tests +- Improved Share features with modal dialog choices +- Added new action on playlists to remove duplicates +- Fixed playlist addition to another playlist (thanks kszulc) +- Fixed Various Artist link on album page (thanks Jucgshu) +- Added session_destroy call when a session should be destroyed +- Added HTML5 ReplayGain track feature +- Added display and mandatory user registration fields settings +- Added .htaccess IfModule mod_access.c directives +- Fixed SmartPlayer results per user (thanks nakinigit) +- Fixed XSS vulnerability CVE-2014-8620 (thanks g0blin) +- Fixed playlist import setting on catalog update to be disabled by default (thanks DaPike) +- Added ability to browse my tags other library items than songs +- Added Stream Control plugins +- Added transcode settings per player type +- Added ability to write directly the new configuration file when it version changed +- Added `quick play url` to have permanent authenticated stream link without session +- Fixed unresponsive website on batch download (thanks Rouzax) +- Added batch download item granularity +- Fixed 'guest' user site rendering +- Added Aurora.js support in webplayer +- Added Google Maps geolocation analyze plugin +- Added statistical graphs +- Added user geolocation +- Added 'Missing Artist' search +- Fixed Ampache installation with FastCGI +- Added a new RSS Feed plugin +- Added a new 'display home' plugin type +- Added Favorite and Rating features to playlists +- Added user feedback near mouse cursor on democratic votes +- Changed header page position to be fixed +- Added external links on song page details +- Fixed Subsonic API getAlbumList2 byGenre and byYear order (thanks rrjk) +- Added html5 desktop notification +- Added album group order setting +- Fixed unwanted album merge when one of the album doesn't have mbid +- Changed video player to go outside the footer +- Added ip address in authentication failure for fail2ban scripts (thanks popindavibe) +- Added parameter to hide directplay button if number of items is above a limit +- Added Tag split (thanks jcwmoore) +- Fixed album/artist arts and stats migration on rename (thanks jcwmoore) +- Fixed get lyrics from files (thanks apastuszak) +- Fixed verify local catalog (thanks JoeDat) +- Removed Twitter code +- Added optional cookie disclaimer for the EU Cookie Law +- Replaced catalog action links to action dropdown list (thanks Psy-Virus) +- Fixed `remember me` feature (thanks ainola) +- Added email when registered user must be enabled by administrators +- Fixed local catalog clean on Windows (thanks Rouzax) +- Added Subsonic API maxBitRate parameter support (thanks philipl) +- Fixed SubSonic API special characters encode (thanks nan4k7) +- Added Beets local and remote catalog support (thanks Razrael) +- Fixed XML error code returned with invalid Ampache API handshake (thanks funkygaddafi) +- Replaced iframe to Ajax dynamic page loading +- Changed `Albums of the moment` to not necessarily have a cover +- Added Plex backend items edition support +- Added hls stream support +- Added X-Content-Duration header support on streams +- Removed Toogle Art from artist page +- Fixed track numbers when removing a song from playlist (thanks stonie08) +- Added Plex backend playlist support +- Added gather art from video files (thanks wagnered) +- Added Plex backend movie / tvshow support +- Added release group on albums +- Added Smart Playlist songs list +- Added zlib test +- Removed old Ampache themes +- Fixed SubSonic API lastModified element (thanks bikkuri10) +- Disabled `beautiful url` on XML-API for retro-compatibility +- Fixed image resource allocation (thanks greengeek1) +- Added setting to write id3 metadata to files (thanks tsquare66) +- Added check for large files manipulation +- Added video subtitle support +- Fixed Google arts to use real arts and not the small size preview +- Added Tmdb metadata plugin +- Added Omdb metadata plugin +- Added Music Clips, Movies and TV Shows support +- Added media type information on catalog +- Fixed get SmartPlaylist in XML-API (thanks opencrf) +- Added beautiful url on arts +- Improved browse list header (thanks Psy-Virus) +- Fixed user online/offline information on Reborn theme (thanks thorsforge) +- Added UPnP backend (thanks SeregaPru) +- Added DAAP backend +- Added sort options on playlists (thanks Shdwdrgn) +- Fixed XML-API tag information (thanks jcwmoore) +- Fixed multiple broadcast play (thanks uk3gaus) +- Added SmartPlaylists to Subsonic API +- Added limit option on SmartPlaylists +- Added random option on SmartPlaylists +- Added 'item count' on browse +- Added direct typed links on items tags +- Fixed SubSonic API compatibility with few players requesting information on library -1 +- Added license information on songs +- Added upload feature on web interface +- Added albumartist information on songs (thanks tsquare66) +- Fixed errors on sql table exists check +- Fixed play/pause on broadcasts (thanks uk3gaus) +- Added donation button +- Added democratic page automatic refresh +- Fixed distinct random albums +- Added collapsing menu (thanks Kaivo) +- Added 'save to playlist' feature on web player (thanks Kaivo) +- Added tag merge feature +- Fixed democratic vote with automatic logins (thanks M4DM4NZ) +- Added git pull update from web interface for development versions +- Fixed http-rang requests on streaming (thanks thejk) +- Improved installation process +- Improved French translation (thanks arnaudbey) +- Improved German translation (thanks Psy-Virus and meandor) + +3.7.0 ---------- - Added Scrutinizer analyze - Fixed playlist play with disabled songs (reported by stebe) diff --git a/sources/docs/PLUGINS b/sources/docs/PLUGINS index 4c58f39..711f076 100644 --- a/sources/docs/PLUGINS +++ b/sources/docs/PLUGINS @@ -27,9 +27,15 @@ should be implemented as a public method: get_metadata(Array $metadata) The passed array contains the best metadata we've got. save_rating(Rating $rating, int $new_value) - save_songplay(Song $song) + save_mediaplay(Media $media) get_lyrics(Song $song) process_wanted(Wanted $wanted) shortener(string $url) get_photos(string $search) - + gather_arts(string $type, array $options, int $limit) + get_song_preview(string $track_mbid, string $artist_name, string $title) + stream_song_preview(string $file) + display_home() + external_share(string $url, string $text) + display_user_field(library_item $libitem = null) + display_on_footer \ No newline at end of file diff --git a/sources/graph.php b/sources/graph.php new file mode 100644 index 0000000..b1e5fa5 --- /dev/null +++ b/sources/graph.php @@ -0,0 +1,69 @@ +render_user_hits($user_id, $object_type, $object_id, $start_date, $end_date, $zoom, $width, $height); + break; + case 'user_bandwidth': + $graph->render_user_bandwidth($user_id, $object_type, $object_id, $start_date, $end_date, $zoom, $width, $height); + break; + case 'catalog_files': + $graph->render_catalog_files($user_id, $object_type, $object_id, $start_date, $end_date, $zoom, $width, $height); + break; + case 'catalog_size': + $graph->render_catalog_size($user_id, $object_type, $object_id, $start_date, $end_date, $zoom, $width, $height); + break; +} diff --git a/sources/image.php b/sources/image.php index 0591995..dfeb43c 100644 --- a/sources/image.php +++ b/sources/image.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -32,10 +32,12 @@ define('NO_SESSION','1'); require_once 'lib/init.php'; -// Check to see if they've got an interface session or a valid API session, if not GTFO -if (!Session::exists('interface', $_COOKIE[AmpConfig::get('session_name')]) && !Session::exists('api', $_REQUEST['auth'])) { - debug_event('image','Access denied, checked cookie session:' . $_COOKIE[AmpConfig::get('session_name')] . ' and auth:' . $_REQUEST['auth'], 1); - exit; +if (AmpConfig::get('use_auth') && AmpConfig::get('require_session')) { + // Check to see if they've got an interface session or a valid API session, if not GTFO + if (!Session::exists('interface', $_COOKIE[AmpConfig::get('session_name')]) && !Session::exists('api', $_REQUEST['auth'])) { + debug_event('image','Access denied, checked cookie session:' . $_COOKIE[AmpConfig::get('session_name')] . ' and auth:' . $_REQUEST['auth'], 1); + exit; + } } // If we aren't resizing just trash thumb @@ -46,44 +48,18 @@ if (!isset($_GET['object_type'])) { $_GET['object_type'] = 'album'; } -$type = Art::validate_type($_GET['object_type']); +$type = $_GET['object_type']; +if (!Core::is_library_item($type)) + exit; /* Decide what size this image is */ -switch ($_GET['thumb']) { - case '1': - /* This is used by the now_playing stuff */ - $size['height'] = '75'; - $size['width'] = '75'; - break; - case '2': - $size['height'] = '128'; - $size['width'] = '128'; - break; - case '3': - /* This is used by the flash player */ - $size['height'] = '80'; - $size['width'] = '80'; - break; - case '4': - /* Web Player size */ - $size['height'] = 200; - $size['width'] = 200; // 200px width, set via CSS - break; - case '5': - /* Web Player size */ - $size['height'] = 32; - $size['width'] = 32; - break; - default: - $size['height'] = '275'; - $size['width'] = '275'; - if (!isset($_GET['thumb'])) { $return_raw = true; } - break; -} // define size based on thumbnail +$size = Art::get_thumb_size($_GET['thumb']); +$kind = isset($_GET['kind']) ? $_GET['kind'] : 'default'; $image = ''; $mime = ''; $filename = ''; +$etag = ''; $typeManaged = false; if (isset($_GET['type'])) { switch ($_GET['type']) { @@ -102,20 +78,47 @@ if (isset($_GET['type'])) { } } if (!$typeManaged) { - $media = new $type($_GET['id']); - $filename = $media->name; + $item = new $type($_GET['object_id']); + $filename = $item->name ?: $item->title; - $art = new Art($media->id,$type); + $art = new Art($item->id, $type, $kind); $art->get_db(); + $etag = $art->id; + + // That means the client has a cached version of the image + $reqheaders = getallheaders(); + if (isset($reqheaders['If-Modified-Since']) && isset($reqheaders['If-None-Match'])) { + $ccontrol = $reqheaders['Cache-Control']; + if ($ccontrol != 'no-cache') { + $cetagf = explode('-', $reqheaders['If-None-Match']); + $cetag = $cetagf[0]; + // Same image than the cached one? Use the cache. + if ($cetag == $etag) { + header('HTTP/1.1 304 Not Modified'); + exit; + } + } + } if (!$art->raw_mime) { - $mime = 'image/jpeg'; - $image = file_get_contents(AmpConfig::get('prefix') . - AmpConfig::get('theme_path') . - '/images/blankalbum.jpg'); + $defaultimg = AmpConfig::get('prefix') . AmpConfig::get('theme_path') . '/images/'; + switch ($type) { + case 'video': + case 'tvshow': + case 'tvshow_season': + $mime = 'image/png'; + $defaultimg .= "blankmovie.png"; + break; + default: + $mime = 'image/jpeg'; + $defaultimg .= "blankalbum.jpg"; + break; + } + $image = file_get_contents($defaultimg); } else { if ($_GET['thumb']) { $thumb_data = $art->get_thumb($size); + $etag .= '-' . $_GET['thumb']; } $mime = isset($thumb_data['thumb_mime']) ? $thumb_data['thumb_mime'] : $art->raw_mime; @@ -129,7 +132,11 @@ if (!empty($image)) { // Send the headers and output the image $browser = new Horde_Browser(); - header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time() + 604800)); + if (!empty($etag)) { + header('ETag: ' . $etag); + header('Cache-Control: private'); + header('Last-Modified: '.gmdate('D, d M Y H:i:s \G\M\T', time())); + } $browser->downloadHeaders($filename, $mime, true); echo $image; } diff --git a/sources/themes/reborn/images/background.png b/sources/images/background.png similarity index 100% rename from sources/themes/reborn/images/background.png rename to sources/images/background.png diff --git a/sources/images/fileupload-border.png b/sources/images/fileupload-border.png new file mode 100644 index 0000000..70b21e9 Binary files /dev/null and b/sources/images/fileupload-border.png differ diff --git a/sources/images/fileupload-icons.png b/sources/images/fileupload-icons.png new file mode 100644 index 0000000..3446eb0 Binary files /dev/null and b/sources/images/fileupload-icons.png differ diff --git a/sources/images/icon_clean.png b/sources/images/icon_clean.png new file mode 100644 index 0000000..cf039f4 Binary files /dev/null and b/sources/images/icon_clean.png differ diff --git a/sources/images/icon_file_refresh.png b/sources/images/icon_file_refresh.png new file mode 100644 index 0000000..e5bebdd Binary files /dev/null and b/sources/images/icon_file_refresh.png differ diff --git a/sources/images/icon_play_add.png b/sources/images/icon_play_add.png index 5a30a7c..99e78b7 100644 Binary files a/sources/images/icon_play_add.png and b/sources/images/icon_play_add.png differ diff --git a/sources/images/icon_play_next.png b/sources/images/icon_play_next.png new file mode 100644 index 0000000..5a30a7c Binary files /dev/null and b/sources/images/icon_play_next.png differ diff --git a/sources/images/icon_replaygain.png b/sources/images/icon_replaygain.png new file mode 100644 index 0000000..5ca7a18 Binary files /dev/null and b/sources/images/icon_replaygain.png differ diff --git a/sources/images/icon_share_facebook.png b/sources/images/icon_share_facebook.png new file mode 100644 index 0000000..3053f26 Binary files /dev/null and b/sources/images/icon_share_facebook.png differ diff --git a/sources/images/icon_share_googleplus.png b/sources/images/icon_share_googleplus.png new file mode 100644 index 0000000..be37ca8 Binary files /dev/null and b/sources/images/icon_share_googleplus.png differ diff --git a/sources/images/icon_share_twitter.png b/sources/images/icon_share_twitter.png new file mode 100644 index 0000000..be32e12 Binary files /dev/null and b/sources/images/icon_share_twitter.png differ diff --git a/sources/images/icon_sort.png b/sources/images/icon_sort.png new file mode 100644 index 0000000..f7de724 Binary files /dev/null and b/sources/images/icon_sort.png differ diff --git a/sources/index.php b/sources/index.php index b2eee23..9610715 100644 --- a/sources/index.php +++ b/sources/index.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -22,12 +22,6 @@ require_once 'lib/init.php'; -if (AmpConfig::get('iframes')) { - if (!isset($_GET['framed'])) { - UI::show_mainframes(); - exit; - } -} UI::show_header(); $action = isset($_REQUEST['action']) ? scrub_in($_REQUEST['action']) : null; @@ -42,7 +36,7 @@ $_SESSION['catalog'] = 0; * refresh_javascript include. Must be greater then 5, I'm not * going to let them break their servers */ -if (AmpConfig::get('refresh_limit') > 5) { +if (AmpConfig::get('refresh_limit') > 5 && AmpConfig::get('home_now_playing')) { $refresh_limit = AmpConfig::get('refresh_limit'); $ajax_url = '?page=index&action=reloadnp'; require_once AmpConfig::get('prefix') . '/templates/javascript_refresh.inc.php'; diff --git a/sources/install.php b/sources/install.php index c180b33..686c1b7 100644 --- a/sources/install.php +++ b/sources/install.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -37,6 +37,7 @@ define('INSTALL', 1); $htaccess_play_file = AmpConfig::get('prefix') . '/play/.htaccess'; $htaccess_rest_file = AmpConfig::get('prefix') . '/rest/.htaccess'; +$htaccess_channel_file = AmpConfig::get('prefix') . '/channel/.htaccess'; // Clean up incoming variables $web_path = scrub_in($_REQUEST['web_path']); @@ -65,6 +66,20 @@ if (isset($_REQUEST['transcode_template'])) { install_config_transcode_mode($mode); } +if (isset($_REQUEST['usecase'])) { + $case = $_REQUEST['usecase']; + if (Dba::check_database()) { + install_config_use_case($case); + } +} + +if (isset($_REQUEST['backends'])) { + $backends = $_REQUEST['backends']; + if (Dba::check_database()) { + install_config_backends($backends); + } +} + // Charset and gettext setup $htmllang = $_REQUEST['htmllang']; $charset = $_REQUEST['charset']; @@ -89,7 +104,7 @@ load_gettext(); header ('Content-Type: text/html; charset=' . AmpConfig::get('site_charset')); // Correct potential \ or / in the dirname -$safe_dirname = rtrim(dirname($_SERVER['PHP_SELF']),"/\\"); +$safe_dirname = get_web_path(); $web_path = $http_type . $_SERVER['HTTP_HOST'] . $safe_dirname; @@ -119,29 +134,54 @@ switch ($_REQUEST['action']) { // Now that it's inserted save the lang preference Preference::update('lang', '-1', AmpConfig::get('lang')); - - header ('Location: ' . $web_path . "/install.php?action=show_create_config&local_db=$database&local_host=$hostname&local_port=$port&htmllang=$htmllang&charset=$charset"); - break; - case 'create_config': - $download = (!isset($_POST['write'])); - $download_htaccess_rest = (isset($_POST['download_htaccess_rest'])); - $download_htaccess_play = (isset($_POST['download_htaccess_play'])); - $write_htaccess_rest = (isset($_POST['write_htaccess_rest'])); - $write_htaccess_play = (isset($_POST['write_htaccess_play'])); - - if ($write_htaccess_rest || $download_htaccess_rest) { - $created_config = install_rewrite_rules($htaccess_rest_file, $_POST['web_path'], $download_htaccess_rest); - } elseif ($write_htaccess_play || $download_htaccess_play) { - $created_config = install_rewrite_rules($htaccess_play_file, $_POST['web_path'], $download_htaccess_play); - } else { - $created_config = install_create_config($download); - } - - require_once 'templates/show_install_config.inc.php'; - break; case 'show_create_config': require_once 'templates/show_install_config.inc.php'; break; + case 'create_config': + $all = (isset($_POST['create_all'])); + $skip = (isset($_POST['skip_config'])); + if (!$skip) { + $write = (isset($_POST['write'])); + $download = (isset($_POST['download'])); + $download_htaccess_channel = (isset($_POST['download_htaccess_channel'])); + $download_htaccess_rest = (isset($_POST['download_htaccess_rest'])); + $download_htaccess_play = (isset($_POST['download_htaccess_play'])); + $write_htaccess_channel = (isset($_POST['write_htaccess_channel'])); + $write_htaccess_rest = (isset($_POST['write_htaccess_rest'])); + $write_htaccess_play = (isset($_POST['write_htaccess_play'])); + + $created_config = true; + if ($write_htaccess_channel || $download_htaccess_channel || $all) { + $created_config = $created_config && install_rewrite_rules($htaccess_channel_file, $_POST['web_path'], $download_htaccess_channel); + } + if ($write_htaccess_rest || $download_htaccess_rest || $all) { + $created_config = $created_config && install_rewrite_rules($htaccess_rest_file, $_POST['web_path'], $download_htaccess_rest); + } + if ($write_htaccess_play || $download_htaccess_play || $all) { + $created_config = $created_config && install_rewrite_rules($htaccess_play_file, $_POST['web_path'], $download_htaccess_play); + } + if ($write || $download || $all) { + $created_config = $created_config && install_create_config($download); + } + } + case 'show_create_account': + $results = parse_ini_file($configfile); + if (!isset($created_config)) $created_config = true; + + /* Make sure we've got a valid config file */ + if (!check_config_values($results) || !$created_config) { + Error::add('general', T_('Error: Config files not found or unreadable')); + require_once AmpConfig::get('prefix') . '/templates/show_install_config.inc.php'; + break; + } + + // Don't try to add administrator user on existing database + if (install_check_status($configfile)) { + require_once AmpConfig::get('prefix') . '/templates/show_install_account.inc.php'; + } else { + header ("Location: " . $web_path . '/login.php'); + } + break; case 'create_account': $results = parse_ini_file($configfile); AmpConfig::set_by_array($results, true); @@ -153,19 +193,13 @@ switch ($_REQUEST['action']) { break; } - header ("Location: " . $web_path . '/login.php'); - break; - case 'show_create_account': - $results = parse_ini_file($configfile); + // Automatically log-in the newly created user + Session::create_cookie(); + Session::create(array('type' => 'mysql', 'username' => $username)); + $_SESSION['userdata']['username'] = $username; + Session::check(); - /* Make sure we've got a valid config file */ - if (!check_config_values($results)) { - Error::add('general', T_('Error: Config file not found or unreadable')); - require_once AmpConfig::get('prefix') . '/templates/show_install_config.inc.php'; - break; - } - - require_once AmpConfig::get('prefix') . '/templates/show_install_account.inc.php'; + header ("Location: " . $web_path . '/index.php'); break; case 'init': require_once 'templates/show_install.inc.php'; diff --git a/sources/labels.php b/sources/labels.php new file mode 100644 index 0000000..4ba238d --- /dev/null +++ b/sources/labels.php @@ -0,0 +1,111 @@ +id . '`.', 1); + UI::access_denied(); + exit; + } + + if ($label->remove()) { + show_confirmation(T_('Label Deletion'), T_('Label has been deleted.'), AmpConfig::get('web_path')); + } else { + show_confirmation(T_('Label Deletion'), T_('Cannot delete this label.'), AmpConfig::get('web_path')); + } + break; + case 'add_label': + // Must be at least a content manager or edit upload enabled + if (!Access::check('interface','50') && !AmpConfig::get('upload_allow_edit')) { + UI::access_denied(); + exit; + } + + if (!Core::form_verify('add_label','post')) { + UI::access_denied(); + exit; + } + + // Remove unauthorized defined values from here + if (isset($_POST['user'])) { + unset($_POST['user']); + } + if (isset($_POST['creation_date'])) { + unset($_POST['creation_date']); + } + + $label_id = Label::create($_POST); + if (!$label_id) { + require_once AmpConfig::get('prefix') . '/templates/show_add_label.inc.php'; + } else { + $body = T_('Label Added'); + $title = ''; + show_confirmation($title, $body, AmpConfig::get('web_path') . '/browse.php?action=label'); + } + break; + case 'show': + $label_id = intval($_REQUEST['label']); + if (!$label_id) { + if (!empty($_REQUEST['name'])) { + $label_id = Label::lookup($_REQUEST); + } + } + if ($label_id > 0) { + $label = new Label($label_id); + $label->format(); + $object_ids = $label->get_artists(); + $object_type = 'artist'; + require_once AmpConfig::get('prefix') . '/templates/show_label.inc.php'; + UI::show_footer(); + exit; + } + case 'show_add_label': + if (Access::check('interface','50') || AmpConfig::get('upload_allow_edit')) { + require_once AmpConfig::get('prefix') . '/templates/show_add_label.inc.php'; + } else { + echo T_('Label cannot be found.'); + } + break; +} // end switch + +UI::show_footer(); diff --git a/sources/lib/.htaccess b/sources/lib/.htaccess index 896fbc5..cfd4051 100644 --- a/sources/lib/.htaccess +++ b/sources/lib/.htaccess @@ -1,2 +1,10 @@ -Order deny,allow -Deny from all \ No newline at end of file +# Apache 2.4 + + Require all denied + + +# Apache 2.2 + + Order deny,allow + Deny from all + \ No newline at end of file diff --git a/sources/lib/batch.lib.php b/sources/lib/batch.lib.php index 9d26e8c..e06adc3 100644 --- a/sources/lib/batch.lib.php +++ b/sources/lib/batch.lib.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -21,93 +21,97 @@ */ /** - * get_song_files + * get_media_files * - * Takes an array of song ids and returns an array of the actual filenames + * Takes an array of media ids and returns an array of the actual filenames * * @param array $media_ids Media IDs. */ -function get_song_files($media_ids) +function get_media_files($media_ids) { $media_files = array(); $total_size = 0; foreach ($media_ids as $element) { if (is_array($element)) { - $type = array_shift($element); - $media = new $type(array_shift($element)); + if (isset($element['object_type'])) { + $type = $element['object_type']; + $id = $element['object_id']; + } else { + $type = array_shift($element); + $id = array_shift($element); + } + $media = new $type($id); } else { $media = new Song($element); } if ($media->enabled) { - $total_size += sprintf("%.2f",($media->size/1048576)); $media->format(); - $dirname = $media->f_album_full; - //debug_event('batch.lib.php', 'Songs file {'.$media->file.'}...', '5'); + $total_size += sprintf("%.2f",($media->size/1048576)); + $dirname = ''; + $parent = $media->get_parent(); + if ($parent != null) { + $pobj = new $parent['object_type']($parent['object_id']); + $pobj->format(); + $dirname = $pobj->get_fullname(); + } if (!array_key_exists($dirname, $media_files)) { $media_files[$dirname] = array(); } - array_push($media_files[$dirname], $media->file); + array_push($media_files[$dirname], Core::conv_lc_file($media->file)); } } return array($media_files, $total_size); -} //get_song_files +} //get_media_files /** * send_zip * - * takes array of full paths to songs + * takes array of full paths to medias * zips them and sends them * * @param string $name name of the zip file to be created - * @param array $song_files array of full paths to songs to zip create w/ call to get_song_files + * @param array $media_files array of full paths to medias to zip create w/ call to get_media_files */ -function send_zip($name, $song_files) +function send_zip($name, $media_files) { - // Check if they want to save it to a file, if so then make sure they've - // got a defined path as well and that it's writable. - $basedir = ''; - if (AmpConfig::get('file_zip_download') && AmpConfig::get('tmp_dir_path')) { - // Check writeable - if (!is_writable(AmpConfig::get('tmp_dir_path'))) { - $in_memory = '1'; - debug_event('Error','File Zip Path:' . AmpConfig::get('tmp_dir_path') . ' is not writable','1'); - } else { - $in_memory = '0'; - $basedir = AmpConfig::get('tmp_dir_path'); - } - } else { - $in_memory = '1'; - } // if file downloads - /* Require needed library */ - require_once AmpConfig::get('prefix') . '/modules/archive/archive.lib.php'; - $arc = new zip_file($name . ".zip" ); + require_once AmpConfig::get('prefix') . '/modules/ZipStream/ZipStream.php'; + $arc = new ZipStream\ZipStream($name . ".zip" ); $options = array( - 'inmemory' => $in_memory, // create archive in memory - 'basedir' => $basedir, - 'storepaths' => 0, // only store file name, not full path - 'level' => 0, // no compression 'comment' => AmpConfig::get('file_zip_comment'), - 'type' => "zip" ); - $arc->set_options( $options ); - foreach ($song_files as $dir => $files) { - $arc->add_files($files, $dir); + foreach ($media_files as $dir => $files) { + foreach ($files as $file) { + $arc->addFileFromPath($dir . "/" . basename($file), $file, $options); + } } - if (count($arc->error)) { - debug_event('archive',"Error: unable to add songs",'3'); - return false; - } // if failed to add songs - - if (!$arc->create_archive()) { - debug_event('archive',"Error: unable to create archive",'3'); - return false; - } // if failed to create archive - - $arc->download_file(); - + $arc->finish(); } // send_zip + +/** + * check_can_zip + * + * Check that an object type is allowed to be zipped. + * + * @param string $object_type + */ +function check_can_zip($object_type) +{ + $allowed = true; + if (AmpConfig::get('allow_zip_types')) { + $allowed = false; + $allowed_types = explode(',', AmpConfig::get('allow_zip_types')); + foreach ($allowed_types as $atype) { + if (trim($atype) == $object_type) { + $allowed = true; + break; + } + } + } + + return $allowed; +} diff --git a/sources/lib/class/access.class.php b/sources/lib/class/access.class.php index ae09ddb..4a2c1f7 100644 --- a/sources/lib/class/access.class.php +++ b/sources/lib/class/access.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -30,27 +30,68 @@ class Access { // Variables from DB + + /** + * @var int $id + */ public $id; + /** + * @var string $name + */ public $name; + /** + * @var string $start + */ public $start; + /** + * @var string $end + */ public $end; + /** + * @var int $level + */ public $level; + /** + * @var int $user + */ public $user; + /** + * @var string $type + */ public $type; + /** + * @var boolean $enabled + */ public $enabled; + /** + * @var string $f_start + */ public $f_start; + /** + * @var string $f_end + */ public $f_end; + /** + * @var string $f_user + */ public $f_user; + /** + * @var string $f_level + */ public $f_level; + /** + * @var string $f_type + */ public $f_type; /** * constructor * * Takes an ID of the access_id dealie :) + * @param int|null $access_id */ - public function __construct($access_id = '') + public function __construct($access_id = null) { if (!$access_id) { return false; } @@ -69,6 +110,7 @@ class Access * _get_info * * Gets the vars for $this out of the database. + * @return array */ private function _get_info() { @@ -100,6 +142,9 @@ class Access * _verify_range * * This outputs an error if the IP range is bad. + * @param string $startp + * @param string $endp + * @return boolean */ private static function _verify_range($startp, $endp) { @@ -128,8 +173,10 @@ class Access * * This function takes a named array as a datasource and updates the current * access list entry. + * @param array $data + * @return boolean */ - public function update($data) + public function update(array $data) { if (!self::_verify_range($data['start'], $data['end'])) { return false; @@ -156,8 +203,10 @@ class Access * * This takes a keyed array of data and trys to insert it as a * new ACL entry + * @param array $data + * @return boolean */ - public static function create($data) + public static function create(array $data) { if (!self::_verify_range($data['start'], $data['end'])) { return false; @@ -191,8 +240,10 @@ class Access * * This sees if the ACL that we've specified already exists in order to * prevent duplicates. The name is ignored. + * @param array $data + * @return boolean */ - public static function exists($data) + public static function exists(array $data) { $start = inet_pton($data['start']); $end = inet_pton($data['end']); @@ -214,6 +265,7 @@ class Access * delete * * deletes the specified access_list entry + * @param int $id */ public static function delete($id) { @@ -224,24 +276,26 @@ class Access * check_function * * This checks if specific functionality is enabled. + * @param string $type + * @return boolean */ public static function check_function($type) { switch ($type) { case 'download': - return AmpConfig::get('download'); + return make_bool(AmpConfig::get('download')); case 'batch_download': if (!function_exists('gzcompress')) { debug_event('access', 'ZLIB extension not loaded, batch download disabled', 3); return false; } - if (AmpConfig::get('allow_zip_download') AND $GLOBALS['user']->has_access('25')) { - return AmpConfig::get('download'); + if (AmpConfig::get('allow_zip_download') AND $GLOBALS['user']->has_access('5')) { + return make_bool(AmpConfig::get('download')); } break; - default: - return false; } + + return false; } /** @@ -249,6 +303,11 @@ class Access * * This takes a type, ip, user, level and key and then returns whether they * are allowed. The IP is passed as a dotted quad. + * @param string $type + * @param int|string $user + * @param int $level + * @param string $ip + * @return boolean */ public static function check_network($type, $user, $level, $ip=null) { @@ -313,8 +372,12 @@ class Access * * Everything uses the global 0,5,25,50,75,100 stuff. GLOBALS['user'] is * always used. + * @param string $type + * @param int $level + * @param int|null $user + * @return boolean */ - public static function check($type, $level) + public static function check($type, $level, $user_id=null) { if (AmpConfig::get('demo_mode')) { return true; @@ -323,6 +386,10 @@ class Access return true; } + $user = $GLOBALS['user']; + if ($user_id) { + $user = new User($user_id); + } $level = intval($level); // Switch on the type @@ -330,10 +397,10 @@ class Access case 'localplay': // Check their localplay_level return (AmpConfig::get('localplay_level') >= $level - || $GLOBALS['user']->access >= 100); + || $user->access >= 100); case 'interface': // Check their standard user level - return ($GLOBALS['user']->access >= $level); + return ($user->access >= $level); default: return false; } @@ -344,6 +411,8 @@ class Access * * This validates the specified type; it will always return a valid type, * even if you pass in an invalid one. + * @param string $type + * @return string */ public static function validate_type($type) { @@ -360,6 +429,7 @@ class Access /** * get_access_lists * returns a full listing of all access rules on this server + * @return array */ public static function get_access_lists() { @@ -380,6 +450,7 @@ class Access * get_level_name * * take the int level and return a named level + * @return string */ public function get_level_name() { @@ -401,6 +472,7 @@ class Access * get_user_name * * Return a name for the users covered by this ACL. + * @return string */ public function get_user_name() { @@ -414,6 +486,7 @@ class Access * get_type_name * * This function returns the pretty name for our current type. + * @return string */ public function get_type_name() { diff --git a/sources/lib/class/ajax.class.php b/sources/lib/class/ajax.class.php index 83a6cbd..057ce1e 100644 --- a/sources/lib/class/ajax.class.php +++ b/sources/lib/class/ajax.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -31,6 +31,7 @@ class Ajax { private static $include_override; + private static $counter = 0; /** * constructor @@ -46,6 +47,11 @@ class Ajax * observe * This returns a string with the correct and full ajax 'observe' stuff * from jQuery + * @param string $source + * @param string $method + * @param string $action + * @param string $confirm + * @return string */ public static function observe($source, $method, $action, $confirm='') { @@ -59,10 +65,15 @@ class Ajax $observe = ""; @@ -73,6 +84,8 @@ class Ajax /** * url * This takes a string and makes an URL + * @param string $action + * @return string */ public static function url($action) { @@ -83,6 +96,10 @@ class Ajax * action * This takes the action, the source and the post (if passed) and * generates the full ajax link + * @param string $action + * @param string $source + * @param string $post + * @return string */ public static function action($action, $source, $post='') { @@ -110,6 +127,14 @@ class Ajax * button * This prints out an img of the specified icon with the specified alt * text and then sets up the required ajax for it. + * @param string $action + * @param string $icon + * @param string $alt + * @param string $source + * @param string $post + * @param string $class + * @param string $confirm + * @return string */ public static function button($action, $icon, $alt, $source='', $post='', $class='', $confirm='') { @@ -138,24 +163,35 @@ class Ajax * text * This prints out the specified text as a link and sets up the required * ajax for the link so it works correctly + * @param string $action + * @param string $text + * @param string $source + * @param string $post + * @param string $class + * @return string */ public static function text($action, $text, $source, $post='', $class='') { - // Avoid duplicate id - $source .= '_' . time(); + // Temporary workaround to avoid sorting on custom base requests + if (!defined("NO_BROWSE_SORTING") || strpos($source, "sort_") === false) { + // Avoid duplicate id + $source .= '_' . time() . '_' . self::$counter++; - // Format the string we wanna use - $ajax_string = self::action($action, $source, $post); + // Format the string we wanna use + $ajax_string = self::action($action, $source, $post); - // If they passed a span class - if ($class) { - $class = ' class="' . $class . '"'; + // If they passed a span class + if ($class) { + $class = ' class="' . $class . '"'; + } + + $string = "$text\n"; + + $string .= self::observe($source, 'click', $ajax_string); + } else { + $string = $text; } - $string = "$text\n"; - - $string .= self::observe($source, 'click', $ajax_string); - return $string; } // text @@ -163,6 +199,7 @@ class Ajax /** * run * This runs the specified action no questions asked + * @param string $action */ public static function run($action) { @@ -176,6 +213,7 @@ class Ajax * set_include_override * This sets the including div override, used only one place. Kind of a * hack. + * @param bool $value */ public static function set_include_override($value) { @@ -187,6 +225,8 @@ class Ajax * start_container * This checks to see if we're AJAXin'. If we aren't then it echoes out * the html needed to start a container that can be replaced by Ajax. + * @param string $name + * @param string $class */ public static function start_container($name, $class = '') { diff --git a/sources/lib/class/album.class.php b/sources/lib/class/album.class.php index c5bd40e..be71bee 100644 --- a/sources/lib/class/album.class.php +++ b/sources/lib/class/album.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * * This program is free software; you can redistribute it and/or @@ -28,39 +28,153 @@ * it is related to the album table in the database. * */ -class Album extends database_object +class Album extends database_object implements library_item { /* Variables from DB */ - public $id; - public $name; - public $disk; - public $year; - public $prefix; - public $mbid; // MusicBrainz ID + /** + * @var int $id + */ + public $id; + /** + * @var string $name + */ + public $name; + /** + * @var int $album_artist + */ + public $album_artist; + /** + * @var int $disk + */ + public $disk; + /** + * @var int $year + */ + public $year; + /** + * @var string $prefix + */ + public $prefix; + /** + * @var string $mbid + */ + public $mbid; // MusicBrainz ID + /** + * @var string $mbid_group + */ + public $mbid_group; // MusicBrainz Release Group ID + /** + * @var string $release_type + */ + public $release_type; + + /** + * @var int $catalog_id + */ + public $catalog_id; + /** + * @var int $song_count + */ public $song_count; + /** + * @var string $artist_prefix + */ public $artist_prefix; + /** + * @var string $artist_name + */ public $artist_name; + /** + * @var int $artist_id + */ public $artist_id; + /** + * @var array $tags + */ public $tags; + /** + * @var string $full_name + */ public $full_name; // Prefix + Name, generated + /** + * @var int $artist_count + */ public $artist_count; + /** + * @var string $f_artist_name + */ public $f_artist_name; + /** + * @var string $f_artist_link + */ public $f_artist_link; + /** + * @var string $f_artist + */ public $f_artist; + /** + * @var string $album_artist_name + */ + public $album_artist_name; + /** + * @var string $f_album_artist_name + */ + public $f_album_artist_name; + /** + * @var string $f_album_artist_link + */ + public $f_album_artist_link; + /** + * @var string $f_name + */ public $f_name; - public $f_name_link; - public $f_link_src; + /** + * @var string $link + */ + public $link; + /** + * @var string $f_link + */ public $f_link; + /** + * @var string $f_tags + */ public $f_tags; + /** + * @var string $f_year + */ + public $f_year; + /** + * @var string $f_title + */ public $f_title; + /** + * @var string $f_release_type + */ + public $f_release_type; // cached information + /** + * @var boolean $_fake + */ public $_fake; + /** + * @var array $_songs + */ public $_songs = array(); + /** + * @var array $_mapcache + */ private static $_mapcache = array(); + /** + * @var array $album_suite + */ public $album_suite = array(); + /** + * @var boolean $allow_group_disks + */ public $allow_group_disks = false; /** @@ -69,8 +183,9 @@ class Album extends database_object * to this album from the database it does not * pull the album or thumb art by default or * get any of the counts. + * @param int|null $id */ - public function __construct($id='') + public function __construct($id=null) { if (!$id) { return false; } @@ -98,8 +213,10 @@ class Album extends database_object * construct_from_array * This is often used by the metadata class, it fills out an album object from a * named array, _fake is set to true + * @param array $data + * @return Album */ - public static function construct_from_array($data) + public static function construct_from_array(array $data) { $album = new Album(0); foreach ($data as $key=>$value) { @@ -126,8 +243,10 @@ class Album extends database_object * build_cache * This takes an array of object ids and caches all of their information * with a single query + * @param array $ids + * @return boolean */ - public static function build_cache($ids) + public static function build_cache(array $ids) { // Nothing to do if they pass us nothing if (!is_array($ids) OR !count($ids)) { @@ -140,7 +259,7 @@ class Album extends database_object $db_results = Dba::read($sql); while ($row = Dba::fetch_assoc($db_results)) { - parent::add_to_cache('album',$row['id'],$row); + parent::add_to_cache('album', $row['id'], $row); } return true; @@ -151,9 +270,14 @@ class Album extends database_object * _get_extra_info * This pulls the extra information from our tables, this is a 3 table join, which is why we don't normally * do it + * @return array */ - private function _get_extra_info() + private function _get_extra_info($limit_threshold = '') { + if (!$this->id) { + return array(); + } + if (parent::is_cached('album_extra', $this->id)) { return parent::get_from_cache('album_extra', $this->id); } @@ -203,7 +327,7 @@ class Album extends database_object $results['has_thumb'] = make_bool($art->thumb); if (AmpConfig::get('show_played_times')) { - $results['object_cnt'] = Stats::get_object_count('album', $this->id); + $results['object_cnt'] = Stats::get_object_count('album', $this->id, $limit_threshold); } parent::add_to_cache('album_extra', $this->id, $results); @@ -212,18 +336,55 @@ class Album extends database_object } // _get_extra_info + public function can_edit($user = null) + { + if (!$user) { + $user = $GLOBALS['user']->id; + } + + if (!$user) + return false; + + if ($this->user !== null && $user == $this->user) + return true; + + if (Access::check('interface', 50, $user)) + return true; + + if (!$this->album_artist) + return false; + + if (!AmpConfig::get('upload_allow_edit')) + return false; + + $owner = $this->get_user_owner(); + return ($owner && $owner === $user); + } + /** * check * * Searches for an album; if none is found, insert a new one. + * @param string $name + * @param int $year + * @param int $disk + * @param string $mbid + * @param string $mbid_group + * @param string $album_artist + * @param string $release_type + * @param boolean $readonly + * @return int|null */ - public static function check($name, $year = 0, $disk = 0, $mbid = null, $readonly = false) + public static function check($name, $year = 0, $disk = 0, $mbid = null, $mbid_group = null, $album_artist = null, $release_type = null, $readonly = false) { - if ($mbid == '') $mbid = null; - $trimmed = Catalog::trim_prefix(trim($name)); $name = $trimmed['string']; $prefix = $trimmed['prefix']; + $album_artist = intval($album_artist); + $album_artist = ($album_artist <= 0) ? null : $album_artist; + $mbid = empty($mbid) ? null : $mbid; + $mbid_group = empty($mbid_group) ? null : $mbid_group; + $release_type = empty($release_type) ? null : $release_type; // Not even sure if these can be negative, but better safe than llama. $year = abs(intval($year)); @@ -233,35 +394,36 @@ class Album extends database_object $name = T_('Unknown (Orphaned)'); $year = 0; $disk = 0; + $album_artist = null; + } + if (isset(self::$_mapcache[$name][$disk][$mbid][$album_artist])) { + return self::$_mapcache[$name][$disk][$mbid][$album_artist]; } - if (isset(self::$_mapcache[$name][$year][$disk][$mbid])) { - return self::$_mapcache[$name][$year][$disk][$mbid]; - } - - $sql = 'SELECT `id` FROM `album` WHERE `name` = ? AND `disk` = ? AND `year` = ? AND `mbid` '; - $params = array($name, $disk, $year); + $sql = 'SELECT `album`.`id` FROM `album` WHERE `album`.`name` = ? AND `album`.`disk` = ? '; + $params = array($name, $disk); if ($mbid) { - $sql .= '= ? '; + $sql .= 'AND `album`.`mbid` = ? '; $params[] = $mbid; } else { - $sql .= 'IS NULL '; + $sql .= 'AND `album`.`mbid` IS NULL '; + } + if ($prefix) { + $sql .= 'AND `album`.`prefix` = ? '; + $params[] = $prefix; } - $sql .= 'AND `prefix` '; - if ($prefix) { - $sql .= '= ?'; - $params[] = $prefix; - } else { - $sql .= 'IS NULL'; + if ($album_artist) { + $sql .= 'AND `album`.`album_artist` = ? '; + $params[] = $album_artist; } $db_results = Dba::read($sql, $params); if ($row = Dba::fetch_assoc($db_results)) { $id = $row['id']; - self::$_mapcache[$name][$year][$disk][$mbid] = $id; + self::$_mapcache[$name][$disk][$mbid][$album_artist] = $id; return $id; } @@ -269,9 +431,9 @@ class Album extends database_object return null; } - $sql = 'INSERT INTO `album` (`name`, `prefix`, `year`, `disk`, `mbid`) VALUES (?, ?, ?, ?, ?)'; + $sql = 'INSERT INTO `album` (`name`, `prefix`, `year`, `disk`, `mbid`, `mbid_group`, `release_type`, `album_artist`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)'; - $db_results = Dba::write($sql, array($name, $prefix, $year, $disk, $mbid)); + $db_results = Dba::write($sql, array($name, $prefix, $year, $disk, $mbid, $mbid_group, $release_type, $album_artist)); if (!$db_results) { return null; } @@ -287,7 +449,7 @@ class Album extends database_object } } - self::$_mapcache[$name][$year][$disk][$mbid] = $id; + self::$_mapcache[$name][$disk][$mbid][$album_artist] = $id; return $id; } @@ -296,6 +458,9 @@ class Album extends database_object * gets the songs for this album takes an optional limit * and an optional artist, if artist is passed it only gets * songs with this album + specified artist + * @param int $limit + * @param string $artist + * @return int[] */ public function get_songs($limit = 0,$artist='') { @@ -331,6 +496,8 @@ class Album extends database_object /** * get_http_album_query_ids * return the html album parameters with all album suite ids + * @param string $url_param_name + * @return string */ public function get_http_album_query_ids($url_param_name) { @@ -346,6 +513,7 @@ class Album extends database_object /** * get_group_disks_ids * return all album suite ids or current album if no albums + * @return int[] */ public function get_group_disks_ids() { @@ -360,14 +528,16 @@ class Album extends database_object /** * get_album_suite * gets the album ids with the same musicbrainz identifier + * @param int $catalog + * return int[] */ - public function get_album_suite($catalog = '') + public function get_album_suite($catalog = 0) { $results = array(); $catalog_where = ""; $catalog_join = "LEFT JOIN `catalog` ON `catalog`.`id` = `song`.`catalog`"; - if (!empty($catalog)) { + if ($catalog) { $catalog_where .= " AND `catalog`.`id` = '$catalog'"; } if (AmpConfig::get('catalog_disable')) { @@ -390,6 +560,8 @@ class Album extends database_object /** * has_track * This checks to see if this album has a track of the specified title + * @param string $title + * @return array */ public function has_track($title) { @@ -402,39 +574,69 @@ class Album extends database_object } // has_track + /** + * get_addtime_first_song + * Get the add date of first added song. + * @return int + */ + public function get_addtime_first_song() + { + $time = 0; + + $sql = "SELECT MIN(`addition_time`) FROM `song` WHERE `album` = ?"; + $db_results = Dba::read($sql, array($this->id)); + if ($data = Dba::fetch_row($db_results)) { + $time = $data[0]; + } + + return $time; + } + /** * format * This is the format function for this object. It sets cleaned up * album information with the base required * f_link, f_name */ - public function format() + public function format($details = true, $limit_threshold = '') { $web_path = AmpConfig::get('web_path'); - /* Pull the advanced information */ - $data = $this->_get_extra_info(); - foreach ($data as $key=>$value) { $this->$key = $value; } + if ($details) { + /* Pull the advanced information */ + $data = $this->_get_extra_info($limit_threshold); + foreach ($data as $key=>$value) { $this->$key = $value; } + + if ($this->album_artist) { + $Album_artist = new Artist($this->album_artist); + $Album_artist->format(); + $this->album_artist_name = $Album_artist->name; + $this->f_album_artist_name = $Album_artist->f_name; + $this->f_album_artist_link = "album_artist . "\" title=\"" . scrub_out($this->album_artist_name) . "\">" . $this->f_album_artist_name . ""; + } + + $this->tags = Tag::get_top_tags('album', $this->id); + $this->f_tags = Tag::get_display($this->tags, true, 'album'); + } /* Truncate the string if it's to long */ $this->f_name = $this->full_name; - $this->f_link_src = $web_path . '/albums.php?action=show&album=' . scrub_out($this->id); - $this->f_name_link = "f_link_src . "\" title=\"" . scrub_out($this->full_name) . "\">" . scrub_out($this->f_name); + $this->link = $web_path . '/albums.php?action=show&album=' . scrub_out($this->id); + $this->f_link = "link . "\" title=\"" . scrub_out($this->full_name) . "\">" . scrub_out($this->f_name); // Looking if we need to combine or display disks if ($this->disk && (!$this->allow_group_disks || ($this->allow_group_disks && !AmpConfig::get('album_group')))) { - $this->f_name_link .= " [" . T_('Disk') . " " . $this->disk . "]"; + $this->f_link .= " [" . T_('Disk') . " " . $this->disk . "]"; } - $this->f_name_link .=""; + $this->f_link .=""; - $this->f_link = $this->f_name_link; $this->f_title = $this->full_name; if ($this->artist_count == '1') { $artist = trim(trim($this->artist_prefix) . ' ' . trim($this->artist_name)); $this->f_artist_name = $artist; - $this->f_artist_link = "artist_id . "\" title=\"" . scrub_out($this->artist_name) . "\">" . $artist . ""; + $this->f_artist_link = "artist_id . "\" title=\"" . scrub_out($this->artist_name) . "\">" . $artist . ""; $this->f_artist = $artist; } else { $this->f_artist_link = "artist_count " . T_('Artists') . "\">" . T_('Various') . ""; @@ -442,18 +644,180 @@ class Album extends database_object $this->f_artist_name = $this->f_artist; } - if ($this->year == '0') { - $this->year = "N/A"; + if (!$this->year) { + $this->f_year = "N/A"; } - $this->tags = Tag::get_top_tags('album', $this->id); - $this->f_tags = Tag::get_display($this->tags); + $this->f_release_type = ucwords($this->release_type); } // format + /** + * Get item keywords for metadata searches. + * @return array + */ + public function get_keywords() + { + $keywords = array(); + $keywords['mb_albumid'] = array('important' => false, + 'label' => T_('Album MusicBrainzID'), + 'value' => $this->mbid); + $keywords['mb_albumid_group'] = array('important' => false, + 'label' => T_('Release Group MusicBrainzID'), + 'value' => $this->mbid_group); + $keywords['artist'] = array('important' => true, + 'label' => T_('Artist'), + 'value' => (($this->artist_count < 2) ? $this->f_artist_name : '')); + $keywords['album'] = array('important' => true, + 'label' => T_('Album'), + 'value' => $this->f_name); + + return $keywords; + } + + /** + * Get item fullname. + * @return string + */ + public function get_fullname() + { + return $this->f_name; + } + + /** + * Get parent item description. + * @return array|null + */ + public function get_parent() + { + if ($this->artist_count == 1) { + return array('object_type' => 'artist', 'object_id' => $this->artist_id); + } + + return null; + } + + /** + * Get item childrens. + * @return array + */ + public function get_childrens() + { + return $this->get_medias(); + } + + /** + * Search for item childrens. + * @param string $name + * @return array + */ + public function search_childrens($name) + { + $search['type'] = "song"; + $search['rule_0_input'] = $name; + $search['rule_0_operator'] = 4; + $search['rule_0'] = "title"; + $search['rule_1_input'] = $this->name; + $search['rule_1_operator'] = 4; + $search['rule_1'] = "album"; + $search['rule_2_input'] = $this->album_artist_name; + $search['rule_2_operator'] = 4; + $search['rule_2'] = "artist"; + $songs = Search::run($search); + + $childrens = array(); + foreach ($songs as $song) { + $childrens[] = array( + 'object_type' => 'song', + 'object_id' => $song + ); + } + + return $childrens; + } + + /** + * Get all childrens and sub-childrens medias. + * @param string $filter_type + * @return array + */ + public function get_medias($filter_type = null) + { + $medias = array(); + if (!$filter_type || $filter_type == 'song') { + $songs = $this->get_songs(); + foreach ($songs as $song_id) { + $medias[] = array( + 'object_type' => 'song', + 'object_id' => $song_id + ); + } + } + return $medias; + } + + /** + * get_catalogs + * + * Get all catalog ids related to this item. + * @return int[] + */ + public function get_catalogs() + { + return array($this->catalog_id); + } + + /** + * Get item's owner. + * @return int|null + */ + public function get_user_owner() + { + if (!$this->album_artist) + return null; + + $artist = new Artist($this->album_artist); + return $artist->get_user_owner(); + } + + /** + * Get default art kind for this item. + * @return string + */ + public function get_default_art_kind() + { + return 'default'; + } + + public function get_description() + { + // Album description is not supported yet, always return artist description + $artist = new Artist($this->artist_id); + return $artist->get_description(); + } + + public function display_art($thumb = 2) + { + $id = null; + $type = null; + + if (Art::has_db($this->id, 'album')) { + $id = $this->id; + $type = 'album'; + } else if (Art::has_db($this->artist_id, 'artist')) { + $id = $this->artist_id; + $type = 'artist'; + } + + if ($id !== null && $type !== null) { + Art::display($type, $id, $this->get_fullname(), $thumb, $this->link); + } + } + /** * get_random_songs * gets a random number, and a random assortment of songs from this album + * @return int[] */ public function get_random_songs() { @@ -481,17 +845,27 @@ class Album extends database_object * update * This function takes a key'd array of data and updates this object * as needed + * @param array $data + * @return int */ - public function update($data) + public function update(array $data) { - $year = $data['year']; - $artist = $data['artist']; - $name = $data['name']; - $disk = $data['disk']; - $mbid = $data['mbid']; + $year = isset($data['year']) ? $data['year'] : $this->year; + $artist = isset($data['artist']) ? intval($data['artist']) : $this->artist_id; + $album_artist = isset($data['album_artist']) ? intval($data['album_artist']) : $this->album_artist; + $name = isset($data['name']) ? $data['name'] : $this->name; + $disk = isset($data['disk']) ? $data['disk']: $this->disk; + $mbid = isset($data['mbid']) ? $data['mbid'] : $this->mbid; + $mbid_group = isset($data['mbid_group']) ? $data['mbid_group'] : $this->mbid_group; + $release_type = isset($data['release_type']) ? $data['release_type'] : $this->release_type; $current_id = $this->id; + if (!empty($data['album_artist_name'])) { + // Need to create new artist according the name + $album_artist = Artist::check($data['album_artist_name']); + } + $updated = false; $songs = null; if ($artist != $this->artist_id AND $artist) { @@ -504,17 +878,31 @@ class Album extends database_object Artist::gc(); } - $album_id = self::check($name, $year, $disk, $mbid); + $album_id = self::check($name, $year, $disk, $mbid, $mbid_group, $album_artist, $release_type); if ($album_id != $this->id) { if (!is_array($songs)) { $songs = $this->get_songs(); } foreach ($songs as $song_id) { Song::update_album($album_id,$song_id); Song::update_year($year,$song_id); + Song::write_id3_for_song($song_id); } $current_id = $album_id; $updated = true; + Stats::migrate('album', $this->id, $album_id); + Art::migrate('album', $this->id, $album_id); self::gc(); + } else { + Album::update_year($year, $album_id); + Album::update_mbid_group($mbid_group, $album_id); + Album::update_release_type($release_type, $album_id); } + $this->year = $year; + $this->mbid_group = $mbid_group; + $this->release_type = $release_type; + $this->name = $name; + $this->disk = $disk; + $this->mbid = $mbid; + $this->album_artist = $album_artist; if ($updated && is_array($songs)) { foreach ($songs as $song_id) { @@ -525,11 +913,19 @@ class Album extends database_object Userflag::gc(); } // if updated - $override_songs = false; - if ($data['apply_childs'] == 'checked') { - $override_songs = true; + $override_childs = false; + if ($data['overwrite_childs'] == 'checked') { + $override_childs = true; + } + + $add_to_childs = false; + if ($data['add_to_childs'] == 'checked') { + $add_to_childs = true; + } + + if (isset($data['edit_tags'])) { + $this->update_tags($data['edit_tags'], $override_childs, $add_to_childs, $current_id, true); } - $this->update_tags($data['edit_tags'], $override_songs, $current_id); return $current_id; @@ -539,27 +935,106 @@ class Album extends database_object * update_tags * * Update tags of albums and/or songs + * @param string $tags_comma + * @param boolean $override_childs + * @param boolean $add_to_childs + * @param int|null $current_id */ - public function update_tags($tags_comma, $override_songs, $current_id = null) + public function update_tags($tags_comma, $override_childs, $add_to_childs, $current_id = null, $force_update = false) { if ($current_id == null) { $current_id = $this->id; } - Tag::update_tag_list($tags_comma, 'album', $current_id); + // When current_id not empty we force to overwrite current object + Tag::update_tag_list($tags_comma, 'album', $current_id, $force_update ? true : $override_childs); - if ($override_songs) { + if ($override_childs || $add_to_childs) { $songs = $this->get_songs(); foreach ($songs as $song_id) { - Tag::update_tag_list($tags_comma, 'song', $song_id); + Tag::update_tag_list($tags_comma, 'song', $song_id, $override_childs); } } } + public function remove_from_disk() + { + $deleted = true; + $song_ids = $this->get_songs(); + foreach ($song_ids as $id) { + $song = new Song($id); + $deleted = $song->remove_from_disk(); + if (!$deleted) { + debug_event('album', 'Error when deleting the song `' . $id .'`.', 1); + break; + } + } + + if ($deleted) { + $sql = "DELETE FROM `album` WHERE `id` = ?"; + $deleted = Dba::write($sql, array($this->id)); + if ($deleted) { + Art::gc('album', $this->id); + Userflag::gc('album', $this->id); + Rating::gc('album', $this->id); + Shoutbox::gc('album', $this->id); + } + } + + return $deleted; + } + + /** + * Update album year. + * @param int $year + * @param int $album_id + */ + public static function update_year($year, $album_id) + { + self::update_field('year', $year, $album_id); + } + + /** + * Update album mbid group. + * @param string $mbid_group + * @param int $album_id + */ + public static function update_mbid_group($mbid_group, $album_id) + { + $mbid_group = (!empty($mbid_group)) ? $mbid_group : null; + self::update_field('mbid_group', $mbid_group, $album_id); + } + + /** + * Update album release type. + * @param string $release_type + * @param int $album_id + */ + public static function update_release_type($release_type, $album_id) + { + $release_type = (!empty($release_type)) ? $release_type : null; + self::update_field('release_type', $release_type, $album_id); + } + + /** + * Update an album field. + * @param string $field + * @param int $album_id + * @return boolean + */ + private static function update_field($field, $value, $album_id) + { + $sql = "UPDATE `album` SET `" . $field . "` = ? WHERE `id` = ?"; + return Dba::write($sql, array($value, $album_id)); + } + /** * get_random * * This returns a number of random albums. + * @param int $count + * @param boolean $with_art + * @return int[] */ public static function get_random($count = 1, $with_art = false) { @@ -569,7 +1044,7 @@ class Album extends database_object $count = 1; } - $sql = "SELECT `album`.`id` FROM `album` " . + $sql = "SELECT DISTINCT `album`.`id` FROM `album` " . "LEFT JOIN `song` ON `song`.`album` = `album`.`id` "; if (AmpConfig::get('catalog_disable')) { $sql .= "LEFT JOIN `catalog` ON `catalog`.`id` = `song`.`catalog` "; diff --git a/sources/lib/class/ampache_rss.class.php b/sources/lib/class/ampache_rss.class.php index 3c00107..2704716 100644 --- a/sources/lib/class/ampache_rss.class.php +++ b/sources/lib/class/ampache_rss.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -27,6 +27,9 @@ */ class Ampache_RSS { + /** + * @var string $type + */ private $type; public $data; @@ -44,33 +47,56 @@ class Ampache_RSS * get_xml * This returns the xmldocument for the current rss type, it calls a sub function that gathers the data * and then uses the xmlDATA class to build the document + * @return string */ - public function get_xml() + public function get_xml($params = null) { - // Function call name - $data_function = 'load_' . $this->type; - $pub_date_function = 'pubdate_' . $this->type; + if ($this->type === "podcast") { + if ($params != null && is_array($params)) { + $object_type = $params['object_type']; + $object_id = $params['object_id']; + if (Core::is_library_item($object_type)) { + $libitem = new $object_type($object_id); + if ($libitem->id) { + $libitem->format(); + return XML_Data::podcast($libitem); + } + } + } + } else { + // Function call name + $data_function = 'load_' . $this->type; + $pub_date_function = 'pubdate_' . $this->type; - $data = call_user_func(array('Ampache_RSS',$data_function)); - $pub_date = call_user_func(array('Ampache_RSS',$pub_date_function)); + $data = call_user_func(array('Ampache_RSS',$data_function)); + $pub_date = null; + if (method_exists('Ampache_RSS', $data_function)) { + $pub_date = call_user_func(array('Ampache_RSS',$pub_date_function)); + } - XML_Data::set_type('rss'); - $xml_document = XML_Data::rss_feed($data,$this->get_title(),$this->get_description(),$pub_date); + XML_Data::set_type('rss'); + $xml_document = XML_Data::rss_feed($data,$this->get_title(),$this->get_description(),$pub_date); - return $xml_document; + return $xml_document; + } + + return null; } // get_xml /** * get_title * This returns the standardized title for the rss feed based on this->type + * @return string */ public function get_title() { $titles = array('now_playing' => T_('Now Playing'), - 'recently_played' => T_('Recently Played'), - 'latest_album' => T_('Newest Albums'), - 'latest_artist' => T_('Newest Artists')); + 'recently_played' => T_('Recently Played'), + 'latest_album' => T_('Newest Albums'), + 'latest_artist' => T_('Newest Artists'), + 'latest_shout' => T_('Newest Shouts') + ); return scrub_out(AmpConfig::get('site_title')) . ' - ' . $titles[$this->type]; @@ -79,6 +105,7 @@ class Ampache_RSS /** * get_description * This returns the standardized description for the rss feed based on this->type + * @return string */ public function get_description() { @@ -90,11 +117,12 @@ class Ampache_RSS /** * validate_type * this returns a valid type for an rss feed, if the specified type is invalid it returns a default value + * @param string $type + * @return string */ public static function validate_type($type) { - $valid_types = array('now_playing','recently_played','latest_album','latest_artist','latest_song', - 'popular_song','popular_album','popular_artist'); + $valid_types = array('now_playing','recently_played','latest_album','latest_artist','latest_shout','podcast'); if (!in_array($type,$valid_types)) { return 'now_playing'; @@ -107,13 +135,28 @@ class Ampache_RSS /** * get_display * This dumps out some html and an icon for the type of rss that we specify + * @param string $type + * @param string $title + * @param array|null $params + * @return string */ - public static function get_display($type='now_playing') + public static function get_display($type='now_playing', $title = '', $params = null) { // Default to now playing $type = self::validate_type($type); - $string = '' . UI::get_icon('feed', T_('RSS Feed')) . ''; + $strparams = ""; + if ($params != null && is_array($params)) { + foreach ($params as $key => $value) { + $strparams .= "&" . scrub_out($key) . "=" . scrub_out($value); + } + } + + $string = '' . UI::get_icon('feed', T_('RSS Feed')); + if (!empty($title)) { + $string .= '  ' . $title; + } + $string .= ''; return $string; @@ -125,6 +168,7 @@ class Ampache_RSS * load_now_playing * This loads in the now playing information. This is just the raw data with key=>value pairs that could be turned * into an xml document if we so wished + * @return array */ public static function load_now_playing() { @@ -152,7 +196,7 @@ class Ampache_RSS 'title' => $title, 'link' => $song->link, 'description' => $description, - 'comments' => $client->fullname . ' - ' . $element['agent'], + 'comments' => $client->f_name . ' - ' . $element['agent'], 'pubDate' => date('r', $element['expire']) ); $results[] = $xml_array; @@ -166,6 +210,7 @@ class Ampache_RSS * pubdate_now_playing * this is the pub date we should use for the now playing information, * this is a little specific as it uses the 'newest' expire we can find + * @return int */ public static function pubdate_now_playing() { @@ -181,6 +226,7 @@ class Ampache_RSS /** * load_recently_played * This loads in the recently played information and formats it up real nice like + * @return array */ public static function load_recently_played() { @@ -237,9 +283,104 @@ class Ampache_RSS } // load_recently_played + /** + * load_latest_album + * This loads in the latest added albums + * @return array + */ + public static function load_latest_album() + { + $ids = Stats::get_newest('album', 10); + + $results = array(); + + foreach ($ids as $id) { + $album = new Album($id); + $album->format(); + + $xml_array = array('title' => $album->f_name, + 'link' => $album->link, + 'description' => $album->f_artist_name . ' - ' . $album->f_name, + 'image' => Art::url($album->id, 'album', null, 2), + 'comments' => '', + 'pubDate' => date("c", $album->get_addtime_first_song()) + ); + $results[] = $xml_array; + + } // end foreach + + return $results; + + } // load_latest_album + + /** + * load_latest_artist + * This loads in the latest added artists + * @return array + */ + public static function load_latest_artist() + { + $ids = Stats::get_newest('artist', 10); + + $results = array(); + + foreach ($ids as $id) { + $artist = new Artist($id); + $artist->format(); + + $xml_array = array('title' => $artist->f_name, + 'link' => $artist->link, + 'description' => $artist->summary, + 'image' => Art::url($artist->id, 'artist', null, 2), + 'comments' => '', + 'pubDate' => '' + ); + $results[] = $xml_array; + + } // end foreach + + return $results; + + } // load_latest_artist + + /** + * load_latest_shout + * This loads in the latest added shouts + * @return array + */ + public static function load_latest_shout() + { + $ids = Shoutbox::get_top(10); + + $results = array(); + + foreach ($ids as $id) { + $shout = new Shoutbox($id); + $shout->format(); + $object = Shoutbox::get_object($shout->object_type, $shout->object_id); + $object->format(); + $user = new User($shout->user); + $user->format(); + + $xml_array = array('title' => $user->username . ' ' . T_('on') . ' ' . $object->get_fullname(), + 'link' => $object->link, + 'description' => $shout->text, + 'image' => Art::url($shout->object_id, $shout->object_type, null, 2), + 'comments' => '', + 'pubDate' => date("c", $shout->date) + ); + $results[] = $xml_array; + + } // end foreach + + return $results; + + } // load_latest_shout + /** * pubdate_recently_played * This just returns the 'newest' recently played entry + * @return int */ public static function pubdate_recently_played() { diff --git a/sources/lib/class/ampconfig.class.php b/sources/lib/class/ampconfig.class.php index 665cfaf..ccb5177 100644 --- a/sources/lib/class/ampconfig.class.php +++ b/sources/lib/class/ampconfig.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -35,6 +35,9 @@ */ class AmpConfig { + /** + * @var array $_global + */ private static $_global = array(); public function __construct() @@ -46,6 +49,7 @@ class AmpConfig * get * * This returns a config value. + * @param string $name */ public static function get($name) { @@ -60,6 +64,7 @@ class AmpConfig * get_all * * This returns all of the current config variables as an array. + * @return array */ public static function get_all() { @@ -70,6 +75,8 @@ class AmpConfig * set * * This sets config values. + * @param string $name + * @param boolean $clobber */ public static function set($name, $value, $clobber = false) { @@ -87,6 +94,8 @@ class AmpConfig * * This is the same as the set function except it takes an array as * input. + * @param array $array + * @param boolean $clobber */ public static function set_by_array($array, $clobber = false) { diff --git a/sources/lib/class/api.class.php b/sources/lib/class/api.class.php index 6e22ca5..e7cd5b6 100644 --- a/sources/lib/class/api.class.php +++ b/sources/lib/class/api.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -30,9 +30,18 @@ */ class Api { + /** + * @var string $auth_version + */ public static $auth_version = '350001'; - public static $version = '370001'; + /** + * @var string $version + */ + public static $version = '380001'; + /** + * @var Browse $browse + */ private static $browse = null; /** @@ -62,6 +71,9 @@ class Api * the filters in a slightly different and vastly simpler way to the * end users--so we have to do a little extra work to make them work * internally. + * @param string $filter + * @param int|string|boolean|null $value + * @return boolean */ public static function set_filter($filter,$value) { @@ -108,6 +120,8 @@ class Api * * This is the function that handles verifying a new handshake * Takes a timestamp, auth key, and username. + * @param array + * @return boolean */ public static function handshake($input) { @@ -155,6 +169,7 @@ class Api ($timestamp > (time() + 1800))) { debug_event('API', 'Login Failed: timestamp out of range ' . $timestamp . '/' . time(), 1); Error::add('api', T_('Login Failed: timestamp out of range')); + echo XML_Data::error('401', T_('Error Invalid Handshake - ') . T_('Login Failed: timestamp out of range')); return false; } @@ -166,6 +181,7 @@ class Api if (!$realpwd) { debug_event('API', 'Unable to find user with userid of ' . $user_id, 1); Error::add('api', T_('Invalid Username/Password')); + echo XML_Data::error('401', T_('Error Invalid Handshake - ') . T_('Invalid Username/Password')); return false; } @@ -184,6 +200,18 @@ class Api $data['username'] = $client->username; $data['type'] = 'api'; $data['value'] = $timestamp; + if (isset($input['client'])) { + $data['agent'] = $input['client']; + } + if (isset($input['geo_latitude'])) { + $data['geo_latitude'] = $input['geo_latitude']; + } + if (isset($input['geo_longitude'])) { + $data['geo_longitude'] = $input['geo_longitude']; + } + if (isset($input['geo_name'])) { + $data['geo_name'] = $input['geo_name']; + } $token = Session::create($data); debug_event('API', 'Login Success, passphrase matched', 1); @@ -233,14 +261,16 @@ class Api } // end while debug_event('API','Login Failed, unable to match passphrase','1'); - XML_Data::error('401', T_('Error Invalid Handshake - ') . T_('Invalid Username/Password')); + echo XML_Data::error('401', T_('Error Invalid Handshake - ') . T_('Invalid Username/Password')); + return false; } // handshake /** * ping * This can be called without being authenticated, it is useful for determining if what the status * of the server is, and what version it is running/compatible with + * @param array $input */ public static function ping($input) { @@ -264,6 +294,7 @@ class Api * This takes a collection of inputs and returns * artist objects. This function is deprecated! * //DEPRECATED + * @param array $input */ public static function artists($input) { @@ -291,6 +322,7 @@ class Api * artist * This returns a single artist based on the UID of said artist * //DEPRECATED + * @param array $input */ public static function artist($input) { @@ -302,6 +334,7 @@ class Api /** * artist_albums * This returns the albums of an artist + * @param array $input */ public static function artist_albums($input) { @@ -320,6 +353,7 @@ class Api /** * artist_songs * This returns the songs of the specified artist + * @param array $input */ public static function artist_songs($input) { @@ -337,6 +371,7 @@ class Api /** * albums * This returns albums based on the provided search filters + * @param array $input */ public static function albums($input) { @@ -361,6 +396,7 @@ class Api /** * album * This returns a single album based on the UID provided + * @param array $input */ public static function album($input) { @@ -372,6 +408,7 @@ class Api /** * album_songs * This returns the songs of a specified album + * @param array $input */ public static function album_songs($input) { @@ -390,6 +427,7 @@ class Api /** * tags * This returns the tags based on the specified filter + * @param array $input */ public static function tags($input) { @@ -413,6 +451,7 @@ class Api /** * tag * This returns a single tag based on UID + * @param array $input */ public static function tag($input) { @@ -425,38 +464,43 @@ class Api /** * tag_artists * This returns the artists associated with the tag in question as defined by the UID + * @param array $input */ public static function tag_artists($input) { $artists = Tag::get_tag_objects('artist',$input['filter']); + if ($artists) { + XML_Data::set_offset($input['offset']); + XML_Data::set_limit($input['limit']); - XML_Data::set_offset($input['offset']); - XML_Data::set_limit($input['limit']); - - ob_end_clean(); - echo XML_Data::artists($artists); + ob_end_clean(); + echo XML_Data::artists($artists); + } } // tag_artists /** * tag_albums * This returns the albums associated with the tag in question + * @param array $input */ public static function tag_albums($input) { $albums = Tag::get_tag_objects('album',$input['filter']); + if ($albums) { + XML_Data::set_offset($input['offset']); + XML_Data::set_limit($input['limit']); - XML_Data::set_offset($input['offset']); - XML_Data::set_limit($input['limit']); - - ob_end_clean(); - echo XML_Data::albums($albums); + ob_end_clean(); + echo XML_Data::albums($albums); + } } // tag_albums /** * tag_songs * returns the songs for this tag + * @param array $input */ public static function tag_songs($input) { @@ -473,6 +517,7 @@ class Api /** * songs * Returns songs based on the specified filter + * @param array $input */ public static function songs($input) { @@ -499,6 +544,7 @@ class Api /** * song * returns a single song + * @param array $input */ public static function song($input) { @@ -513,6 +559,7 @@ class Api * url_to_song * * This takes a url and returns the song object in question + * @param array $input */ public static function url_to_song($input) { @@ -525,6 +572,7 @@ class Api /** * playlists * This returns playlists based on the specified filter + * @param array $input */ public static function playlists($input) { @@ -548,6 +596,7 @@ class Api /** * playlist * This returns a single playlist + * @param array $input */ public static function playlist($input) { @@ -561,6 +610,7 @@ class Api /** * playlist_songs * This returns the songs for a playlist + * @param array $input */ public static function playlist_songs($input) { @@ -577,13 +627,14 @@ class Api XML_Data::set_offset($input['offset']); XML_Data::set_limit($input['limit']); ob_end_clean(); - echo XML_Data::songs($songs); + echo XML_Data::songs($songs,$items); } // playlist_songs /** * playlist_create * This create a new playlist and return it + * @param array $input */ public static function playlist_create($input) { @@ -600,6 +651,7 @@ class Api /** * playlist_delete * This delete a playlist + * @param array $input */ public static function playlist_delete($input) { @@ -616,12 +668,13 @@ class Api /** * playlist_add_song * This add a song to a playlist + * @param array $input */ public static function playlist_add_song($input) { ob_end_clean(); $playlist = new Playlist($input['filter']); - $song = new Playlist($input['song']); + $song = $input['song']; if (!$playlist->has_access()) { echo XML_Data::error('401', T_('Access denied to this playlist.')); } else { @@ -634,12 +687,13 @@ class Api /** * playlist_remove_song * This remove a song from a playlist + * @param array $input */ public static function playlist_remove_song($input) { ob_end_clean(); $playlist = new Playlist($input['filter']); - $track = new Playlist($input['track']); + $track = scrub_in($input['track']); if (!$playlist->has_access()) { echo XML_Data::error('401', T_('Access denied to this playlist.')); } else { @@ -652,6 +706,7 @@ class Api /** * search_songs * This searches the songs and returns... songs + * @param array $input */ public static function search_songs($input) { @@ -675,6 +730,7 @@ class Api /** * videos * This returns video objects! + * @param array $input */ public static function videos($input) { @@ -697,6 +753,7 @@ class Api /** * video * This returns a single video + * @param array $input */ public static function video($input) { @@ -710,6 +767,7 @@ class Api /** * localplay * This is for controling localplay + * @param array $input */ public static function localplay($input) { @@ -737,6 +795,7 @@ class Api /** * democratic * This is for controlling democratic play + * @param array $input */ public static function democratic($input) { @@ -781,7 +840,7 @@ class Api $objects = $democratic->get_items(); Song::build_cache($democratic->object_ids); Democratic::build_vote_cache($democratic->vote_ids); - XML_Data::democratic($objects); + echo XML_Data::democratic($objects); break; case 'play': $url = $democratic->play_url(); @@ -795,12 +854,18 @@ class Api } // democratic + /** + * This get library stats. + * @param array $input + */ public static function stats($input) { $type = $input['type']; $offset = $input['offset']; $limit = $input['limit']; + $username = $input['username']; + $albums = null; if ($type == "newest") { $albums = Stats::get_newest("album", $limit, $offset); } else if ($type == "highest") { @@ -808,7 +873,16 @@ class Api } else if ($type == "frequent") { $albums = Stats::get_top("album", $limit, '', $offset); } else if ($type == "recent") { - $albums = Stats::get_recent("album", $limit, $offset); + if (!empty($username)) { + $user = User::get_from_username($username); + if ($user !== null) { + $albums = $user->get_recently_played($limit, 'album'); + } else { + debug_event('api', 'User `' . $username . '` cannot be found.', 1); + } + } else { + $albums = Stats::get_recent("album", $limit, $offset); + } } else if ($type == "flagged") { $albums = Userflag::get_latest('album'); } else { @@ -818,8 +892,109 @@ class Api $albums = Album::get_random($limit); } - ob_end_clean(); - echo XML_Data::albums($albums); - } + if ($albums !== null) { + ob_end_clean(); + echo XML_Data::albums($albums); + } + } // stats + + /** + * user + * This get an user public information + * @param array $input + */ + public static function user($input) + { + $username = $input['username']; + if (!empty($username)) { + $user = User::get_from_username($username); + if ($user !== null) { + ob_end_clean(); + echo XML_Data::user($user); + } else { + debug_event('api', 'User `' . $username . '` cannot be found.', 1); + } + } else { + debug_event('api', 'Username required on user function call.', 1); + } + } // user + + /** + * followers + * This get an user followers + * @param array $input + */ + public static function followers($input) + { + if (AmpConfig::get('sociable')) { + $username = $input['username']; + if (!empty($username)) { + $user = User::get_from_username($username); + if ($user !== null) { + $users = $user->get_followers(); + ob_end_clean(); + echo XML_Data::users($user); + } else { + debug_event('api', 'User `' . $username . '` cannot be found.', 1); + } + } else { + debug_event('api', 'Username required on followers function call.', 1); + } + } else { + debug_event('api', 'Sociable feature is not enabled.', 3); + } + } // followers + + /** + * following + * This get the user list followed by an user + * @param array $input + */ + public static function following($input) + { + if (AmpConfig::get('sociable')) { + $username = $input['username']; + if (!empty($username)) { + $user = User::get_from_username($username); + if ($user !== null) { + $users = $user->get_following(); + ob_end_clean(); + echo XML_Data::users($user); + } else { + debug_event('api', 'User `' . $username . '` cannot be found.', 1); + } + } else { + debug_event('api', 'Username required on following function call.', 1); + } + } else { + debug_event('api', 'Sociable feature is not enabled.', 3); + } + } // following + + /** + * last_shouts + * This get the latest posted shouts + * @param array $input + */ + public static function last_shouts($input) + { + $limit = intval($input['limit']); + if ($limit < 1) { + $limit = AmpConfig::get('popular_threshold'); + } + if (AmpConfig::get('sociable')) { + $username = $input['username']; + if (!empty($username)) { + $shouts = Shoutbox::get_top($limit, $username); + } else { + $shouts = Shoutbox::get_top($limit); + } + + ob_end_clean(); + echo XML_Data::shouts($shouts); + } else { + debug_event('api', 'Sociable feature is not enabled.', 3); + } + } // last_shouts } // API class diff --git a/sources/lib/class/art.class.php b/sources/lib/class/art.class.php index 41c371f..afe2765 100644 --- a/sources/lib/class/art.class.php +++ b/sources/lib/class/art.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -32,25 +32,60 @@ use MusicBrainz\Clients\RequestsMbClient; */ class Art extends database_object { + /** + * @var int $id + */ + public $id; + /** + * @var string $type + */ public $type; + /** + * @var int $uid + */ public $uid; // UID of the object not ID because it's not the ART.ID + /** + * @var string $raw + */ public $raw; // Raw art data + /** + * @var string $raw_mime + */ public $raw_mime; + /** + * @var string $kind + */ + public $kind; + /** + * @var string $thumb + */ public $thumb; + /** + * @var string $thumb_mime + */ public $thumb_mime; + /** + * @var bool $enabled + */ private static $enabled; /** * Constructor * Art constructor, takes the UID of the object and the * object type. + * @param int $uid + * @param string $type + * @param string $kind */ - public function __construct($uid, $type = 'album') + public function __construct($uid, $type = 'album', $kind = 'default') { - $this->type = Art::validate_type($type); - $this->uid = $uid; + if (!Core::is_library_item($type)) + return false; + $this->type = $type; + $this->uid = intval($uid); + $this->kind = $kind; } // constructor @@ -59,6 +94,8 @@ class Art extends database_object * This attempts to reduce # of queries by asking for everything in the * browse all at once and storing it in the cache, this can help if the * db connection is the slow point + * @param int[] $object_ids + * @return bool */ public static function build_cache($object_ids) { @@ -97,6 +134,7 @@ class Art extends database_object /** * is_enabled * Checks whether the user currently wants art + * @return boolean */ public static function is_enabled() { @@ -110,6 +148,7 @@ class Art extends database_object /** * set_enabled * Changes the value of enabled + * @param bool|null $value */ public static function set_enabled($value = null) { @@ -123,27 +162,11 @@ class Art extends database_object //setcookie('art_enabled', self::$enabled, time() + 31536000, "/"); } - /** - * validate_type - * This validates the type - */ - public static function validate_type($type) - { - switch ($type) { - case 'album': - case 'artist': - case 'video': - case 'user': - return $type; - default: - return 'album'; - } - - } // validate_type - /** * extension * This returns the file extension for the currently loaded art + * @param string $mime + * @return string */ public static function extension($mime) { @@ -159,6 +182,8 @@ class Art extends database_object /** * test_image * Runs some sanity checks on the putative image + * @param string $source + * @return boolean */ public static function test_image($source) { @@ -167,17 +192,25 @@ class Art extends database_object return false; } + // Check image size doesn't exceed the limit + if (strlen($source) > AmpConfig::get('max_upload_size')) { + debug_event('Art', 'Image size (' . strlen($source) . ') exceed the limit (' . AmpConfig::get('max_upload_size') . ').', 1); + return false; + } + + $test = true; // Check to make sure PHP:GD exists. If so, we can sanity check // the image. if (function_exists('ImageCreateFromString')) { $image = ImageCreateFromString($source); if (!$image || imagesx($image) < 5 || imagesy($image) < 5) { debug_event('Art', 'Image failed PHP-GD test',1); - return false; + $test = false; } + @imagedestroy($image); } - return true; + return $test; } //test_image /** @@ -186,6 +219,8 @@ class Art extends database_object * look in the database and will return the thumb if it * exists, if it doesn't depending on settings it will try * to create it. + * @param boolean $raw + * @return string */ public function get($raw=false) { @@ -208,38 +243,45 @@ class Art extends database_object * This pulls the information out from the database, depending * on if we want to resize and if there is not a thumbnail go * ahead and try to resize + * @return boolean */ public function get_db() { - $type = Dba::escape($this->type); - $id = Dba::escape($this->uid); - - $sql = "SELECT `image`, `mime`, `size` FROM `image` WHERE `object_type`='$type' AND `object_id`='$id'"; - $db_results = Dba::read($sql); + $sql = "SELECT `id`, `image`, `mime`, `size` FROM `image` WHERE `object_type` = ? AND `object_id` = ? AND `kind` = ?"; + $db_results = Dba::read($sql, array($this->type, $this->uid, $this->kind)); while ($results = Dba::fetch_assoc($db_results)) { if ($results['size'] == 'original') { - $this->raw = $results['image']; + if (AmpConfig::get('album_art_store_disk')) { + $this->raw = self::read_from_dir($results['size'], $this->type, $this->uid, $this->kind); + } else { + $this->raw = $results['image']; + } $this->raw_mime = $results['mime']; - } else if (AmpConfig::get('resize_images') && - $results['size'] == '275x275') { - $this->thumb = $results['image']; + } else if (AmpConfig::get('resize_images') && $results['size'] == '275x275') { + if (AmpConfig::get('album_art_store_disk')) { + $this->thumb = self::read_from_dir($results['size'], $this->type, $this->uid, $this->kind); + } else { + $this->thumb = $results['image']; + } $this->raw_mime = $results['mime']; } + $this->id = $results['id']; } // If we get nothing return false if (!$this->raw) { return false; } // If there is no thumb and we want thumbs if (!$this->thumb && AmpConfig::get('resize_images')) { - $data = $this->generate_thumb($this->raw, array('width' => 275, 'height' => 275), $this->raw_mime); + $size = array('width' => 275, 'height' => 275); + $data = $this->generate_thumb($this->raw, $size, $this->raw_mime); // If it works save it! if ($data) { - $this->save_thumb($data['thumb'], $data['thumb_mime'], '275x275'); + $this->save_thumb($data['thumb'], $data['thumb_mime'], $size); $this->thumb = $data['thumb']; $this->thumb_mime = $data['thumb_mime']; } else { - debug_event('Art','Unable to retrieve or generate thumbnail for ' . $type . '::' . $id,1); + debug_event('Art','Unable to retrieve or generate thumbnail for ' . $this->type . '::' . $this->id,1); } } // if no thumb, but art and we want to resize @@ -247,12 +289,60 @@ class Art extends database_object } // get_db + /** + * This check if an object has an associated image in db. + * @param int $object_id + * @param string $object_type + * @param string $kind + * @return boolean + */ + public static function has_db($object_id, $object_type, $kind = 'default') + { + $sql = "SELECT COUNT(`id`) AS `nb_img` FROM `image` WHERE `object_type` = ? AND `object_id` = ? AND `kind` = ?"; + $db_results = Dba::read($sql, array($object_type, $object_id, $kind)); + $nb_img = 0; + if ($results = Dba::fetch_assoc($db_results)) { + $nb_img = $results['nb_img']; + } + + return ($nb_img > 0); + } + + /** + * This insert art from url. + * @param string $url + */ + public function insert_url($url) + { + debug_event('art', 'Insert art from url ' . $url, '5'); + $image = Art::get_from_source(array('url' => $url), $this->type); + $rurl = pathinfo($url); + $mime = "image/" . $rurl['extension']; + $this->insert($image, $mime); + } + + /** + * This insert art from file on disk. + * @param string $filepath + */ + public function insert_from_file($filepath) + { + debug_event('art', 'Insert art from file on disk ' . $filepath, '5'); + $image = Art::get_from_source(array('file' => $filepath), $this->type); + $rfile = pathinfo($filepath); + $mime = "image/" . $rfile['extension']; + $this->insert($image, $mime); + } + /** * insert * This takes the string representation of an image and inserts it into * the database. You must also pass the mime type. + * @param string $source + * @param string $mime + * @return boolean */ - public function insert($source, $mime) + public function insert($source, $mime = '') { // Disabled in demo mode cause people suck and upload porn if (AmpConfig::get('demo_mode')) { return false; } @@ -265,36 +355,199 @@ class Art extends database_object // Default to image/jpeg if they don't pass anything $mime = $mime ? $mime : 'image/jpeg'; - - $image = Dba::escape($source); - $mime = Dba::escape($mime); - $uid = Dba::escape($this->uid); - $type = Dba::escape($this->type); - // Blow it away! $this->reset(); + if (AmpConfig::get('write_id3_art')) { + if ($this->type == 'album') { + $album = new Album($this->uid ); + debug_event('Art', 'Inserting image Album ' . $album->name . ' on songs.', 5); + $songs = $album->get_songs(); + foreach ($songs as $song_id) { + $song = new Song($song_id); + $song->format(); + $id3 = new vainfo($song->file); + $data = $id3->read_id3(); + if (isset($data['tags']['id3v2'])) { + $image_from_tag = ''; + if (isset($data['id3v2']['APIC'][0]['data'])) { + $image_from_tag = $data['id3v2']['APIC'][0]['data']; + } + if ($image_from_tag != $source) { + $ndata = array(); + $ndata['APIC']['data'] = $source; + $ndata['APIC']['mime'] = $mime; + $ndata = array_merge($ndata, $song->get_metadata()); + $id3->write_id3($ndata); + } + } + } + } + } + + $dimensions = Core::image_dimensions($source); + $width = intval($dimensions['width']); + $height = intval($dimensions['height']); + $sizetext = 'original'; + + if (!self::check_dimensions($dimensions)) { + return false; + } + + if (AmpConfig::get('album_art_store_disk')) { + self::write_to_dir($source, $sizetext, $this->type, $this->uid, $this->kind); + $source = null; + } + // Insert it! - $sql = "INSERT INTO `image` (`image`, `mime`, `size`, `object_type`, `object_id`) VALUES('$image', '$mime', 'original', '$type', '$uid')"; - Dba::write($sql); + $sql = "INSERT INTO `image` (`image`, `mime`, `size`, `width`, `height`, `object_type`, `object_id`, `kind`) VALUES(?, ?, ?, ?, ?, ?, ?, ?)"; + Dba::write($sql, array($source, $mime, $sizetext, $width, $height, $this->type, $this->uid, $this->kind)); return true; } // insert + public static function check_dimensions($dimensions) + { + $w = intval($dimensions['width']); + $h = intval($dimensions['height']); + + if ($w > 0 && $h > 0) { + $minw = AmpConfig::get('album_art_min_width'); + $maxw = AmpConfig::get('album_art_max_width'); + $minh = AmpConfig::get('album_art_min_height'); + $maxh = AmpConfig::get('album_art_max_height'); + + if ($minw > 0 && ($w < $minw || $w > $maxw)) { + debug_event('Art', 'Image width not in range.', 1); + return false; + } + + if ($minh > 0 && ($h < $minh || $h > $maxh)) { + debug_event('Art', 'Image height not in range.', 1); + return false; + } + } + + return true; + } + + private static function get_dir_on_disk($type, $uid, $kind = '', $autocreate = false) + { + $path = AmpConfig::get('local_metadata_dir'); + if (!$path) { + debug_event('Art', 'local_metadata_dir setting is required to store arts on disk.', 1); + return false; + } + + // Correctly detect the slash we need to use here + if (strpos($path, '/') !== false) { + $slash_type = '/'; + } else { + $slash_type = '\\'; + } + + $path .= $slash_type . $type; + if ($autocreate && !Core::is_readable($path)) { + mkdir($path); + } + + $path .= $slash_type . $uid; + if ($autocreate && !Core::is_readable($path)) { + mkdir($path); + } + + if (!empty($kind)) { + $path .= $slash_type . $kind; + if ($autocreate && !Core::is_readable($path)) { + mkdir($path); + } + } + $path .= $slash_type; + + return $path; + } + + private static function write_to_dir($source, $sizetext, $type, $uid, $kind) + { + $path = self::get_dir_on_disk($type, $uid, $kind, true); + if ($path === false) { + return false; + } + $path .= "art-" . $sizetext . ".jpg"; + if (Core::is_readable($path)) { + unlink($path); + } + $fp = fopen($path, "wb"); + fwrite($fp, $source); + fclose ($fp); + + return true; + } + + private static function read_from_dir($sizetext, $type, $uid, $kind) + { + $path = self::get_dir_on_disk($type, $uid, $kind); + if ($path === false) { + return null; + } + $path .= "art-" . $sizetext . ".jpg"; + if (!Core::is_readable($path)) { + debug_event('Art', 'Local image art ' . $path . ' cannot be read.', 1); + return null; + } + + $image = ''; + $fp = fopen($path, "rb"); + do { + $image .= fread($fp, 2048); + } while (!feof($fp)); + fclose($fp); + + return $image; + } + + private static function delete_from_dir($type, $uid, $kind = '') + { + if ($type && $uid) { + $path = self::get_dir_on_disk($type, $uid, $kind); + self::delete_rec_dir($path); + } + } + + private static function delete_rec_dir($path) + { + debug_event('Art', 'Deleting ' . $path . ' directory...', 5); + + if (Core::is_readable($path)) { + foreach (scandir($path) as $file) { + if ('.' === $file || '..' === $file) continue; + elseif (is_dir($path . '/' . $file)) self::delete_rec_dir($path . '/' . $file); + else unlink($path . '/' . $file); + } + rmdir($path); + } + } + /** * reset * This resets the art in the database */ public function reset() { - $sql = "DELETE FROM `image` WHERE `object_id` = ? AND `object_type` = ?"; - Dba::write($sql, array($this->uid, $this->type)); + if (AmpConfig::get('album_art_store_disk')) { + self::delete_from_dir($this->type, $this->uid, $this->kind); + } + $sql = "DELETE FROM `image` WHERE `object_id` = ? AND `object_type` = ? AND `kind` = ?"; + Dba::write($sql, array($this->uid, $this->type, $this->kind)); } // reset /** * save_thumb * This saves the thumbnail that we're passed + * @param string $source + * @param string $mime + * @param array $size */ public function save_thumb($source, $mime, $size) { @@ -304,44 +557,45 @@ class Art extends database_object return false; } - $source = Dba::escape($source); - $mime = Dba::escape($mime); - $size = Dba::escape($size); - $uid = Dba::escape($this->uid); - $type = Dba::escape($this->type); + $width = intval($size['width']); + $height = intval($size['height']); + $sizetext = $width . 'x' . $height; - $sql = "DELETE FROM `image` WHERE `object_id`='$uid' AND `object_type`='$type' AND `size`='$size'"; - Dba::write($sql); + $sql = "DELETE FROM `image` WHERE `object_id` = ? AND `object_type` = ? AND `size` = ? AND `kind` = ?"; + Dba::write($sql, array($this->uid, $this->type, $sizetext, $this->kind)); - $sql = "INSERT INTO `image` (`image`, `mime`, `size`, `object_type`, `object_id`) VALUES('$source', '$mime', '$size', '$type', '$uid')"; - Dba::write($sql); + if (AmpConfig::get('album_art_store_disk')) { + self::write_to_dir($source, $sizetext, $this->type, $this->uid, $this->kind); + $source = null; + } + $sql = "INSERT INTO `image` (`image`, `mime`, `size`, `width`, `height`, `object_type`, `object_id`, `kind`) VALUES(?, ?, ?, ?, ?, ?, ?, ?)"; + Dba::write($sql, array($source, $mime, $sizetext, $width, $height, $this->type, $this->uid, $this->kind)); } // save_thumb /** * get_thumb * Returns the specified resized image. If the requested size doesn't * already exist, create and cache it. + * @param array $size + * @return string */ public function get_thumb($size) { $sizetext = $size['width'] . 'x' . $size['height']; - $sizetext = Dba::escape($sizetext); - $type = Dba::escape($this->type); - $uid = Dba::escape($this->uid); - - $sql = "SELECT `image`, `mime` FROM `image` WHERE `size`='$sizetext' AND `object_type`='$type' AND `object_id`='$uid'"; - $db_results = Dba::read($sql); + $sql = "SELECT `image`, `mime` FROM `image` WHERE `size` = ? AND `object_type` = ? AND `object_id` = ? AND `kind` = ?"; + $db_results = Dba::read($sql, array($sizetext, $this->type, $this->uid, $this->kind)); $results = Dba::fetch_assoc($db_results); if (count($results)) { - return array('thumb' => $results['image'], + return array( + 'thumb' => (AmpConfig::get('album_art_store_disk')) ? self::read_from_dir($sizetext, $this->type, $this->uid, $this->kind) : $results['image'], 'thumb_mime' => $results['mime']); } // If we didn't get a result $results = $this->generate_thumb($this->raw, $size, $this->raw_mime); if ($results) { - $this->save_thumb($results['thumb'], $results['thumb_mime'], $sizetext); + $this->save_thumb($results['thumb'], $results['thumb_mime'], $size); } return $results; @@ -353,8 +607,12 @@ class Art extends database_object * Automatically resizes the image for thumbnail viewing. * Only works on gif/jpg/png/bmp. Fails if PHP-GD isn't available * or lacks support for the requested image type. + * @param string $image + * @param array $size + * @param string $mime + * @return string */ - public function generate_thumb($image,$size,$mime) + public function generate_thumb($image, $size, $mime) { $data = explode("/",$mime); $type = strtolower($data['1']); @@ -401,8 +659,11 @@ class Art extends database_object if (!imagecopyresampled($thumbnail, $source, 0, 0, 0, 0, $size['width'], $size['height'], $source_size['width'], $source_size['height'])) { debug_event('Art','Unable to create resized image',1); + imagedestroy($source); + imagedestroy($thumbnail); return false; } + imagedestroy($source); // Start output buffer ob_start(); @@ -434,6 +695,7 @@ class Art extends database_object $data = ob_get_contents(); ob_end_clean(); + imagedestroy($thumbnail); if (!strlen($data)) { debug_event('Art', 'Unknown Error resizing art', 1); return false; @@ -451,6 +713,9 @@ class Art extends database_object * ['url'] = URL *** OPTIONAL *** * ['file'] = FILENAME *** OPTIONAL *** * ['raw'] = Actual Image data, already captured + * @param array $data + * @param string $type + * @return string|null */ public static function get_from_source($data, $type = 'album') { @@ -461,12 +726,8 @@ class Art extends database_object // If it came from the database if (isset($data['db'])) { - // Repull it - $uid = Dba::escape($data['db']); - $type = Dba::escape($type); - - $sql = "SELECT * FROM `image` WHERE `object_type`='$type' AND `object_id`='$uid' AND `size`='original'"; - $db_results = Dba::read($sql); + $sql = "SELECT * FROM `image` WHERE `object_type` = ? AND `object_id` =? AND `size`='original'"; + $db_results = Dba::read($sql, array($type, $data['db'])); $row = Dba::fetch_assoc($db_results); return $row['art']; } // came from the db @@ -474,23 +735,22 @@ class Art extends database_object // Check to see if it's a URL if (isset($data['url'])) { $options = array(); - if (AmpConfig::get('proxy_host') AND AmpConfig::get('proxy_port')) { - $proxy = array(); - $proxy[] = AmpConfig::get('proxy_host') . ':' . AmpConfig::get('proxy_port'); - if (AmpConfig::get('proxy_user')) { - $proxy[] = AmpConfig::get('proxy_user'); - $proxy[] = AmpConfig::get('proxy_pass'); - } - $options['proxy'] = $proxy; + try { + $options['timeout'] = 3; + $request = Requests::get($data['url'], array(), Core::requests_options($options)); + $raw = $request->body; + } catch (Exception $e) { + debug_event('Art', 'Error getting art: ' . $e->getMessage(), '1'); + $raw = null; } - $request = Requests::get($data['url'], array(), $options); - return $request->body; + + return $raw; } // Check to see if it's a FILE if (isset($data['file'])) { $handle = fopen($data['file'],'rb'); - $image_data = fread($handle,filesize($data['file'])); + $image_data = fread($handle,Core::get_filesize($data['file'])); fclose($handle); return $image_data; } @@ -511,18 +771,32 @@ class Art extends database_object } } // if data song - return false; + return null; } // get_from_source /** * url * This returns the constructed URL for the art in question + * @param int $uid + * @param string $type + * @param string $sid + * @param int|null $thumb + * @return string */ - public static function url($uid,$type,$sid=false) + public static function url($uid,$type,$sid=null,$thumb=null) { - $sid = $sid ? scrub_out($sid) : scrub_out(session_id()); - $type = self::validate_type($type); + if (!Core::is_library_item($type)) + return null; + + if (AmpConfig::get('use_auth') && AmpConfig::get('require_session')) { + $sid = $sid ? scrub_out($sid) : scrub_out(session_id()); + if ($sid == null) { + $sid = Session::create(array( + 'type' => 'api' + )); + } + } $key = $type . $uid; @@ -551,8 +825,25 @@ class Art extends database_object $mime = isset($thumb_mime) ? $thumb_mime : (isset($mime) ? $mime : null); $extension = self::extension($mime); - $name = 'art.' . $extension; - $url = AmpConfig::get('web_path') . '/image.php?id=' . scrub_out($uid) . '&object_type=' . scrub_out($type) . '&auth=' . $sid . '&name=' . $name; + if (AmpConfig::get('stream_beautiful_url')) { + if (empty($extension)) { + $extension = 'jpg'; + } + $url = AmpConfig::get('web_path') . '/play/art/' . $sid . '/' . scrub_out($type) . '/' . scrub_out($uid) . '/thumb'; + if ($thumb) { + $url .= $thumb; + } + $url .= '.' . $extension; + } else { + $url = AmpConfig::get('web_path') . '/image.php?object_id=' . scrub_out($uid) . '&object_type=' . scrub_out($type) . '&auth=' . $sid; + if ($thumb) { + $url .= '&thumb=' . $thumb; + } + if (!empty($extension)) { + $name = 'art.' . $extension; + $url .= '&name=' . $name; + } + } return $url; @@ -562,38 +853,99 @@ class Art extends database_object * gc * This cleans up art that no longer has a corresponding object */ - public static function gc() + public static function gc($object_type = null, $object_id = null) { - // iterate over our types and delete the images - foreach (array('album', 'artist') as $type) { - $sql = "DELETE FROM `image` USING `image` LEFT JOIN `" . - $type . "` ON `" . $type . "`.`id`=" . - "`image`.`object_id` WHERE `object_type`='" . - $type . "' AND `" . $type . "`.`id` IS NULL"; - Dba::write($sql); - } // foreach + $types = array('album', 'artist','tvshow','tvshow_season','video','user'); + + if ($object_type != null) { + if (in_array($object_type, $types)) { + if (AmpConfig::get('album_art_store_disk')) { + self::delete_from_dir($object_type, $object_id); + } + $sql = "DELETE FROM `image` WHERE `object_type` = ? AND `object_id` = ?"; + Dba::write($sql, array($object_type, $object_id)); + } else { + debug_event('art', 'Garbage collect on type `' . $object_type . '` is not supported.', 1); + } + } else { + // iterate over our types and delete the images + foreach ($types as $type) { + if (AmpConfig::get('album_art_store_disk')) { + $sql = "SELECT `image`.`object_id`, `image`.`object_type` FROM `image` LEFT JOIN `" . + $type . "` ON `" . $type . "`.`id`=" . + "`image`.`object_id` WHERE `object_type`='" . + $type . "' AND `" . $type . "`.`id` IS NULL"; + $db_results = Dba::read($sql); + while ($row = Dba::fetch_row($db_results)) { + self::delete_from_dir($row[1], $row[0]); + } + } + $sql = "DELETE FROM `image` USING `image` LEFT JOIN `" . + $type . "` ON `" . $type . "`.`id`=" . + "`image`.`object_id` WHERE `object_type`='" . + $type . "' AND `" . $type . "`.`id` IS NULL"; + Dba::write($sql); + } // foreach + } + } + + /** + * Migrate an object associate images to a new object + * @param string $object_type + * @param int $old_object_id + * @param int $new_object_id + * @return boolean + */ + public static function migrate($object_type, $old_object_id, $new_object_id) + { + $sql = "UPDATE `image` SET `object_id` = ? WHERE `object_type` = ? AND `object_id` = ?"; + return Dba::write($sql, array($new_object_id, $object_type, $old_object_id)); + } + + /** + * Duplicate an object associate images to a new object + * @param string $object_type + * @param int $old_object_id + * @param int $new_object_id + * @return boolean + */ + public static function duplicate($object_type, $old_object_id, $new_object_id) + { + if (AmpConfig::get('album_art_store_disk')) { + $sql = "SELECT `size`, `kind` FROM `image` WHERE `object_type` = ? AND `object_id` = ?"; + $db_results = Dba::read($sql, array($object_type, $old_object_id)); + while ($row = Dba::fetch_assoc($db_results)) { + $image = self::read_from_dir($row['size'], $object_type, $old_object_id, $row['kind']); + if ($image != null) { + self::write_to_dir($image, $row['size'], $object_type, $new_object_id, $row['kind']); + } + } + } + + $sql = "INSERT INTO `image` (`image`, `mime`, `size`, `object_type`, `object_id`, `kind`) SELECT `image`, `mime`, `size`, `object_type`, ? as `object_id`, `kind` FROM `image` WHERE `object_type` = ? AND `object_id` = ?"; + return Dba::write($sql, array($new_object_id, $object_type, $old_object_id)); } /** * gather * This tries to get the art in question + * @param array $options + * @param int $limit + * @return array */ - public function gather($options = array(), $limit = false) + public function gather($options = array(), $limit = 0) { // Define vars $results = array(); - - switch ($this->type) { - case 'album': - $allowed_methods = array('db','lastfm','folder','amazon','google','musicbrainz','tags'); - break; - case 'artist': - case 'video': - default: - $allowed_methods = array(); - break; + $type = $this->type; + if (isset($options['type'])) { + $type = $options['type']; } + if (count($options) == 0) { + debug_event('Art', 'No options for art search, skipped.', 3); + return array(); + } $config = AmpConfig::get('art_order'); $methods = get_class_methods('Art'); @@ -608,40 +960,42 @@ class Art extends database_object debug_event('Art','Searching using:' . json_encode($config), 3); + $plugin_names = Plugin::get_plugins('gather_arts'); foreach ($config as $method) { - - if (!in_array($method, $allowed_methods)) { - debug_event('Art', "$method not in allowed_methods, skipping", 3); - continue; - } - $method_name = "gather_" . $method; - if (in_array($method_name, $methods)) { + $data = array(); + if (in_array($method, $plugin_names)) { + $plugin = new Plugin($method); + $installed_version = Plugin::get_plugin_version($plugin->_plugin->name); + if ($installed_version) { + if ($plugin->load($GLOBALS['user'])) { + $data = $plugin->_plugin->gather_arts($type, $options, $limit); + } + } + } else if (in_array($method_name, $methods)) { debug_event('Art', "Method used: $method_name", 3); // Some of these take options! switch ($method_name) { - case 'gather_amazon': - $data = $this->{$method_name}($limit, $options['keyword']); - break; case 'gather_lastfm': $data = $this->{$method_name}($limit, $options); break; + case 'gather_google': + $data = $this->{$method_name}($limit, $options); + break; default: $data = $this->{$method_name}($limit); break; } + } else { + debug_event("Art", $method_name . " not defined", 1); + } - // Add the results we got to the current set - $results = array_merge($results, (array) $data); + // Add the results we got to the current set + $results = array_merge($results, (array) $data); - if ($limit && count($results) >= $limit) { - return array_slice($results, 0, $limit); - } - - } // if the method exists - else { - debug_event("Art", "$method_name not defined", 1); + if ($limit && count($results) >= $limit) { + return array_slice($results, 0, $limit); } } // end foreach @@ -658,6 +1012,8 @@ class Art extends database_object * gather_db * This function retrieves art that's already in the database * + * @param int|null $limit + * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function gather_db($limit = null) @@ -672,20 +1028,21 @@ class Art extends database_object * gather_musicbrainz * This function retrieves art based on MusicBrainz' Advanced * Relationships + * @param int $limit + * @param array $data + * @return array */ - public function gather_musicbrainz($limit = 5) + public function gather_musicbrainz($limit = 5, $data = array()) { $images = array(); $num_found = 0; - if ($this->type == 'album') { - $album = new Album($this->uid); - } else { + if ($this->type != 'album') { return $images; } - if ($album->mbid) { - debug_event('mbz-gatherart', "Album MBID: " . $album->mbid, '5'); + if ($data['mbid']) { + debug_event('mbz-gatherart', "Album MBID: " . $data['mbid'], '5'); } else { return $images; } @@ -695,12 +1052,12 @@ class Art extends database_object 'url-rels' ); try { - $release = $mb->lookup('release', $album->mbid, $includes); + $release = $mb->lookup('release', $data['mbid'], $includes); } catch (Exception $e) { return $images; } - $asin = $release->asin; + $asin = $release['asin']; if ($asin) { debug_event('mbz-gatherart', "Found ASIN: " . $asin, '5'); @@ -715,23 +1072,14 @@ class Art extends database_object // to avoid complicating things even further, we only look for large cover art $url = 'http://' . $base_url . '/images/P/' . $asin . '.' . $server_num . '.LZZZZZZZ.jpg'; debug_event('mbz-gatherart', "Evaluating Amazon URL: " . $url, '5'); - $options = array(); - if (AmpConfig::get('proxy_host') AND AmpConfig::get('proxy_port')) { - $proxy = array(); - $proxy[] = AmpConfig::get('proxy_host') . ':' . AmpConfig::get('proxy_port'); - if (AmpConfig::get('proxy_user')) { - $proxy[] = AmpConfig::get('proxy_user'); - $proxy[] = AmpConfig::get('proxy_pass'); - } - $options['proxy'] = $proxy; - } - $request = Requests::get($url, array(), $options); + $request = Requests::get($url, array(), Core::requests_options()); if ($request->status_code == 200) { $num_found++; debug_event('mbz-gatherart', "Amazon URL added: " . $url, '5'); $images[] = array( 'url' => $url, 'mime' => 'image/jpeg', + 'title' => 'MusicBrainz' ); if ($num_found >= $limit) { return $images; @@ -814,6 +1162,7 @@ class Art extends database_object $images[] = array( 'url' => $url, 'mime' => 'image/jpeg', + 'title' => 'MusicBrainz' ); if ($num_found >= $limit) { return $images; @@ -827,152 +1176,20 @@ class Art extends database_object } // gather_musicbrainz - /** - * gather_amazon - * This takes keywords and performs a search of the Amazon website - * for the art. It returns an array of found objects with mime/url keys - */ - public function gather_amazon($limit = 5, $keywords = '') - { - $images = array(); - $final_results = array(); - $possible_keys = array( - 'LargeImage', - 'MediumImage', - 'SmallImage' - ); - - if ($this->type == 'album') { - $album = new Album($this->uid); - } else { - return $images; - } - - // Prevent the script from timing out - set_time_limit(0); - - if (empty($keywords)) { - $keywords = $album->full_name; - /* If this isn't a various album combine with artist name */ - if ($album->artist_count == '1') { $keywords .= ' ' . $album->artist_name; } - } - - /* Attempt to retrieve the album art order */ - $amazon_base_urls = AmpConfig::get('amazon_base_urls'); - - /* If it's not set */ - if (!count($amazon_base_urls)) { - $amazon_base_urls = array('http://webservices.amazon.com'); - } - - /* Foreach through the base urls that we should check */ - foreach ($amazon_base_urls as $amazon_base) { - - // Create the Search Object - $amazon = new AmazonSearch(AmpConfig::get('amazon_developer_public_key'), AmpConfig::get('amazon_developer_private_key'), AmpConfig::get('amazon_developer_associate_tag'), $amazon_base); - if (AmpConfig::get('proxy_host') AND AmpConfig::get('proxy_port')) { - $proxyhost = AmpConfig::get('proxy_host'); - $proxyport = AmpConfig::get('proxy_port'); - $proxyuser = AmpConfig::get('proxy_user'); - $proxypass = AmpConfig::get('proxy_pass'); - debug_event('amazon', 'setProxy', 5); - $amazon->setProxy($proxyhost, $proxyport, $proxyuser, $proxypass); - } - - $search_results = array(); - - /* Set up the needed variables */ - $max_pages_to_search = max(AmpConfig::get('max_amazon_results_pages'),$amazon->_default_results_pages); - // while we have pages to search - do { - $raw_results = $amazon->search(array('artist'=>'', 'album'=>'', 'keywords'=>$keywords)); - $total = count($raw_results) + count($search_results); - - // If we've gotten more then we wanted - if ($limit && $total > $limit) { - $raw_results = array_slice($raw_results, 0, -($total - $limit), true); - - debug_event('amazon-xml', "Found $total, limit $limit; reducing and breaking from loop", 5); - // Merge the results and BREAK! - $search_results = array_merge($search_results,$raw_results); - break; - } // if limit defined - - $search_results = array_merge($search_results,$raw_results); - $pages_to_search = min($max_pages_to_search, $amazon->_maxPage); - debug_event('amazon-xml', "Searched results page " . ($amazon->_currentPage+1) . "/" . $pages_to_search,'5'); - $amazon->_currentPage++; - - } while ($amazon->_currentPage < $pages_to_search); - - - // Only do the second search if the first actually returns something - if (count($search_results)) { - $final_results = $amazon->lookup($search_results); - } - - /* Log this if we're doin debug */ - debug_event('amazon-xml',"Searched using $keywords with " . AmpConfig::get('amazon_developer_key') . " as key, results: " . count($final_results), 5); - - // If we've hit our limit - if (!empty($limit) && count($final_results) >= $limit) { - break; - } - - } // end foreach - - /* Foreach through what we've found */ - foreach ($final_results as $result) { - - $key = ''; - /* Recurse through the images found */ - foreach ($possible_keys as $k) { - if (strlen($result[$k])) { - $key = $k; - break; - } - } // foreach - - // Rudimentary image type detection, only JPG and GIF allowed. - if (substr($result[$key], -4) == '.jpg') { - $mime = "image/jpeg"; - } elseif (substr($result[$key], -4) == '.gif') { - $mime = "image/gif"; - } elseif (substr($result[$key], -4) == '.png') { - $mime = "image/png"; - } else { - /* Just go to the next result */ - continue; - } - - $data = array(); - $data['url'] = $result[$key]; - $data['mime'] = $mime; - - $images[] = $data; - - if (!empty($limit)) { - if (count($images) >= $limit) { - return $images; - } - } - - } // if we've got something - - return $images; - - } // gather_amazon - /** * gather_folder * This returns the art from the folder of the files * If a limit is passed or the preferred filename is found the current * results set is returned + * @param int $limit + * @return array */ public function gather_folder($limit = 5) { - $media = new Album($this->uid); - $songs = $media->get_songs(); + if (!$limit) { + $limit = 5; + } + $results = array(); $preferred = false; // For storing which directories we've already done @@ -991,10 +1208,20 @@ class Art extends database_object 'png' ); - foreach ($songs as $song_id) { - $song = new Song($song_id); - $dir = dirname($song->file); + $dirs = array(); + if ($this->type == 'album') { + $media = new Album($this->uid); + $songs = $media->get_songs(); + foreach ($songs as $song_id) { + $song = new Song($song_id); + $dirs[] = Core::conv_lc_file( dirname($song->file) ); + } + } else if ($this->type == 'video') { + $media = new Video($this->uid); + $dirs[] = Core::conv_lc_file( dirname($media->file) ); + } + foreach ($dirs as $dir) { if (isset($processed[$dir])) { continue; } @@ -1013,7 +1240,7 @@ class Art extends database_object $processed[$dir] = true; // Recurse through this dir and create the files array - while ($file = readdir($handle)) { + while (false !== ($file = readdir($handle))) { $extension = pathinfo($file); $extension = $extension['extension']; @@ -1025,7 +1252,7 @@ class Art extends database_object $full_filename = $dir . '/' . $file; // Make sure it's got something in it - if (!filesize($full_filename)) { + if (!Core::get_filesize($full_filename)) { debug_event('folder_art', "Empty file, rejecting $file", 5); continue; } @@ -1045,7 +1272,8 @@ class Art extends database_object debug_event('folder_art', "Found preferred image file: $file", 5); $preferred[$index] = array( 'file' => $full_filename, - 'mime' => 'image/' . $extension + 'mime' => 'image/' . $extension, + 'title' => 'Folder' ); break; } @@ -1053,13 +1281,14 @@ class Art extends database_object debug_event('folder_art', "Found image file: $file", 5); $results[$index] = array( 'file' => $full_filename, - 'mime' => 'image/' . $extension + 'mime' => 'image/' . $extension, + 'title' => 'Folder' ); } // end while reading dir closedir($handle); - } // end foreach songs + } // end foreach dirs if (is_array($preferred)) { // We found our favourite filename somewhere, so we need @@ -1080,8 +1309,42 @@ class Art extends database_object * gather_tags * This looks for the art in the meta-tags of the file * itself + * @param int $limit + * @return array */ public function gather_tags($limit = 5) + { + if (!$limit) { + $limit = 5; + } + + if ($this->type == "video") { + $data = $this->gather_video_tags(); + } elseif ($this->type == 'album') { + $data = $this->gather_song_tags($limit); + } else { + $data = array(); + } + + return $data; + } + + /** + * Gather tags from video files. + * @return array + */ + public function gather_video_tags() + { + $video = new Video($this->uid); + return $this->gather_media_tags($video); + } + + /** + * Gather tags from audio files. + * @param int $limit + * @return array + */ + public function gather_song_tags($limit = 5) { // We need the filenames $album = new Album($this->uid); @@ -1093,72 +1356,109 @@ class Art extends database_object // Foreach songs in this album foreach ($songs as $song_id) { $song = new Song($song_id); - // If we find a good one, stop looking - $getID3 = new getID3(); - try { $id3 = $getID3->analyze($song->file); } catch (Exception $error) { - debug_event('getid3', $error->getMessage(), 1); - } - - if (isset($id3['asf']['extended_content_description_object']['content_descriptors']['13'])) { - $image = $id3['asf']['extended_content_description_object']['content_descriptors']['13']; - $data[] = array( - 'song' => $song->file, - 'raw' => $image['data'], - 'mime' => $image['mime']); - } - - if (isset($id3['id3v2']['APIC'])) { - // Foreach in case they have more then one - foreach ($id3['id3v2']['APIC'] as $image) { - $data[] = array( - 'song' => $song->file, - 'raw' => $image['data'], - 'mime' => $image['mime']); - } - } + $data = array_merge($data, $this->gather_media_tags($song)); if ($limit && count($data) >= $limit) { return array_slice($data, 0, $limit); } + } - } // end foreach + return $data; + } + + /** + * Gather tags from files. + * @param media $media + * @return array + */ + protected function gather_media_tags($media) + { + $mtype = strtolower(get_class($media)); + $data = array(); + $getID3 = new getID3(); + try { $id3 = $getID3->analyze($media->file); } catch (Exception $error) { + debug_event('getid3', $error->getMessage(), 1); + } + + if (isset($id3['asf']['extended_content_description_object']['content_descriptors']['13'])) { + $image = $id3['asf']['extended_content_description_object']['content_descriptors']['13']; + $data[] = array( + $mtype => $media->file, + 'raw' => $image['data'], + 'mime' => $image['mime'], + 'title' => 'ID3'); + } + + if (isset($id3['id3v2']['APIC'])) { + // Foreach in case they have more then one + foreach ($id3['id3v2']['APIC'] as $image) { + $data[] = array( + $mtype => $media->file, + 'raw' => $image['data'], + 'mime' => $image['mime'], + 'title' => 'ID3'); + } + } + + if (isset($id3['comments']['picture']['0'])) { + $image = $id3['comments']['picture']['0']; + $data[] = array( + $mtype => $media->file, + 'raw' => $image['data'], + 'mime' => $image['image_mime'], + 'title' => 'ID3'); + return $data; + } return $data; - } // gather_tags + } /** * gather_google * Raw google search to retrieve the art, not very reliable * + * @param int $limit + * @param array $data + * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function gather_google($limit = 5) + public function gather_google($limit = 5, $data = array()) { + if (!$limit) { + $limit = 5; + } + $images = array(); - $media = new $this->type($this->uid); - $media->format(); - - $search = $media->full_name; - - if ($media->artist_count == '1') - $search = $media->artist_name . ', ' . $search; - - $search = rawurlencode($search); - + $search = rawurlencode($data['keyword']); $size = '&imgsz=m'; // Medium - //$size = '&imgsz=l'; // Large - $html = file_get_contents("http://images.google.com/images?source=hp&q=$search&oq=&um=1&ie=UTF-8&sa=N&tab=wi&start=0&tbo=1$size"); + $url = "http://images.google.com/images?source=hp&q=" . $search . "&oq=&um=1&ie=UTF-8&sa=N&tab=wi&start=0&tbo=1" . $size; + debug_event('Art', 'Search url: ' . $url, '5'); - if(preg_match_all("|\ssrc\=\"(http.+?)\"|", $html, $matches, PREG_PATTERN_ORDER)) - foreach ($matches[1] as $match) { - $extension = "image/jpeg"; + try { + // Need this to not be considered as a bot (are we? ^^) + $headers = array( + 'User-Agent' => 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11', + ); + $query = Requests::get($url, $headers, Core::requests_options()); + $html = $query->body; - if (strrpos($extension, '.') !== false) $extension = substr($extension, strrpos($extension, '.') + 1); + if (preg_match_all("|imgres\?imgurl\=(http.+?)&|", $html, $matches, PREG_PATTERN_ORDER)) { + foreach ($matches[1] as $match) { + $match = rawurldecode($match); + debug_event('Art', 'Found image at: ' . $match, '5'); + $results = pathinfo($match); + $mime = 'image/' . $results['extension']; - $images[] = array('url' => $match, 'mime' => $extension); + $images[] = array('url' => $match, 'mime' => $mime, 'title' => 'Google'); + if ($limit > 0 && count($images) >= $limit) + break; + } } + } catch (Exception $e) { + debug_event('Art', 'Error getting google images: ' . $e->getMessage(), '1'); + } return $images; @@ -1168,64 +1468,270 @@ class Art extends database_object * gather_lastfm * This returns the art from lastfm. It doesn't currently require an * account but may in the future. + * @param int $limit + * @param array $data + * @return array */ - public function gather_lastfm($limit, $options = false) + public function gather_lastfm($limit = 5, $data = array()) { - $data = array(); - // Create the parser object - $lastfm = new LastFMSearch(); + if (!$limit) { + $limit = 5; + } - switch ($this->type) { - case 'album': - if (is_array($options)) { - $artist = $options['artist']; - $album = $options['album_name']; - } else { - $media = new Album($this->uid); - $media->format(); - $artist = $media->artist_name; - $album = $media->full_name; + $images = array(); + + if ($this->type != 'album' || empty($data['artist']) || empty($data['album'])) { + return $images; + } + + try { + $xmldata = Recommendation::album_search($data['artist'], $data['album']); + + if (!count($xmldata)) { return array(); } + + $coverart = (array) $xmldata->coverart; + if (!$coverart) { return array(); } + + ksort($coverart); + foreach ($coverart as $url) { + // We need to check the URL for the /noimage/ stuff + if (strpos($url, '/noimage/') !== false) { + debug_event('LastFM', 'Detected as noimage, skipped ' . $url, 3); + continue; } - break; - default: - return $data; - } - if (AmpConfig::get('proxy_host') AND AmpConfig::get('proxy_port')) { - $proxyhost = AmpConfig::get('proxy_host'); - $proxyport = AmpConfig::get('proxy_port'); - $proxyuser = AmpConfig::get('proxy_user'); - $proxypass = AmpConfig::get('proxy_pass'); - debug_event('LastFM', 'proxy set', 5); - $lastfm->setProxy($proxyhost, $proxyport, $proxyuser, $proxypass); - } - - $raw_data = $lastfm->album_search($artist, $album); - - if (!count($raw_data)) { return array(); } - - $coverart = $raw_data['coverart']; - if (!is_array($coverart)) { return array(); } - - ksort($coverart); - foreach ($coverart as $url) { - // We need to check the URL for the /noimage/ stuff - if (strpos($url, '/noimage/') !== false) { - debug_event('LastFM', 'Detected as noimage, skipped ' . $url, 3); - continue; - } - - // HACK: we shouldn't rely on the extension to determine file type + // HACK: we shouldn't rely on the extension to determine file type $results = pathinfo($url); - $mime = 'image/' . $results['extension']; - $data[] = array('url' => $url, 'mime' => $mime); - if ($limit && count($data) >= $limit) { - return $data; - } - } // end foreach + $mime = 'image/' . $results['extension']; + $images[] = array('url' => $url, 'mime' => $mime, 'title' => 'LastFM'); + if ($limit && count($images) >= $limit) { + return $images; + } + } // end foreach + } catch (Exception $e) { + debug_event('art', 'LastFM error: ' . $e->getMessage(), 5); + } - return $data; + return $images; } // gather_lastfm + /** + * Gather metadata from plugin. + * @param string $type + * @param array $options + * @return array + */ + public static function gather_metadata_plugin($plugin, $type, $options) + { + $gtypes = array(); + $media_info = array(); + switch ($type) { + case 'tvshow': + case 'tvshow_season': + case 'tvshow_episode': + $gtypes[] = 'tvshow'; + $media_info['tvshow'] = $options['tvshow']; + $media_info['tvshow_season'] = $options['tvshow_season']; + $media_info['tvshow_episode'] = $options['tvshow_episode']; + break; + case 'song': + $media_info['mb_trackid'] = $options['mb_trackid']; + $media_info['title'] = $options['title']; + $media_info['artist'] = $options['artist']; + $media_info['album'] = $options['album']; + $gtypes[] = 'song'; + break; + case 'album': + $media_info['mb_albumid'] = $options['mb_albumid']; + $media_info['mb_albumid_group'] = $options['mb_albumid_group']; + $media_info['artist'] = $options['artist']; + $media_info['title'] = $options['album']; + $gtypes[] = 'music'; + $gtypes[] = 'album'; + break; + case 'artist': + $media_info['mb_artistid'] = $options['mb_artistid']; + $media_info['title'] = $options['artist']; + $gtypes[] = 'music'; + $gtypes[] = 'artist'; + break; + case 'movie': + $gtypes[] = 'movie'; + $media_info['title'] = $options['keyword']; + break; + } + + $meta = $plugin->get_metadata($gtypes, $media_info); + $images = array(); + + if ($meta['art']) { + $url = $meta['art']; + $ures = pathinfo($url); + $images[] = array('url' => $url, 'mime' => 'image/' . $ures['extension'], 'title' => $plugin->name); + } + if ($meta['tvshow_season_art']) { + $url = $meta['tvshow_season_art']; + $ures = pathinfo($url); + $images[] = array('url' => $url, 'mime' => 'image/' . $ures['extension'], 'title' => $plugin->name); + } + if ($meta['tvshow_art']) { + $url = $meta['tvshow_art']; + $ures = pathinfo($url); + $images[] = array('url' => $url, 'mime' => 'image/' . $ures['extension'], 'title' => $plugin->name); + } + + return $images; + } + + /** + * Get thumb size from thumb type. + * @param int $thumb + * @return array + */ + public static function get_thumb_size($thumb) + { + $size = array(); + + switch ($thumb) { + case 1: + /* This is used by the now_playing / browse stuff */ + $size['height'] = 100; + $size['width'] = 100; + break; + case 2: + $size['height'] = 128; + $size['width'] = 128; + break; + case 3: + /* This is used by the embedded web player */ + $size['height'] = 80; + $size['width'] = 80; + break; + case 4: + /* Web Player size */ + $size['height'] = 200; + $size['width'] = 200; // 200px width, set via CSS + break; + case 5: + /* Web Player size */ + $size['height'] = 32; + $size['width'] = 32; + break; + case 6: + /* Video browsing size */ + $size['height'] = 150; + $size['width'] = 100; + break; + case 7: + /* Video page size */ + $size['height'] = 300; + $size['width'] = 200; + break; + case 8: + /* Video preview size */ + $size['height'] = 200; + $size['width'] = 470; + break; + case 9: + /* Video preview size */ + $size['height'] = 100; + $size['width'] = 235; + break; + case 10: + /* Search preview size */ + $size['height'] = 24; + $size['width'] = 24; + break; + default: + $size['height'] = 275; + $size['width'] = 275; + break; + } + + return $size; + } + + /** + * Display an item art. + * @param library_item $item + * @param int $thumb + * @param string $link + * @return boolean + */ + public static function display_item($item, $thumb, $link = null) + { + return self::display($item->type, $item->id, $item->get_fullname(), $thumb, $link); + } + + /** + * Display an item art. + * @param string $object_type + * @param int $object_id + * @param string $name + * @param int $thumb + * @param string $link + * @param boolean $show_default + * @param string $kind + * @return boolean + */ + public static function display($object_type, $object_id, $name, $thumb, $link = null, $show_default = true, $kind = 'default') + { + if (!Core::is_library_item($object_type)) + return false; + + if (!$show_default) { + // Don't show any image if not available + if (!self::has_db($object_id, $object_type, $kind)) { + return false; + } + } + $size = self::get_thumb_size($thumb); + $prettyPhoto = ($link == null); + if ($link == null) { + $link = AmpConfig::get('web_path') . "/image.php?object_id=" . $object_id . "&object_type=" . $object_type; + if (AmpConfig::get('use_auth') && AmpConfig::get('require_session')) { + $link .= "&auth=" . session_id(); + } + if ($kind != 'default') { + $link .= '&kind=' . $kind; + } + } + echo ""; + + return true; + } + } // Art diff --git a/sources/lib/class/artist.class.php b/sources/lib/class/artist.class.php index ee286a4..11587a8 100644 --- a/sources/lib/class/artist.class.php +++ b/sources/lib/class/artist.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -20,42 +20,123 @@ * */ -class Artist extends database_object +class Artist extends database_object implements library_item { /* Variables from DB */ - public $id; - public $name; - public $summary; - public $placeformed; - public $yearformed; - public $last_update; - public $songs; - public $albums; - public $prefix; - public $mbid; // MusicBrainz ID - public $catalog_id; - public $time; + /** + * @var int $id + */ + public $id; + /** + * @var string $name + */ + public $name; + /** + * @var string $summary + */ + public $summary; + /** + * @var string $placeformed + */ + public $placeformed; + /** + * @var int $yearformed + */ + public $yearformed; + /** + * @var int $last_update + */ + public $last_update; + /** + * @var int $songs + */ + public $songs; + /** + * @var int $albums + */ + public $albums; + /** + * @var string $prefix + */ + public $prefix; + /** + * @var string $mbid + */ + public $mbid; // MusicBrainz ID + /** + * @var int $catalog_id + */ + public $catalog_id; + /** + * @var int $time + */ + public $time; + /** + * @var int $user + */ + public $user; + + /** + * @var array $tags + */ public $tags; + /** + * @var string $f_tags + */ public $f_tags; + /** + * @var array $labels + */ + public $labels; + /** + * @var string $f_labels + */ + public $f_labels; + /** + * @var int $object_cnt + */ public $object_cnt; + /** + * @var string $f_name + */ public $f_name; + /** + * @var string $f_full_name + */ public $f_full_name; + /** + * @var string $link + */ + public $link; + /** + * @var string $f_link + */ public $f_link; - public $f_name_link; + /** + * @var string $f_time + */ public $f_time; // Constructed vars + /** + * @var boolean $_fake + */ public $_fake = false; // Set if construct_from_array() used + /** + * @var array $_mapcache + */ private static $_mapcache = array(); /** * Artist * Artist class, for modifing a artist * Takes the ID of the artist and pulls the info from the db + * @param int|null $id + * @param int $catalog_init */ - public function __construct($id='',$catalog_init=0) + public function __construct($id=null,$catalog_init=0) { /* If they failed to pass in an id, just run for it */ if (!$id) { return false; } @@ -76,6 +157,8 @@ class Artist extends database_object * construct_from_array * This is used by the metadata class specifically but fills out a Artist object * based on a key'd array, it sets $_fake to true + * @param array $data + * @return Artist */ public static function construct_from_array($data) { @@ -98,13 +181,20 @@ class Artist extends database_object */ public static function gc() { - Dba::write('DELETE FROM `artist` USING `artist` LEFT JOIN `song` ON `song`.`artist` = `artist`.`id` LEFT JOIN `wanted` ON `wanted`.`artist` = `artist`.`id` WHERE `song`.`id` IS NULL AND `wanted`.`id` IS NULL'); + Dba::write('DELETE FROM `artist` USING `artist` LEFT JOIN `song` ON `song`.`artist` = `artist`.`id` ' . + 'LEFT JOIN `album` ON `album`.`album_artist` = `artist`.`id` ' . + 'LEFT JOIN `wanted` ON `wanted`.`artist` = `artist`.`id` ' . + 'LEFT JOIN `clip` ON `clip`.`artist` = `artist`.`id` ' . + 'WHERE `song`.`id` IS NULL AND `album`.`id` IS NULL AND `wanted`.`id` IS NULL AND `clip`.`id` IS NULL'); } /** * this attempts to build a cache of the data from the passed albums all in one query + * @param int[] $ids + * @param boolean $extra + * @return boolean */ - public static function build_cache($ids,$extra=false) + public static function build_cache($ids, $extra=false, $limit_threshold = '') { if (!is_array($ids) OR !count($ids)) { return false; } @@ -119,14 +209,14 @@ class Artist extends database_object // If we need to also pull the extra information, this is normally only used when we are doing the human display if ($extra) { - $sql = "SELECT `song`.`artist`, COUNT(`song`.`id`) AS `song_count`, COUNT(DISTINCT `song`.`album`) AS `album_count`, SUM(`song`.`time`) AS `time` FROM `song` WHERE `song`.`artist` IN $idlist GROUP BY `song`.`artist`"; + $sql = "SELECT `song`.`artist`, COUNT(DISTINCT `song`.`id`) AS `song_count`, COUNT(DISTINCT `song`.`album`) AS `album_count`, SUM(`song`.`time`) AS `time` FROM `song` WHERE `song`.`artist` IN $idlist GROUP BY `song`.`artist`"; debug_event("Artist", "build_cache sql: " . $sql, "6"); $db_results = Dba::read($sql); while ($row = Dba::fetch_assoc($db_results)) { if (AmpConfig::get('show_played_times')) { - $row['object_cnt'] = Stats::get_object_count('artist', $row['artist']); + $row['object_cnt'] = Stats::get_object_count('artist', $row['artist'], $limit_threshold); } parent::add_to_cache('artist_extra',$row['artist'],$row); } @@ -140,6 +230,8 @@ class Artist extends database_object /** * get_from_name * This gets an artist object based on the artist name + * @param string $name + * @return Artist */ public static function get_from_name($name) { @@ -158,13 +250,17 @@ class Artist extends database_object * get_albums * gets the album ids that this artist is a part * of + * @param int|null $catalog + * @param boolean $ignoreAlbumGroups + * @param boolean $group_release_type + * @return int[] */ - public function get_albums($catalog = null, $ignoreAlbumGroups = false) + public function get_albums($catalog = null, $ignoreAlbumGroups = false, $group_release_type = false) { $catalog_where = ""; $catalog_join = "LEFT JOIN `catalog` ON `catalog`.`id` = `song`.`catalog`"; if ($catalog) { - $catalog_where .= " AND `catalog`.`id` = '$catalog'"; + $catalog_where .= " AND `catalog`.`id` = '" . $catalog . "'"; } if (AmpConfig::get('catalog_disable')) { $catalog_where .= " AND `catalog`.`enabled` = '1'"; @@ -190,14 +286,38 @@ class Artist extends database_object } $sql_group = "COALESCE($sql_group_type, `album`.`id`)"; - $sql = "SELECT `album`.`id` FROM album LEFT JOIN `song` ON `song`.`album`=`album`.`id` $catalog_join " . - "WHERE `song`.`artist`='$this->id' $catalog_where GROUP BY $sql_group ORDER BY $sql_sort"; + $sql = "SELECT `album`.`id`, `album`.`release_type` FROM album LEFT JOIN `song` ON `song`.`album`=`album`.`id` $catalog_join " . + "WHERE (`song`.`artist`='$this->id' OR `album`.`album_artist`='$this->id') $catalog_where GROUP BY $sql_group ORDER BY $sql_sort"; debug_event("Artist", "$sql", "6"); $db_results = Dba::read($sql); while ($r = Dba::fetch_assoc($db_results)) { - $results[] = $r['id']; + if ($group_release_type) { + // We assume undefined release type is album + $rtype = $r['release_type'] ?: 'album'; + if (!isset($results[$rtype])) { + $results[$rtype] = array(); + } + $results[$rtype][] = $r['id']; + + $sort = AmpConfig::get('album_release_type_sort'); + if ($sort) { + $results_sort = array(); + $asort = explode(',', $sort); + + foreach ($asort as $rtype) { + if (array_key_exists($rtype, $results)) { + $results_sort[$rtype] = $results[$rtype]; + unset($results[$rtype]); + } + } + + $results = array_merge($results_sort, $results); + } + } else { + $results[] = $r['id']; + } } return $results; @@ -207,6 +327,7 @@ class Artist extends database_object /** * get_songs * gets the songs for this artist + * @return int[] */ public function get_songs() { @@ -214,12 +335,12 @@ class Artist extends database_object if (AmpConfig::get('catalog_disable')) { $sql .= "LEFT JOIN `catalog` ON `catalog`.`id` = `song`.`catalog` "; } - $sql .= "WHERE `song`.`artist`='" . Dba::escape($this->id) . "' "; + $sql .= "WHERE `song`.`artist` = ? "; if (AmpConfig::get('catalog_disable')) { $sql .= "AND `catalog`.`enabled` = '1' "; } - $sql .= "ORDER BY album, track"; - $db_results = Dba::read($sql); + $sql .= "ORDER BY `song`.`album`, `song`.`track`"; + $db_results = Dba::read($sql, array($this->id)); $results = array(); while ($r = Dba::fetch_assoc($db_results)) { @@ -233,6 +354,7 @@ class Artist extends database_object /** * get_random_songs * Gets the songs from this artist in a random order + * @return int[] */ public function get_random_songs() { @@ -242,12 +364,12 @@ class Artist extends database_object if (AmpConfig::get('catalog_disable')) { $sql .= "LEFT JOIN `catalog` ON `catalog`.`id` = `song`.`catalog` "; } - $sql .= "WHERE `song`.`artist`='$this->id' "; + $sql .= "WHERE `song`.`artist` = ? "; if (AmpConfig::get('catalog_disable')) { $sql .= "AND `catalog`.`enabled` = '1' "; } $sql .= "ORDER BY RAND()"; - $db_results = Dba::read($sql); + $db_results = Dba::read($sql, array($this->id)); while ($r = Dba::fetch_assoc($db_results)) { $results[] = $r['id']; @@ -260,15 +382,17 @@ class Artist extends database_object /** * _get_extra info * This returns the extra information for the artist, this means totals etc + * @param int $catalog + * @return array */ - private function _get_extra_info($catalog=FALSE) + private function _get_extra_info($catalog=0, $limit_threshold ='') { // Try to find it in the cache and save ourselves the trouble if (parent::is_cached('artist_extra',$this->id) ) { $row = parent::get_from_cache('artist_extra',$this->id); } else { $uid = Dba::escape($this->id); - $sql = "SELECT `song`.`artist`,COUNT(`song`.`id`) AS `song_count`, COUNT(DISTINCT `song`.`album`) AS `album_count`, SUM(`song`.`time`) AS `time` FROM `song` LEFT JOIN `catalog` ON `catalog`.`id` = `song`.`catalog` " . + $sql = "SELECT `song`.`artist`,COUNT(DISTINCT `song`.`id`) AS `song_count`, COUNT(DISTINCT `song`.`album`) AS `album_count`, SUM(`song`.`time`) AS `time`, `song`.`catalog` as `catalog_id` FROM `song` LEFT JOIN `catalog` ON `catalog`.`id` = `song`.`catalog` " . "WHERE `song`.`artist`='$uid' "; if ($catalog) { $sql .= "AND (`song`.`catalog` = '$catalog') "; @@ -282,7 +406,7 @@ class Artist extends database_object $db_results = Dba::read($sql); $row = Dba::fetch_assoc($db_results); if (AmpConfig::get('show_played_times')) { - $row['object_cnt'] = Stats::get_object_count('artist', $row['artist']); + $row['object_cnt'] = Stats::get_object_count('artist', $row['artist'], $limit_threshold); } parent::add_to_cache('artist_extra',$row['artist'],$row); } @@ -291,6 +415,7 @@ class Artist extends database_object $this->songs = $row['song_count']; $this->albums = $row['album_count']; $this->time = $row['time']; + $this->catalog_id = $row['catalog_id']; return $row; @@ -302,48 +427,225 @@ class Artist extends database_object * information and reformats the relevent values * so they can be displayed in a table for example * it changes the title into a full link. + * @return boolean */ - public function format() + public function format($details = true, $limit_threshold = '') { /* Combine prefix and name, trim then add ... if needed */ $name = trim($this->prefix . " " . $this->name); $this->f_name = $name; $this->f_full_name = trim(trim($this->prefix) . ' ' . trim($this->name)); - // If this is a fake object, we're done here - if ($this->_fake) { return true; } + // If this is a memory-only object, we're done here + if (!$this->id) { return true; } if ($this->catalog_id) { - $this->f_link = AmpConfig::get('web_path') . '/artists.php?action=show&catalog=' . $this->catalog_id . '&artist=' . $this->id; - $this->f_name_link = "f_link . "\" title=\"" . $this->f_full_name . "\">" . $name . ""; + $this->link = AmpConfig::get('web_path') . '/artists.php?action=show&catalog=' . $this->catalog_id . '&artist=' . $this->id; + $this->f_link = "link . "\" title=\"" . $this->f_full_name . "\">" . $name . ""; } else { - $this->f_link = AmpConfig::get('web_path') . '/artists.php?action=show&artist=' . $this->id; - $this->f_name_link = "f_link . "\" title=\"" . $this->f_full_name . "\">" . $name . ""; + $this->link = AmpConfig::get('web_path') . '/artists.php?action=show&artist=' . $this->id; + $this->f_link = "link . "\" title=\"" . $this->f_full_name . "\">" . $name . ""; } - // Get the counts - $extra_info = $this->_get_extra_info($this->catalog_id); - //Format the new time thingy that we just got - $min = sprintf("%02d",(floor($extra_info['time']/60)%60)); + if ($details) { + // Get the counts + $extra_info = $this->_get_extra_info($this->catalog_id, $limit_threshold); - $sec = sprintf("%02d",($extra_info['time']%60)); - $hours = floor($extra_info['time']/3600); + //Format the new time thingy that we just got + $min = sprintf("%02d",(floor($extra_info['time']/60)%60)); - $this->f_time = ltrim($hours . ':' . $min . ':' . $sec,'0:'); + $sec = sprintf("%02d",($extra_info['time']%60)); + $hours = floor($extra_info['time']/3600); - $this->tags = Tag::get_top_tags('artist', $this->id); - $this->f_tags = Tag::get_display($this->tags); + $this->f_time = ltrim($hours . ':' . $min . ':' . $sec,'0:'); - $this->object_cnt = $extra_info['object_cnt']; + $this->tags = Tag::get_top_tags('artist', $this->id); + $this->f_tags = Tag::get_display($this->tags, true, 'artist'); + + if (AmpConfig::get('label')) { + $this->labels = Label::get_labels($this->id); + $this->f_labels = Label::get_display($this->labels, true); + } + + $this->object_cnt = $extra_info['object_cnt']; + } return true; } // format + /** + * Get item keywords for metadata searches. + * @return array + */ + public function get_keywords() + { + $keywords = array(); + $keywords['mb_artistid'] = array('important' => false, + 'label' => T_('Artist MusicBrainzID'), + 'value' => $this->mbid); + $keywords['artist'] = array('important' => true, + 'label' => T_('Artist'), + 'value' => $this->f_full_name); + + return $keywords; + } + + /** + * Get item fullname. + * @return string + */ + public function get_fullname() + { + return $this->f_full_name; + } + + /** + * Get parent item description. + * @return array|null + */ + public function get_parent() + { + return null; + } + + /** + * Get item childrens. + * @return array + */ + public function get_childrens() + { + $medias = array(); + $albums = $this->get_albums(); + foreach ($albums as $album_id) { + $medias[] = array( + 'object_type' => 'album', + 'object_id' => $album_id + ); + } + return array('album' => $medias); + } + + /** + * Search for item childrens. + * @param string $name + * @return array + */ + public function search_childrens($name) + { + $search['type'] = "album"; + $search['rule_0_input'] = $name; + $search['rule_0_operator'] = 4; + $search['rule_0'] = "title"; + $search['rule_1_input'] = $this->name; + $search['rule_1_operator'] = 4; + $search['rule_1'] = "artist"; + $albums = Search::run($search); + + $childrens = array(); + foreach ($albums as $album) { + $childrens[] = array( + 'object_type' => 'album', + 'object_id' => $album + ); + } + return $childrens; + } + + /** + * Get all childrens and sub-childrens medias. + * @param string $filter_type + * @return array + */ + public function get_medias($filter_type = null) + { + $medias = array(); + if (!$filter_type || $filter_type == 'song') { + $songs = $this->get_songs(); + foreach ($songs as $song_id) { + $medias[] = array( + 'object_type' => 'song', + 'object_id' => $song_id + ); + } + } + return $medias; + } + + /** + * get_catalogs + * + * Get all catalog ids related to this item. + * @return int[] + */ + public function get_catalogs() + { + return array($this->catalog_id); + } + + /** + * Get item's owner. + * @return int|null + */ + public function get_user_owner() + { + return $this->user; + } + + /** + * Get default art kind for this item. + * @return string + */ + public function get_default_art_kind() + { + return 'default'; + } + + public function get_description() + { + return $this->summary; + } + + public function display_art($thumb = 2) + { + $id = null; + $type = null; + + if (Art::has_db($this->id, 'artist')) { + $id = $this->id; + $type = 'artist'; + } + + if ($id !== null && $type !== null) { + Art::display($type, $id, $this->get_fullname(), $thumb, $this->link); + } + } + + public function can_edit($user = null) + { + if (!$user) { + $user = $GLOBALS['user']->id; + } + + if (!$user) + return false; + + if (AmpConfig::get('upload_allow_edit')) { + if ($this->user !== null && $user == $this->user) + return true; + } + + return Access::check('interface', 50, $user); + } + /** * check * * Checks for an existing artist; if none exists, insert one. + * @param string $name + * @param string $mbid + * @param boolean $readonly + * @return int|null */ public static function check($name, $mbid = null, $readonly = false) { @@ -429,27 +731,37 @@ class Artist extends database_object /** * update * This takes a key'd array of data and updates the current artist + * @param array $data + * @return int */ - public function update($data) + public function update(array $data) { // Save our current ID + $name = isset($data['name']) ? $data['name'] : $this->name; + $mbid = isset($data['mbid']) ? $data['mbid'] : $this->mbid; + $summary = isset($data['summary']) ? $data['summary'] : $this->summary; + $placeformed = isset($data['placeformed']) ? $data['placeformed'] : $this->placeformed; + $yearformed = isset($data['yearformed']) ? $data['yearformed'] : $this->yearformed; + $current_id = $this->id; // Check if name is different than current name - if ($this->name != $data['name']) { - $artist_id = self::check($data['name'], $this->mbid); + if ($this->name != $name) { + $artist_id = self::check($name, $mbid, true); $updated = false; $songs = array(); // If it's changed we need to update - if ($artist_id != $this->id) { + if ($artist_id != null && $artist_id != $this->id) { $songs = $this->get_songs(); foreach ($songs as $song_id) { Song::update_artist($artist_id,$song_id); } $updated = true; $current_id = $artist_id; + Stats::migrate('artist', $this->id, $artist_id); + Art::migrate('artist', $this->id, $artist_id); self::gc(); } // end if it changed @@ -461,24 +773,41 @@ class Artist extends database_object Rating::gc(); Userflag::gc(); } // if updated - } else if ($this->mbid != $data['mbid']) { + } else if ($this->mbid != $mbid) { $sql = 'UPDATE `artist` SET `mbid` = ? WHERE `id` = ?'; - Dba::write($sql, array($data['mbid'], $current_id)); + Dba::write($sql, array($mbid, $current_id)); } // Update artist name (if we don't want to use the MusicBrainz name) - $trimmed = Catalog::trim_prefix(trim($data['name'])); + $trimmed = Catalog::trim_prefix(trim($name)); $name = $trimmed['string']; if ($name != '' && $name != $this->name) { $sql = 'UPDATE `artist` SET `name` = ? WHERE `id` = ?'; Dba::write($sql, array($name, $current_id)); } + $this->update_artist_info($summary, $placeformed, $yearformed); + + $this->name = $name; + $this->mbid = $mbid; + $override_childs = false; - if ($data['apply_childs'] == 'checked') { + if ($data['overwrite_childs'] == 'checked') { $override_childs = true; } - $this->update_tags($data['edit_tags'], $override_childs, $current_id); + + $add_to_childs = false; + if ($data['add_to_childs'] == 'checked') { + $add_to_childs = true; + } + + if (isset($data['edit_tags'])) { + $this->update_tags($data['edit_tags'], $override_childs, $add_to_childs, $current_id, true); + } + + if (AmpConfig::get('label') && isset($data['edit_labels'])) { + Label::update_label_list($data['edit_labels'], $this->id, true); + } return $current_id; @@ -488,28 +817,82 @@ class Artist extends database_object * update_tags * * Update tags of artists and/or albums + * @param string $tags_comma + * @param boolean $override_childs + * @param int|null $current_id */ - public function update_tags($tags_comma, $override_childs, $current_id = null) + public function update_tags($tags_comma, $override_childs, $add_to_childs, $current_id = null, $force_update = false) { if ($current_id == null) { $current_id = $this->id; } - Tag::update_tag_list($tags_comma, 'artist', $current_id); + Tag::update_tag_list($tags_comma, 'artist', $current_id, $force_update ? true : $override_childs); - if ($override_childs) { + if ($override_childs || $add_to_childs) { $albums = $this->get_albums(null, true); foreach ($albums as $album_id) { $album = new Album($album_id); - $album->update_tags($tags_comma, $override_childs); + $album->update_tags($tags_comma, $override_childs, $add_to_childs); } } } + /** + * Update artist information. + * @param string $summary + * @param string $placeformed + * @param int $yearformed + * @return boolean + */ public function update_artist_info($summary, $placeformed, $yearformed) { $sql = "UPDATE `artist` SET `summary` = ?, `placeformed` = ?, `yearformed` = ?, `last_update` = ? WHERE `id` = ?"; - return Dba::write($sql, array($summary, $placeformed, $yearformed, time(), $this->id)); + $sqlret = Dba::write($sql, array($summary, $placeformed, $yearformed, time(), $this->id)); + + $this->summary = $summary; + $this->placeformed = $placeformed; + $this->yearformed = $yearformed; + + return $sqlret; + } + + /** + * Update artist associated user. + * @param int $user + * @return boolean + */ + public function update_artist_user($user) + { + $sql = "UPDATE `artist` SET `user` = ? WHERE `id` = ?"; + return Dba::write($sql, array($user, $this->id)); + } + + public function remove_from_disk() + { + $deleted = true; + $album_ids = $this->get_albums(); + foreach ($album_ids as $id) { + $album = new Album($id); + $deleted = $album->remove_from_disk(); + if (!$deleted) { + debug_event('artist', 'Error when deleting the album `' . $id .'`.', 1); + break; + } + } + + if ($deleted) { + $sql = "DELETE FROM `artist` WHERE `id` = ?"; + $deleted = Dba::write($sql, array($this->id)); + if ($deleted) { + Art::gc('artist', $this->id); + Userflag::gc('artist', $this->id); + Rating::gc('artist', $this->id); + Shoutbox::gc('artist', $this->id); + } + } + + return $deleted; } } // end of artist class diff --git a/sources/lib/class/artist_event.class.php b/sources/lib/class/artist_event.class.php index 5ddd68c..7b33a13 100644 --- a/sources/lib/class/artist_event.class.php +++ b/sources/lib/class/artist_event.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -33,8 +33,10 @@ class Artist_Event /** * get_upcoming_events * Returns a list of upcoming events + * @param Artist $artist + * @return SimpleXMLElement|boolean */ - public static function get_upcoming_events($artist) + public static function get_upcoming_events(Artist $artist) { if (isset($artist->mbid)) { $query = 'mbid=' . rawurlencode($artist->mbid); @@ -59,8 +61,10 @@ class Artist_Event /** * get_past_events * Returns a list of past events + * @param Artist $artist + * @return SimpleXMLElement|boolean */ - public static function get_past_events($artist) + public static function get_past_events(Artist $artist) { if (isset($artist->mbid)) { $query = 'mbid=' . rawurlencode($artist->mbid); diff --git a/sources/lib/class/auth.class.php b/sources/lib/class/auth.class.php index 3d22a6e..3748b21 100644 --- a/sources/lib/class/auth.class.php +++ b/sources/lib/class/auth.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -44,6 +44,8 @@ class Auth * This is called when you want to log out and nuke your session. * This is the function used for the Ajax logouts, if no id is passed * it tries to find one from the session, + * @param string $key + * @param boolean $relogin */ public static function logout($key='', $relogin = true) { @@ -82,6 +84,10 @@ class Auth * * This takes a username and password and then returns the results * based on what happens when we try to do the auth. + * @param string $username + * @param string $password + * @param boolean $allow_ui + * @return array */ public static function login($username, $password, $allow_ui = false) { @@ -104,6 +110,8 @@ class Auth * login_step2 * * This process authenticate step2 for an auth module + * @param string $auth_mod + * @return array */ public static function login_step2($auth_mod) { @@ -122,6 +130,9 @@ class Auth * mysql_auth * * This is the core function of our built-in authentication. + * @param string $username + * @param string $password + * @return array */ private static function mysql_auth($username, $password) { @@ -168,6 +179,9 @@ class Auth * * Check to make sure the pam_auth function is implemented (module is * installed), then check the credentials. + * @param string $username + * @param string $password + * @return array */ private static function pam_auth($username, $password) { @@ -197,6 +211,9 @@ class Auth * * Calls an external program compatible with mod_authnz_external * such as pwauth. + * @param string $username + * @param string $password + * @return array */ private static function external_auth($username, $password) { @@ -221,7 +238,7 @@ class Auth fclose($pipes[0]); fclose($pipes[1]); if ($stderr = fread($pipes[2], 8192)) { - debug_event('external_auth', $stderr, 5); + debug_event('external_auth', "fread error: " . $stderr, 5); } fclose($pipes[2]); } else { @@ -257,6 +274,9 @@ class Auth * the DN fetched from the LDAP directory" * * require-attribute "an attribute fetched from the LDAP * directory matches the given value" + * @param string $username + * @param string $password + * @return array */ private static function ldap_auth($username, $password) { @@ -285,6 +305,13 @@ class Auth return $results; } + if (strpos($ldap_filter, "%v") >= 0) { + $ldap_filter = str_replace("%v", $username, $ldap_filter); + } else { + // This to support previous configuration where only the fieldname was set + $ldap_filter = "($ldap_filter=$username)"; + } + $ldap_name_field = AmpConfig::get('ldap_name_field'); $ldap_email_field = AmpConfig::get('ldap_email_field'); @@ -300,7 +327,9 @@ class Auth return $results; } // If bind fails - $sr = ldap_search($ldap_link, $ldap_dn, "(&(objectclass=$ldap_class)($ldap_filter=$username))"); + $searchstr = "(&(objectclass=$ldap_class)$ldap_filter)"; + debug_event('ldap_auth', 'ldap_search: ' . $searchstr, 5); + $sr = ldap_search($ldap_link, $ldap_dn, $searchstr); $info = ldap_get_entries($ldap_link, $sr); if ($info["count"] == 1) { @@ -367,6 +396,9 @@ class Auth * http_auth * This auth method relies on HTTP auth from the webserver * + * @param string $username + * @param string $password + * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ private static function http_auth($username, $password) @@ -390,6 +422,9 @@ class Auth * openid_auth * Authenticate user with OpenID * + * @param string $username + * @param string $password + * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ private static function openid_auth($username, $password) @@ -468,6 +503,7 @@ class Auth /** * openid_auth_2 * Authenticate user with OpenID, step 2 + * @return array */ private static function openid_auth_2() { diff --git a/sources/lib/class/autoupdate.class.php b/sources/lib/class/autoupdate.class.php index 2593124..c114820 100644 --- a/sources/lib/class/autoupdate.class.php +++ b/sources/lib/class/autoupdate.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -38,6 +38,10 @@ class AutoUpdate // static class } + /** + * Check if current version is a development version. + * @return boolean + */ protected static function is_develop() { $version = AmpConfig::get('version'); @@ -46,39 +50,56 @@ class AutoUpdate return ($vspart[count($vspart) - 1] == 'develop'); } + /** + * Check if current version is a git repository. + * @return boolean + */ protected static function is_git_repository() { return is_dir(AmpConfig::get('prefix') . '/.git'); } + /** + * Check if branch develop exists in git repository. + * @return boolean + */ protected static function is_branch_develop_exists() { return is_readable(AmpConfig::get('prefix') . '/.git/refs/heads/develop'); } + /** + * Perform a GitHub request. + * @param string $action + * @return string|null + */ public static function github_request($action) { try { + // https is mandatory $url = "https://api.github.com/repos/ampache/ampache" . $action; - $request = Requests::get($url); + $request = Requests::get($url, array(), Core::requests_options()); // Not connected / API rate limit exceeded: just ignore, it will pass next time if ($request->status_code != 200) { debug_event('autoupdate', 'Github API request ' . $url . ' failed with http code ' . $request->status_code, '1'); - return; + return null; } return json_decode($request->body); } catch (Exception $e) { debug_event('autoupdate', 'Request error: ' . $e->getMessage(), '1'); - return ""; + return null; } } + /** + * Check if last github check expired. + * @return boolean + */ protected static function lastcheck_expired() { $lastcheck = AmpConfig::get('autoupdate_lastcheck'); if (!$lastcheck) { - User::rebuild_all_preferences(); Preference::update('autoupdate_lastcheck', $GLOBALS['user']->id, '1'); AmpConfig::set('autoupdate_lastcheck', '1', true); } @@ -86,11 +107,21 @@ class AutoUpdate return ((time() - (3600 * 3)) > $lastcheck); } + /** + * Get latest available version from GitHub. + * @param boolean $force + * @return string + */ public static function get_latest_version($force = false) { $lastversion = ''; // Forced or last check expired, check latest version from Github if ($force || (self::lastcheck_expired() && AmpConfig::get('autoupdate'))) { + // Always update last check time to avoid infinite check on permanent errors (proxy, firewall, ...) + $time = time(); + Preference::update('autoupdate_lastcheck', $GLOBALS['user']->id, $time); + AmpConfig::set('autoupdate_lastcheck', $time, true); + // Development version, get latest commit on develop branch if (self::is_develop()) { $commits = self::github_request('/commits/develop'); @@ -98,9 +129,6 @@ class AutoUpdate $lastversion = $commits->sha; Preference::update('autoupdate_lastversion', $GLOBALS['user']->id, $lastversion); AmpConfig::set('autoupdate_lastversion', $lastversion, true); - $time = time(); - Preference::update('autoupdate_lastcheck', $GLOBALS['user']->id, $time); - AmpConfig::set('autoupdate_lastcheck', $time, true); $available = self::is_update_available(true); Preference::update('autoupdate_lastversion_new', $GLOBALS['user']->id, $available); AmpConfig::set('autoupdate_lastversion_new', $available, true); @@ -113,9 +141,6 @@ class AutoUpdate $lastversion = $tags[0]->name; Preference::update('autoupdate_lastversion', $GLOBALS['user']->id, $lastversion); AmpConfig::set('autoupdate_lastversion', $lastversion, true); - $time = time(); - Preference::update('autoupdate_lastcheck', $GLOBALS['user']->id, $time); - AmpConfig::set('autoupdate_lastcheck', $time, true); $available = self::is_update_available(true); Preference::update('autoupdate_lastversion_new', $GLOBALS['user']->id, $available); AmpConfig::set('autoupdate_lastversion_new', $available, true); @@ -130,6 +155,10 @@ class AutoUpdate return $lastversion; } + /** + * Get current local version. + * @return string + */ public static function get_current_version() { if (self::is_develop()) { @@ -139,15 +168,24 @@ class AutoUpdate } } + /** + * Get current local git commit. + * @return string + */ public static function get_current_commit() { if (self::is_branch_develop_exists()) { return trim(file_get_contents(AmpConfig::get('prefix') . '/.git/refs/heads/develop')); } - return false; + return ''; } + /** + * Check if an update is available. + * @param boolean $force + * @return boolean + */ public static function is_update_available($force = false) { if (!$force && (!self::lastcheck_expired() || !AmpConfig::get('autoupdate'))) { @@ -183,6 +221,9 @@ class AutoUpdate return $available; } + /** + * Display new version information and update link if possible. + */ public static function show_new_version() { echo '
'; @@ -191,6 +232,23 @@ class AutoUpdate echo T_('See') . ' ' . T_('changes') . ' '; echo T_('or') . ' ' . T_('download') . '.'; + if (self::is_git_repository()) { + echo ' | .:: Update ::.'; + } echo '
'; } + + /** + * Update local git repository. + */ + public static function update_files() + { + echo T_('Updating Ampache sources with `git pull` ...') . '
'; + ob_flush(); + chdir(AmpConfig::get('prefix')); + exec('git pull https://github.com/ampache/ampache.git'); + echo T_('Done') . '
'; + ob_flush(); + self::get_latest_version(true); + } } diff --git a/sources/lib/class/broadcast.class.php b/sources/lib/class/broadcast.class.php index 7007a19..56669cc 100644 --- a/sources/lib/class/broadcast.class.php +++ b/sources/lib/class/broadcast.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -20,22 +20,61 @@ * */ -class Broadcast extends database_object +class Broadcast extends database_object implements library_item { + /** + * @var int $id + */ public $id; + /** + * @var boolean $started + */ public $started; + /** + * @var int $listeners + */ public $listeners; + /** + * @var int $song + */ public $song; + /** + * @var int $song_position + */ public $song_position; + /** + * @var string $name + */ public $name; + /** + * @var int $user + */ + public $user; + /** + * @var array $tags + */ public $tags; + /** + * @var string $f_name + */ public $f_name; + /** + * @var string $f_link + */ public $f_link; + /** + * @var string $f_tags + */ public $f_tags; + /** + * @var boolean $is_private + */ + public $is_private; /** * Constructor + * @param int $id */ public function __construct($id=0) { @@ -52,6 +91,11 @@ class Broadcast extends database_object return true; } //constructor + /** + * Update broadcast state. + * @param boolean $started + * @param string $key + */ public function update_state($started, $key='') { $sql = "UPDATE `broadcast` SET `started` = ?, `key` = ?, `song` = '0', `listeners` = '0' WHERE `id` = ?"; @@ -60,6 +104,10 @@ class Broadcast extends database_object $this->started = $started; } + /** + * Update broadcast listeners. + * @param int $listeners + */ public function update_listeners($listeners) { $sql = "UPDATE `broadcast` SET `listeners` = ? " . @@ -68,6 +116,10 @@ class Broadcast extends database_object $this->listeners = $listeners; } + /** + * Update broadcast current song. + * @param int $song_id + */ public function update_song($song_id) { $sql = "UPDATE `broadcast` SET `song` = ? " . @@ -77,12 +129,22 @@ class Broadcast extends database_object $this->song_position = 0; } + /** + * Delete the broadcast. + * @return boolean + */ public function delete() { $sql = "DELETE FROM `broadcast` WHERE `id` = ?"; return Dba::write($sql, array($this->id)); } + /** + * Create a broadcast + * @param string $name + * @param string $description + * @return int + */ public static function create($name, $description='') { if (!empty($name)) { @@ -95,26 +157,144 @@ class Broadcast extends database_object return 0; } - public function update($data) + /** + * Update a broadcast from data array. + * @param array $data + * @return int + */ + public function update(array $data) { if (isset($data['edit_tags'])) { - Tag::update_tag_list($data['edit_tags'], 'broadcast', $this->id); + Tag::update_tag_list($data['edit_tags'], 'broadcast', $this->id, true); } $sql = "UPDATE `broadcast` SET `name` = ?, `description` = ?, `is_private` = ? " . "WHERE `id` = ?"; $params = array($data['name'], $data['description'], !empty($data['private']), $this->id); - return Dba::write($sql, $params); + Dba::write($sql, $params); + + return $this->id; } - public function format() + public function format($details = true) { $this->f_name = $this->name; $this->f_link = '' . scrub_out($this->f_name) . ''; - $this->tags = Tag::get_top_tags('broadcast', $this->id); - $this->f_tags = Tag::get_display($this->tags); + if ($details) { + $this->tags = Tag::get_top_tags('broadcast', $this->id); + $this->f_tags = Tag::get_display($this->tags, true, 'broadcast'); + } } + /** + * Get item keywords for metadata searches. + * @return array + */ + public function get_keywords() + { + return array(); + } + + /** + * Get item fullname. + * @return string + */ + public function get_fullname() + { + return $this->f_name; + } + + /** + * Get parent item description. + * @return array|null + */ + public function get_parent() + { + return null; + } + + /** + * Get item childrens. + * @return array + */ + public function get_childrens() + { + return array(); + } + + /** + * Search for item childrens. + * @param string $name + * @return array + */ + public function search_childrens($name) + { + return array(); + } + + /** + * Get all childrens and sub-childrens medias. + * @param string $filter_type + * @return array + */ + public function get_medias($filter_type = null) + { + // Not a media, shouldn't be that + $medias = array(); + if (!$filter_type || $filter_type == 'broadcast') { + $medias[] = array( + 'object_type' => 'broadcast', + 'object_id' => $this->id + ); + } + return $medias; + } + + /** + * get_catalogs + * + * Get all catalog ids related to this item. + * @return int[] + */ + public function get_catalogs() + { + return array(); + } + + /** + * Get item's owner. + * @return int|null + */ + public function get_user_owner() + { + return $this->user; + } + + /** + * Get default art kind for this item. + * @return string + */ + public function get_default_art_kind() + { + return 'default'; + } + + public function get_description() + { + return null; + } + + public function display_art($thumb = 2) + { + if (Art::has_db($this->id, 'broadcast')) { + Art::display('broadcast', $this->id, $this->get_fullname(), $thumb, $this->link); + } + } + + /** + * Get all broadcasts sql query. + * @return string + */ public static function get_broadcast_list_sql() { $sql = "SELECT `id` FROM `broadcast` WHERE `started` = '1' "; @@ -122,6 +302,10 @@ class Broadcast extends database_object return $sql; } + /** + * Get all broadcasts. + * @return int[] + */ public static function get_broadcast_list() { $sql = self::get_broadcast_list_sql(); @@ -135,12 +319,21 @@ class Broadcast extends database_object return $results; } + /** + * Generate a new broadcast key. + * @return string + */ public static function generate_key() { // Should be improved for security reasons! return md5(uniqid(rand(), true)); } + /** + * Get broadcast from its key. + * @param string $key + * @return Broadcast|null + */ public static function get_broadcast($key) { $sql = "SELECT `id` FROM `broadcast` WHERE `key` = ?"; @@ -153,16 +346,23 @@ class Broadcast extends database_object return null; } + /** + * Show action buttons. + */ public function show_action_buttons() { if ($this->id) { if ($GLOBALS['user']->has_access('75')) { - echo "id . "\" onclick=\"showEditDialog('broadcast_row', '" . $this->id . "', 'edit_broadcast_" . $this->id . "', '" . T_('Broadcast edit') . "', 'broadcast_row_', 'refresh_broadcast')\">" . UI::get_icon('edit', T_('Edit')) . ""; + echo "id . "\" onclick=\"showEditDialog('broadcast_row', '" . $this->id . "', 'edit_broadcast_" . $this->id . "', '" . T_('Broadcast edit') . "', 'broadcast_row_')\">" . UI::get_icon('edit', T_('Edit')) . ""; echo " id ."\">" . UI::get_icon('delete', T_('Delete')) . ""; } } } + /** + * Get broadcast link. + * @return string + */ public static function get_broadcast_link() { $link = "
"; @@ -171,6 +371,11 @@ class Broadcast extends database_object return $link; } + /** + * Get unbroadcast link. + * @param int $id + * @return string + */ public static function get_unbroadcast_link($id) { $link = "
"; @@ -180,6 +385,11 @@ class Broadcast extends database_object return $link; } + /** + * Get broadcasts from an user. + * @param int $user_id + * @return int[] + */ public static function get_broadcasts($user_id) { $sql = "SELECT `id` FROM `broadcast` WHERE `user` = ?"; @@ -192,11 +402,22 @@ class Broadcast extends database_object return $broadcasts; } + public static function gc() + { + + } + /* + * Get play url. * + * @param int $oid + * @param string $additional_params + * @param string $player + * @param boolean $local + * @return string * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public static function play_url($oid, $additional_params='') + public static function play_url($oid, $additional_params='', $player=null, $local=false) { return $oid; } diff --git a/sources/lib/class/broadcast_server.class.php b/sources/lib/class/broadcast_server.class.php index 5ded480..bd1d558 100644 --- a/sources/lib/class/broadcast_server.class.php +++ b/sources/lib/class/broadcast_server.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -36,9 +36,21 @@ class Broadcast_Server implements MessageComponentInterface const BROADCAST_AUTH_SID = "AUTH_SID"; public $verbose; + /** + * @var ConnectionInterface[] $clients + */ protected $clients; + /** + * @var string[] $sids + */ protected $sids; + /** + * @var ConnectionInterface[] $listeners + */ protected $listeners; + /** + * @var Broadcast[] $broadcasters + */ protected $broadcasters; public function __construct() @@ -50,11 +62,20 @@ class Broadcast_Server implements MessageComponentInterface $this->broadcasters = array(); } + /** + * + * @param \Ratchet\ConnectionInterface $conn + */ public function onOpen(ConnectionInterface $conn) { $this->clients[$conn->resourceId] = $conn; } + /** + * + * @param \Ratchet\ConnectionInterface $from + * @param string $msg + */ public function onMessage(ConnectionInterface $from, $msg) { $commands = explode(';', $msg); @@ -101,6 +122,11 @@ class Broadcast_Server implements MessageComponentInterface } } + /** + * + * @param int $song_id + * @return string + */ protected function getSongJS($song_id) { $media = array(); @@ -113,13 +139,18 @@ class Broadcast_Server implements MessageComponentInterface return WebPlayer::get_media_js_param($item[0]); } - protected function notifySong($from, $song_id) + /** + * + * @param \Ratchet\ConnectionInterface $from + * @param int $song_id + */ + protected function notifySong(ConnectionInterface $from, $song_id) { if ($this->isBroadcaster($from)) { $broadcast = $this->broadcasters[$from->resourceId]; $clients = $this->getListeners($broadcast); - Session::extend(Stream::$session, 'stream'); + Session::extend(Stream::get_session(), 'stream'); $broadcast->update_song($song_id); $this->broadcastMessage($clients, self::BROADCAST_SONG, base64_encode($this->getSongJS($song_id))); @@ -132,7 +163,12 @@ class Broadcast_Server implements MessageComponentInterface } } - protected function notifySongPosition($from, $song_position) + /** + * + * @param \Ratchet\ConnectionInterface $from + * @param int $song_position + */ + protected function notifySongPosition(ConnectionInterface $from, $song_position) { if ($this->isBroadcaster($from)) { $broadcast = $this->broadcasters[$from->resourceId]; @@ -151,12 +187,17 @@ class Broadcast_Server implements MessageComponentInterface } } - protected function notifyPlayerPlay($from, $play) + /** + * + * @param \Ratchet\ConnectionInterface $from + * @param boolean $play + */ + protected function notifyPlayerPlay(ConnectionInterface $from, $play) { if ($this->isBroadcaster($from)) { $broadcast = $this->broadcasters[$from->resourceId]; $clients = $this->getListeners($broadcast); - $this->broadcastMessage($clients, self::BROADCAST_PLAYER_PLAY, $play); + $this->broadcastMessage($clients, self::BROADCAST_PLAYER_PLAY, $play ? 'true' : 'false'); if ($this->verbose) { echo "[" . time() ."][info]Broadcast " . $broadcast->id . " player state: " . $play . "." . "\r\n"; @@ -166,7 +207,31 @@ class Broadcast_Server implements MessageComponentInterface } } - protected function registerBroadcast($from, $broadcast_key) + /** + * + * @param \Ratchet\ConnectionInterface $from + */ + protected function notifyEnded(ConnectionInterface $from) + { + if ($this->isBroadcaster($from)) { + $broadcast = $this->broadcasters[$from->resourceId]; + $clients = $this->getListeners($broadcast); + $this->broadcastMessage($clients, self::BROADCAST_ENDED); + + if ($this->verbose) { + echo "[" . time() ."][info]Broadcast " . $broadcast->id . " ended." . "\r\n"; + } + } else { + debug_event('broadcast', 'Action unauthorized.', '3'); + } + } + + /** + * + * @param \Ratchet\ConnectionInterface $from + * @param string $broadcast_key + */ + protected function registerBroadcast(ConnectionInterface $from, $broadcast_key) { $broadcast = Broadcast::get_broadcast($broadcast_key); if ($broadcast) { @@ -179,7 +244,11 @@ class Broadcast_Server implements MessageComponentInterface } } - protected function unregisterBroadcast($conn) + /** + * + * @param \Ratchet\ConnectionInterface $conn + */ + protected function unregisterBroadcast(ConnectionInterface $conn) { $broadcast = $this->broadcasters[$conn->resourceId]; $clients = $this->getListeners($broadcast); @@ -194,6 +263,11 @@ class Broadcast_Server implements MessageComponentInterface } } + /** + * + * @param int $broadcast_id + * @return Broadcast + */ protected function getRunningBroadcast($broadcast_id) { $broadcast = null; @@ -206,7 +280,12 @@ class Broadcast_Server implements MessageComponentInterface return $broadcast; } - protected function registerListener($from, $broadcast_id) + /** + * + * @param \Ratchet\ConnectionInterface $from + * @param int $broadcast_id + */ + protected function registerListener(ConnectionInterface $from, $broadcast_id) { $broadcast = $this->getRunningBroadcast($broadcast_id); @@ -226,7 +305,12 @@ class Broadcast_Server implements MessageComponentInterface } } - protected function authSid($conn, $sid) + /** + * + * @param \Ratchet\ConnectionInterface $conn + * @param string $sid + */ + protected function authSid(ConnectionInterface $conn, $sid) { if (Session::exists('stream', $sid)) { $this->sids[$conn->resourceId] = $sid; @@ -237,7 +321,11 @@ class Broadcast_Server implements MessageComponentInterface } } - protected function unregisterListener($conn) + /** + * + * @param \Ratchet\ConnectionInterface $conn + */ + protected function unregisterListener(ConnectionInterface $conn) { foreach ($this->listeners as $broadcast_id => $brlisteners) { $lindex = array_search($conn, $brlisteners); @@ -257,7 +345,11 @@ class Broadcast_Server implements MessageComponentInterface } } - protected function notifyNbListeners($broadcast) + /** + * + * @param Broadcast $broadcast + */ + protected function notifyNbListeners(Broadcast $broadcast) { $broadcaster_id = array_search($broadcast, $this->broadcasters); if ($broadcaster_id) { @@ -269,16 +361,32 @@ class Broadcast_Server implements MessageComponentInterface } } - protected function getListeners($broadcast) + /** + * + * @param Broadcast $broadcast + * @return \Ratchet\ConnectionInterface + */ + protected function getListeners(Broadcast $broadcast) { return $this->listeners[$broadcast->id]; } - protected function isBroadcaster($conn) + /** + * + * @param \Ratchet\ConnectionInterface $conn + * @return boolean + */ + protected function isBroadcaster(ConnectionInterface $conn) { return array_key_exists($conn->resourceId, $this->broadcasters); } + /** + * + * @param \Ratchet\ConnectionInterface[] $clients + * @param string $cmd + * @param string $value + */ protected function broadcastMessage($clients, $cmd, $value='') { $msg = $cmd . ':' . $value . ';'; @@ -291,6 +399,10 @@ class Broadcast_Server implements MessageComponentInterface } } + /** + * + * @param \Ratchet\ConnectionInterface $conn + */ public function onClose(ConnectionInterface $conn) { if ($this->isBroadcaster($conn)) { @@ -303,11 +415,20 @@ class Broadcast_Server implements MessageComponentInterface unset($this->sids[$conn->resourceId]); } + /** + * + * @param \Ratchet\ConnectionInterface $conn + * @param \Exception $e + */ public function onError(ConnectionInterface $conn, \Exception $e) { $conn->close(); } + /** + * + * @return string + */ public static function get_address() { $websocket_address = AmpConfig::get('websocket_address'); diff --git a/sources/lib/class/browse.class.php b/sources/lib/class/browse.class.php index 2df265e..459873c 100644 --- a/sources/lib/class/browse.class.php +++ b/sources/lib/class/browse.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -31,8 +31,17 @@ */ class Browse extends Query { + /** + * @var boolean $show_header + */ public $show_header; + /** + * Constructor. + * + * @param int|null $id + * @param boolean $cached + */ public function __construct($id = null, $cached = true) { parent::__construct($id, $cached); @@ -48,6 +57,8 @@ class Browse extends Query * set_simple_browse * This sets the current browse object to a 'simple' browse method * which means use the base query provided and expand from there + * + * @param boolean $value */ public function set_simple_browse($value) { @@ -58,6 +69,9 @@ class Browse extends Query /** * add_supplemental_object * Legacy function, need to find a better way to do that + * + * @param string $class + * @param int $uid */ public function add_supplemental_object($class, $uid) { @@ -71,6 +85,8 @@ class Browse extends Query * get_supplemental_objects * This returns an array of 'class','id' for additional objects that * need to be created before we start this whole browsing thing. + * + * @return array */ public function get_supplemental_objects() { @@ -84,11 +100,42 @@ class Browse extends Query } // get_supplemental_objects + /** + * update_browse_from_session + * Restore the previous start index from something saved into the current session. + */ + public function update_browse_from_session() + { + if ($this->is_simple() && $this->get_start() == 0) { + $name = 'browse_current_' . $this->get_type(); + if (isset($_SESSION[$name]) && isset($_SESSION[$name]['start']) && $_SESSION[$name]['start'] > 0) { + + // Checking if value is suitable + $start = $_SESSION[$name]['start']; + if ($this->get_offset() > 0) { + + $set_page = floor($start / $this->get_offset()); + if ($this->get_total() > $this->get_offset()) { + $total_pages = ceil($this->get_total() / $this->get_offset()); + } else { + $total_pages = 0; + } + + if ($set_page >= 0 && $set_page <= $total_pages) { + $this->set_start($start); + } + } + } + } + } + /** * show_objects * This takes an array of objects * and requires the correct template based on the * type that we are currently browsing + * + * @param int[] $object_ids */ public function show_objects($object_ids = null, $argument = null) { @@ -100,9 +147,8 @@ class Browse extends Query // Limit is based on the user's preferences if this is not a // simple browse because we've got too much here - if ((count($object_ids) > $this->get_start()) && - ! $this->is_simple() && - ! $this->is_static_content()) { + if ($this->get_start() >= 0 && (count($object_ids) > $this->get_start()) && + ! $this->is_simple()) { $object_ids = array_slice( $object_ids, $this->get_start(), @@ -133,37 +179,53 @@ class Browse extends Query $match = ' (' . $filter_value . ')';*/ } elseif ($filter_value = $this->get_filter('catalog')) { // Get the catalog title - $catalog = Catalog::create_from_id($filter_value); + $catalog = Catalog::create_from_id(intval($filter_value)); $match = ' (' . $catalog->name . ')'; } $type = $this->get_type(); + // Update the session value only if it's allowed on the current browser + if ($this->get_update_session()) { + $_SESSION['browse_current_' . $type]['start'] = $browse->get_start(); + } + // Set the correct classes based on type $class = "box browse_" . $type; - debug_event('browse', 'Called for type {'.$type.'}', '5'); + $argument_param = ($argument ? '&argument=' . scrub_in($argument) : ''); + + debug_event('browse', 'Show objects called for type {'.$type.'}', '5'); + + $limit_threshold = $this->get_threshold(); // Switch on the type of browsing we're doing switch ($type) { case 'song': $box_title = T_('Songs') . $match; - Song::build_cache($object_ids); + Song::build_cache($object_ids, $limit_threshold); $box_req = AmpConfig::get('prefix') . '/templates/show_songs.inc.php'; break; case 'album': - $box_title = T_('Albums') . $match; Album::build_cache($object_ids); - $allow_group_disks = $argument; + $box_title = T_('Albums') . $match; + if (is_array($argument)) { + $allow_group_disks = $argument['group_disks']; + if ($argument['title']) { + $box_title = $argument['title']; + } + } else { + $allow_group_disks = false; + } $box_req = AmpConfig::get('prefix') . '/templates/show_albums.inc.php'; break; case 'user': - $box_title = T_('Manage Users') . $match; + $box_title = T_('Users') . $match; $box_req = AmpConfig::get('prefix') . '/templates/show_users.inc.php'; break; case 'artist': $box_title = T_('Artists') . $match; - Artist::build_cache($object_ids, 'extra'); + Artist::build_cache($object_ids, true, $limit_threshold); $box_req = AmpConfig::get('prefix') . '/templates/show_artists.inc.php'; break; case 'live_stream': @@ -187,7 +249,7 @@ class Browse extends Query break; case 'smartplaylist': $box_title = T_('Smart Playlists') . $match; - $box_req = AmpConfig::get('prefix') . '/templates/show_smartplaylists.inc.php'; + $box_req = AmpConfig::get('prefix') . '/templates/show_searches.inc.php'; break; case 'catalog': $box_title = T_('Catalogs'); @@ -204,6 +266,7 @@ class Browse extends Query break; case 'video': Video::build_cache($object_ids); + $video_type = 'video'; $box_title = T_('Videos'); $box_req = AmpConfig::get('prefix') . '/templates/show_videos.inc.php'; break; @@ -231,12 +294,52 @@ class Browse extends Query $box_title = T_('Broadcasts'); $box_req = AmpConfig::get('prefix') . '/templates/show_broadcasts.inc.php'; break; + case 'license': + $box_title = T_('Media Licenses'); + $box_req = AmpConfig::get('prefix') . '/templates/show_manage_license.inc.php'; + break; + case 'tvshow': + $box_title = T_('TV Shows'); + $box_req = AmpConfig::get('prefix') . '/templates/show_tvshows.inc.php'; + break; + case 'tvshow_season': + $box_title = T_('Seasons'); + $box_req = AmpConfig::get('prefix') . '/templates/show_tvshow_seasons.inc.php'; + break; + case 'tvshow_episode': + $box_title = T_('Episodes'); + $video_type = $type; + $box_req = AmpConfig::get('prefix') . '/templates/show_videos.inc.php'; + break; + case 'movie': + $box_title = T_('Movies'); + $video_type = $type; + $box_req = AmpConfig::get('prefix') . '/templates/show_videos.inc.php'; + break; + case 'clip': + $box_title = T_('Clips'); + $video_type = $type; + $box_req = AmpConfig::get('prefix') . '/templates/show_videos.inc.php'; + break; + case 'personal_video': + $box_title = T_('Personal Videos'); + $video_type = $type; + $box_req = AmpConfig::get('prefix') . '/templates/show_videos.inc.php'; + break; + case 'label': + $box_title = T_('Labels'); + $box_req = AmpConfig::get('prefix') . '/templates/show_labels.inc.php'; + break; + case 'pvmsg': + $box_title = T_('Private Messages'); + $box_req = AmpConfig::get('prefix') . '/templates/show_pvmsgs.inc.php'; + break; default: // Rien a faire break; } // end switch on type - Ajax::start_container('browse_content_' . $type, 'browse_content'); + Ajax::start_container($this->get_content_div(), 'browse_content'); if ($this->get_show_header()) { if (isset($box_req) && isset($box_title)) { UI::show_box_top($box_title, $class); @@ -252,31 +355,32 @@ class Browse extends Query UI::show_box_bottom(); } echo ''; } else { if (!$this->get_use_pages()) { - $this->show_next_link(); + $this->show_next_link($argument); } } Ajax::end_container(); } // show_object - public function show_next_link() + public function show_next_link($argument = null) { - $limit = $this->get_offset(); - $start = $this->get_start(); - $total = $this->get_total(); + $limit = $this->get_offset(); + $start = $this->get_start(); + $total = $this->get_total(); $next_offset = $start + $limit; if ($next_offset <= $total) { - echo '' . T_('More') . ''; + echo '' . T_('More') . ''; } } /** * set_filter_from_request * //FIXME + * @param array $request */ public function set_filter_from_request($request) { @@ -300,6 +404,11 @@ class Browse extends Query } } // set_filter_from_request + /** + * + * @param string $type + * @param string $custom_base + */ public function set_type($type, $custom_base = '') { $cn = 'browse_' . $type . '_pages'; @@ -321,6 +430,11 @@ class Browse extends Query parent::set_type($type, $custom_base); } + /** + * + * @param string $option + * @param string $value + */ public function save_cookie_params($option, $value) { if ($this->get_type()) { @@ -328,36 +442,96 @@ class Browse extends Query } } + /** + * + * @param boolean $use_pages + */ public function set_use_pages($use_pages) { $this->save_cookie_params('pages', $use_pages ? 'true' : 'false'); $this->_state['use_pages'] = $use_pages; } + /** + * + * @return boolean + */ public function get_use_pages() { return $this->_state['use_pages']; } + /** + * + * @param boolean $use_alpha + */ public function set_use_alpha($use_alpha) { $this->save_cookie_params('alpha', $use_alpha ? 'true' : 'false'); $this->_state['use_alpha'] = $use_alpha; } + /** + * + * @return boolean + */ public function get_use_alpha() { return $this->_state['use_alpha']; } + /** + * + * @param boolean $show_header + */ public function set_show_header($show_header) { $this->show_header = $show_header; } + /** + * Allow the current page to be save into the current session + * @param boolean $update_session + */ + public function set_update_session($update_session) + { + $this->_state['update_session'] = $update_session; + } + + /** + * + * @return boolean + */ public function get_show_header() { return $this->show_header; } + /** + * + * @return boolean + */ + public function get_update_session() + { + return $this->_state['update_session']; + } + + /** + * + * @param string $threshold + */ + public function set_threshold($threshold) + { + $this->_state['threshold'] = $threshold; + } + + /** + * + * @return string + */ + public function get_threshold() + { + return $this->_state['threshold']; + } + } // browse diff --git a/sources/lib/class/catalog.class.php b/sources/lib/class/catalog.class.php index 478848d..0090bf8 100644 --- a/sources/lib/class/catalog.class.php +++ b/sources/lib/class/catalog.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -29,49 +29,167 @@ */ abstract class Catalog extends database_object { + /** + * @var int $id + */ public $id; + /** + * @var string $name + */ public $name; + /** + * @var int $last_update + */ public $last_update; + /** + * @var int $last_add + */ public $last_add; + /** + * @var int $last_clean + */ public $last_clean; + /** + * @var string $key + */ public $key; + /** + * @var string $rename_pattern + */ public $rename_pattern; + /** + * @var string $sort_pattern + */ public $sort_pattern; + /** + * @var string $catalog_type + */ public $catalog_type; + /** + * @var string $gather_types + */ + public $gather_types; + /** + * @var string $f_name + */ public $f_name; - public $f_name_link; + /** + * @var string $link + */ + public $link; + /** + * @var string $f_link + */ + public $f_link; + /** + * @var string $f_update + */ public $f_update; + /** + * @var string $f_add + */ public $f_add; + /** + * @var string $f_clean + */ public $f_clean; - /* This is a private var that's used during catalog builds */ + /* + * This is a private var that's used during catalog builds + * @var array $_playlists + */ protected $_playlists = array(); - // Cache all files in catalog for quick lookup during add + /* + * Cache all files in catalog for quick lookup during add + * @var array $_filecache + */ protected $_filecache = array(); // Used in functions + /** + * @var array $albums + */ protected static $albums = array(); + /** + * @var array $artists + */ protected static $artists = array(); + /** + * @var array $tags + */ protected static $tags = array(); + /** + * @return string + */ abstract public function get_type(); + /** + * @return string + */ abstract public function get_description(); + /** + * @return string + */ abstract public function get_version(); + /** + * @return string + */ abstract public function get_create_help(); + /** + * @return boolean + */ abstract public function is_installed(); + /** + * @return boolean + */ abstract public function install(); abstract public function add_to_catalog($options = null); abstract public function verify_catalog_proc(); abstract public function clean_catalog_proc(); + /** + * @return array + */ abstract public function catalog_fields(); + /** + * @return string + */ abstract public function get_rel_path($file_path); + /** + * @return media|null + */ abstract public function prepare_media($media); - /** + /** + * Check if the catalog is ready to perform actions (configuration completed, ...) + * @return boolean + */ + public function isReady() + { + return true; + } + + /** + * Show a message to make the catalog ready. + */ + public function show_ready_process() + { + // Do nothing. + } + + /** + * Perform the last step process to make the catalog ready. + */ + public function perform_ready() + { + // Do nothing. + } + + /** * uninstall * This removes the remote catalog + * @return boolean */ public function uninstall() { @@ -82,9 +200,13 @@ abstract class Catalog extends database_object Dba::query($sql); return true; - } // uninstall + /** + * Create a catalog from its id. + * @param int $id + * @return Catalog|null + */ public static function create_from_id($id) { $sql = 'SELECT `catalog_type` FROM `catalog` WHERE `id` = ?'; @@ -100,10 +222,15 @@ abstract class Catalog extends database_object * create_catalog_type * This function attempts to create a catalog type * all Catalog modules should be located in /modules/catalog/.class.php + * @param string $type + * @param int $id + * @return Catalog|null */ public static function create_catalog_type($type, $id=0) { - if (!$type) { return false; } + if (!$type) { + return false; + } $filename = AmpConfig::get('prefix') . '/modules/catalog/' . $type . '.catalog.php'; $include = require_once $filename; @@ -126,9 +253,12 @@ abstract class Catalog extends database_object } return $catalog; } - } // create_catalog_type + /** + * Show dropdown catalog types. + * @param string $divback + */ public static function show_catalog_types($divback = 'catalog_type_fields') { echo ""; } // run_playlist_method @@ -347,23 +458,29 @@ class Stream * get_base_url * This returns the base requirements for a stream URL this does not include anything after the index.php?sid=???? */ - public static function get_base_url() + public static function get_base_url($local=false) { $session_string = ''; - if (AmpConfig::get('require_session')) { - $session_string = 'ssid=' . self::$session . '&'; + if (AmpConfig::get('use_auth') && AmpConfig::get('require_session')) { + $session_string = 'ssid=' . self::get_session() . '&'; } - $web_path = AmpConfig::get('web_path'); + if ($local) { + $web_path = AmpConfig::get('local_web_path'); + } else { + $web_path = AmpConfig::get('web_path'); + } - if (AmpConfig::get('force_http_play') OR !empty(self::$force_http)) { + if (AmpConfig::get('force_http_play')) { $web_path = str_replace("https://", "http://",$web_path); } - if (AmpConfig::get('http_port') != '80') { + + $http_port = AmpConfig::get('http_port'); + if (!empty($http_port) && $http_port != '80') { if (preg_match("/:(\d+)/",$web_path,$matches)) { - $web_path = str_replace(':' . $matches['1'],':' . AmpConfig::get('http_port'),$web_path); + $web_path = str_replace(':' . $matches['1'], ':' . $http_port, $web_path); } else { - $web_path = str_replace(AmpConfig::get('http_host'), AmpConfig::get('http_host') . ':' . AmpConfig::get('http_port'), $web_path); + $web_path = str_replace(AmpConfig::get('http_host'), AmpConfig::get('http_host') . ':' . $http_port, $web_path); } } diff --git a/sources/lib/class/stream_playlist.class.php b/sources/lib/class/stream_playlist.class.php index e2990ae..ae84e04 100644 --- a/sources/lib/class/stream_playlist.class.php +++ b/sources/lib/class/stream_playlist.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -45,7 +45,7 @@ class Stream_Playlist Stream::set_session($id); } - $this->id = Stream::$session; + $this->id = Stream::get_session(); if (!Session::exists('stream', $this->id)) { debug_event('stream_playlist', 'Session::exists failed', 2); @@ -70,7 +70,6 @@ class Stream_Playlist debug_event("stream_playlist.class.php", "Adding url {".json_encode($url)."}...", 5); $this->urls[] = $url; - $sql = 'INSERT INTO `stream_playlist` '; $fields = array(); @@ -115,19 +114,23 @@ class Stream_Playlist $additional_params .= "&custom_play_action=" . $medium['custom_play_action']; } + if ($_SESSION['iframe']['subtitle']) { + $additional_params .= "&subtitle=" . $_SESSION['iframe']['subtitle']; + } + $type = $medium['object_type']; - //$url['object_id'] = $medium['object_id']; + $object_id = $medium['object_id']; $url['type'] = $type; - $object = new $type($medium['object_id']); + $object = new $type($object_id); $object->format(); // Don't add disabled media objects to the stream playlist // Playing a disabled media return a 404 error that could make failed the player (mpd ...) - if (make_bool($object->enabled)) { + if (!isset($object->enabled) || make_bool($object->enabled)) { //FIXME: play_url shouldn't be static $url['url'] = $type::play_url($object->id, $additional_params); - $api_session = (AmpConfig::get('require_session')) ? Stream::$session : false; + $api_session = (AmpConfig::get('require_session')) ? Stream::get_session() : false; // Set a default which can be overridden $url['author'] = 'Ampache'; @@ -137,14 +140,15 @@ class Stream_Playlist $url['title'] = $object->title; $url['author'] = $object->f_artist_full; $url['info_url'] = $object->f_link; - $url['image_url'] = Art::url($object->album, 'album', $api_session); + $url['image_url'] = Art::url($object->album, 'album', $api_session, (AmpConfig::get('ajax_load') ? 3 : 4)); $url['album'] = $object->f_album_full; break; case 'video': $url['title'] = 'Video - ' . $object->title; $url['author'] = $object->f_artist_full; + $url['resolution'] = $object->f_resolution; break; - case 'radio': + case 'live_stream': $url['title'] = 'Radio - ' . $object->name; if (!empty($object->site_url)) { $url['title'] .= ' (' . $object->site_url . ')'; @@ -177,11 +181,17 @@ class Stream_Playlist public static function check_autoplay_append() { // For now, only iframed web player support media append in the currently played playlist - return ((AmpConfig::get('iframes') && AmpConfig::get('play_type') == 'web_player') || + return ((AmpConfig::get('ajax_load') && AmpConfig::get('play_type') == 'web_player') || AmpConfig::get('play_type') == 'localplay' ); } + public static function check_autoplay_next() + { + // Currently only supported for web player + return (AmpConfig::get('ajax_load') && AmpConfig::get('play_type') == 'web_player'); + } + public function generate_playlist($type, $redirect = false) { if (!count($this->urls)) { @@ -203,7 +213,7 @@ class Stream_Playlist unset($ext); break; case 'asx': - $ct = 'video/x-ms-wmv'; + $ct = 'video/x-ms-asf'; break; case 'pls': $ct = 'audio/x-scpls'; @@ -218,6 +228,10 @@ class Stream_Playlist case 'xspf': $ct = 'application/xspf+xml'; break; + case 'hls': + $ext = 'm3u8'; + $ct = 'application/vnd.apple.mpegurl'; + break; case 'm3u': default: // Assume M3U if the pooch is screwed @@ -294,9 +308,11 @@ class Stream_Playlist { echo "#EXTM3U\n"; + $i = 0; foreach ($this->urls as $url) { echo '#EXTINF:' . $url->time, ',' . $url->author . ' - ' . $url->title . "\n"; echo $url->url . "\n"; + $i++; } } // create_m3u @@ -391,6 +407,48 @@ class Stream_Playlist } // create_xspf + public function create_hls() + { + $ssize = 10; + echo "#EXTM3U\n"; + echo "#EXT-X-TARGETDURATION:" . $ssize . "\n"; + echo "#EXT-X-VERSION:1\n"; + echo "#EXT-X-ALLOW-CACHE:NO\n"; + echo "#EXT-X-MEDIA-SEQUENCE:0\n"; + echo "#EXT-X-PLAYLIST-TYPE:VOD\n"; // Static list of segments + + foreach ($this->urls as $url) { + $soffset = 0; + $segment = 0; + while ($soffset < $url->time) { + $type = $url->type; + $size = (($soffset + $ssize) <= $url->time) ? $ssize : ($url->time - $soffset); + $additional_params = '&transcode_to=ts&segment=' . $segment; + echo "#EXTINF:" . $size . ",\n"; + $purl = Stream_URL::parse($url->url); + $id = $purl['id']; + + unset($purl['id']); + unset($purl['ssid']); + unset($purl['type']); + unset($purl['base_url']); + unset($purl['uid']); + unset($purl['name']); + + foreach ($purl as $key => $value) { + $additional_params .= '&' . $key . '=' . $value; + } + + $hu = $type::play_url($id, $additional_params); + echo $hu . "\n"; + $soffset += $size; + $segment++; + } + } + + echo "#EXT-X-ENDLIST\n\n"; + } + /** * create_web_player * @@ -398,7 +456,7 @@ class Stream_Playlist */ public function create_web_player() { - if (AmpConfig::get("iframes")) { + if (AmpConfig::get("ajax_load")) { require AmpConfig::get('prefix') . '/templates/create_web_player_embedded.inc.php'; } else { require AmpConfig::get('prefix') . '/templates/create_web_player.inc.php'; @@ -422,6 +480,16 @@ class Stream_Playlist $localplay->add_url($url); } if (!$append) { + // We don't have metadata on Stream_URL to know its kind + // so we check the content to know if it is democratic + if (count($this->urls) == 1) { + $furl = $this->urls[0]; + if (strpos($furl->url, "&demo_id=1") !== false && $furl->time == -1) { + // If democratic, repeat the song to get the next voted one. + debug_event('stream_playlist', 'Playing democratic on localplay, enabling repeat...', 5); + $localplay->repeat(true); + } + } $localplay->play(); } @@ -445,6 +513,7 @@ class Stream_Playlist } $democratic->add_vote($items); + display_notification(T_('Vote added')); } /** @@ -460,7 +529,8 @@ class Stream_Playlist // Header redirect baby! $url = current($this->urls); - header('Location: ' . $url->url . '&action=download'); + $url = Stream_URL::add_options($url->url, '&action=download'); + header('Location: ' . $url); exit; } //create_download diff --git a/sources/lib/class/stream_url.class.php b/sources/lib/class/stream_url.class.php index dddf16f..019d8c1 100644 --- a/sources/lib/class/stream_url.class.php +++ b/sources/lib/class/stream_url.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -73,6 +73,36 @@ class Stream_URL extends memory_object return $results; } + /** + * add_options + * + * Add options to an existing stream url. + */ + public static function add_options($url, $options) + { + if (AmpConfig::get('stream_beautiful_url')) { + // We probably want beautiful url to have a real mp3 filename at the end. + // Add the new options before the filename + + $curel = explode('/', $url); + $newel = explode('&', $options); + + if (count($curel) > 2) { + foreach ($newel as $el) { + if (!empty($el)) { + $el = explode('=', $el); + array_splice($curel, count($curel) - 2, 0, $el); + } + } + $url = implode('/', $curel); + } + } else { + $url .= $options; + } + + return $url; + } + /** * format * This format the string url according to settings. diff --git a/sources/lib/class/subsonic_api.class.php b/sources/lib/class/subsonic_api.class.php index 22c972a..07584a0 100644 --- a/sources/lib/class/subsonic_api.class.php +++ b/sources/lib/class/subsonic_api.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,6 +62,19 @@ class Subsonic_Api return $input[$parameter]; } + public static function decrypt_password($password) + { + // Decode hex-encoded password + $encpwd = strpos($password, "enc:"); + if ($encpwd !== false) { + $hex = substr($password, 4); + $decpwd = ''; + for ($i=0; $i $reqheaders, CURLOPT_HEADER => false, CURLOPT_RETURNTRANSFER => false, CURLOPT_FOLLOWLOCATION => true, @@ -122,6 +150,7 @@ class Subsonic_Api } else { header("Content-type: text/xml; charset=" . AmpConfig::get('site_charset')); } + header("access-control-allow-origin: *"); } public static function apiOutput($input, $xml) @@ -145,7 +174,6 @@ class Subsonic_Api $dom->formatOutput = true; echo $dom->saveXML(); } - } /** @@ -231,6 +259,9 @@ class Subsonic_Api $propertiesArray = !$options['autoText'] || $attributesArray || $tagsArray || ($plainText === '') ? array_merge($attributesArray, $tagsArray, $textContentArray) : $plainText; + if (isset($propertiesArray['xmlns'])) { + unset($propertiesArray['xmlns']); + } //return node as array return array( $xml->getName() => $propertiesArray @@ -291,7 +322,7 @@ class Subsonic_Api $ifModifiedSince = $input['ifModifiedSince']; $catalogs = array(); - if (!empty($musicFolderId)) { + if (!empty($musicFolderId) && $musicFolderId != '-1') { $catalogs[] = $musicFolderId; } else { $catalogs = Catalog::get_catalogs(); @@ -309,7 +340,7 @@ class Subsonic_Api if ($catalog->last_clean > $clastmodified) $clastmodified = $catalog->last_clean; if ($clastmodified > $lastmodified) $lastmodified = $clastmodified; - if (!empty($ifModifiedSince) && $clastmodified > $ifModifiedSince) $fcatalogs[] = $id; + if (!empty($ifModifiedSince) && $clastmodified > ($ifModifiedSince / 1000)) $fcatalogs[] = $id; } if (empty($ifModifiedSince)) $fcatalogs = $catalogs; @@ -353,7 +384,7 @@ class Subsonic_Api self::check_version($input, "1.9.0"); $r = Subsonic_XML_Data::createSuccessResponse(); - Subsonic_XML_Data::addGenres($r, Tag::get_tags()); + Subsonic_XML_Data::addGenres($r, Tag::get_tags('song')); self::apiOutput($input, $r); } @@ -419,14 +450,14 @@ class Subsonic_Api * getVideos * Get all videos. * Takes no parameter. - * Not supported yet. */ public static function getvideos($input) { self::check_version($input, "1.7.0"); $r = Subsonic_XML_Data::createSuccessResponse(); - Subsonic_XML_Data::addVideos($r); + $videos = Catalog::get_videos(); + Subsonic_XML_Data::addVideos($r, $videos); self::apiOutput($input, $r); } @@ -443,12 +474,21 @@ class Subsonic_Api $size = $input['size']; $offset = $input['offset']; + $musicFolderId = $input['musicFolderId'] ?: 0; + + // Get albums from all catalogs by default + // Catalog filter is not supported for all request type for now. + $catalogs = null; + if ($musicFolderId > 0) { + $catalogs = array(); + $catalogs[] = $musicFolderId; + } $albums = array(); if ($type == "random") { $albums = Album::get_random($size); } else if ($type == "newest") { - $albums = Stats::get_newest("album", $size, $offset); + $albums = Stats::get_newest("album", $size, $offset, $musicFolderId); } else if ($type == "highest") { $albums = Rating::get_highest("album", $size, $offset); } else if ($type == "frequent") { @@ -458,9 +498,42 @@ class Subsonic_Api } else if ($type == "starred") { $albums = Userflag::get_latest('album'); } else if ($type == "alphabeticalByName") { - $albums = Catalog::get_albums($size, $offset); + $albums = Catalog::get_albums($size, $offset, $catalogs); } else if ($type == "alphabeticalByArtist") { - $albums = Catalog::get_albums_by_artist($size, $offset); + $albums = Catalog::get_albums_by_artist($size, $offset, $catalogs); + } else if ($type == "byYear") { + $fromYear = $input['fromYear']; + $toYear = $input['toYear']; + + if ($fromYear || $toYear) { + $search = array(); + $search['limit'] = $size; + $search['offset'] = $offset; + $search['type'] = "album"; + $i = 0; + if ($fromYear) { + $search['rule_'.$i.'_input'] = $fromYear; + $search['rule_'.$i.'_operator'] = 0; + $search['rule_'.$i.''] = "year"; + ++$i; + } + if ($toYear) { + $search['rule_'.$i.'_input'] = $toYear; + $search['rule_'.$i.'_operator'] = 1; + $search['rule_'.$i.''] = "year"; + ++$i; + } + + $query = new Search(null, 'album'); + $albums = $query->run($search); + } + } else if ($type == "byGenre") { + $genre = self::check_parameter($input, 'genre'); + + $tag_id = Tag::tag_exists($genre); + if ($tag_id) { + $albums = Tag::get_tag_objects('album', $tag_id, $size, $offset); + } } if (count($albums)) { @@ -526,17 +599,20 @@ class Subsonic_Api if (Subsonic_XML_Data::isArtist($musicFolderId)) { $artist = new Artist(Subsonic_XML_Data::getAmpacheId($musicFolderId)); $finput = $artist->name; + $operator = 4; $ftype = "artist"; } else if (Subsonic_XML_Data::isAlbum($musicFolderId)) { $album = new Album(Subsonic_XML_Data::getAmpacheId($musicFolderId)); $finput = $album->name; + $operator = 4; $ftype = "artist"; } else { - $finput = ""; - $ftype = ""; + $finput = intval($musicFolderId); + $operator = 0; + $ftype = "catalog"; } $search['rule_'.$i.'_input'] = $finput; - $search['rule_'.$i.'_operator'] = 4; + $search['rule_'.$i.'_operator'] = $operator; $search['rule_'.$i.''] = $ftype; ++$i; } @@ -617,6 +693,14 @@ class Subsonic_Api $query = self::check_parameter($input, 'query'); + $operator = 0; + if (strlen($query) > 1) { + if (substr($query, -1) == "*") { + $query = substr($query, 0, -1); + $operator = 2; // Start with + } + } + $artistCount = $input['artistCount']; $artistOffset = $input['artistOffset']; $albumCount = $input['albumCount']; @@ -628,7 +712,7 @@ class Subsonic_Api $sartist['limit'] = $artistCount; if ($artistOffset) $sartist['offset'] = $artistOffset; $sartist['rule_1_input'] = $query; - $sartist['rule_1_operator'] = 0; + $sartist['rule_1_operator'] = $operator; $sartist['rule_1'] = "name"; $sartist['type'] = "artist"; $artists = Search::run($sartist); @@ -637,7 +721,7 @@ class Subsonic_Api $salbum['limit'] = $albumCount; if ($albumOffset) $salbum['offset'] = $albumOffset; $salbum['rule_1_input'] = $query; - $salbum['rule_1_operator'] = 0; + $salbum['rule_1_operator'] = $operator; $salbum['rule_1'] = "title"; $salbum['type'] = "album"; $albums = Search::run($salbum); @@ -646,7 +730,7 @@ class Subsonic_Api $ssong['limit'] = $songCount; if ($songOffset) $ssong['offset'] = $songOffset; $ssong['rule_1_input'] = $query; - $ssong['rule_1_operator'] = 0; + $ssong['rule_1_operator'] = $operator; $ssong['rule_1'] = "anywhere"; $ssong['type'] = "song"; $songs = Search::run($ssong); @@ -680,7 +764,7 @@ class Subsonic_Api // Don't allow playlist listing for another user if (empty($username) || $username == $GLOBALS['user']->username) { - Subsonic_XML_Data::addPlaylists($r, Playlist::get_playlists()); + Subsonic_XML_Data::addPlaylists($r, Playlist::get_playlists(), Search::get_searches()); } else { $user = User::get_from_username($username); if ($user->id) { @@ -703,9 +787,14 @@ class Subsonic_Api $playlistid = self::check_parameter($input, 'id'); - $playlist = new Playlist($playlistid); $r = Subsonic_XML_Data::createSuccessResponse(); - Subsonic_XML_Data::addPlaylist($r, $playlist, true); + if (Subsonic_XML_Data::isSmartPlaylist($playlistid)) { + $playlist = new Search(Subsonic_XML_Data::getAmpacheId($playlistid), 'song'); + Subsonic_XML_Data::addSmartPlaylist($r, $playlist, true); + } else { + $playlist = new Playlist($playlistid); + Subsonic_XML_Data::addPlaylist($r, $playlist, true); + } self::apiOutput($input, $r); } @@ -726,7 +815,7 @@ class Subsonic_Api self::_updatePlaylist($playlistId, $name, $songId); $r = Subsonic_XML_Data::createSuccessResponse(); } else if (!empty($name)) { - $playlistId = Playlist::create($name, 'public'); + $playlistId = Playlist::create($name, 'private'); if (count($songId) > 0) { self::_updatePlaylist($playlistId, "", $songId); } @@ -746,20 +835,27 @@ class Subsonic_Api $newdata['pl_type'] = ($public) ? "public" : "private"; $playlist->update($newdata); - if (!is_array($songsIdToAdd)) { - $songsIdToAdd = array($songsIdToAdd); - } - if (count($songsIdToAdd) > 0) { - $playlist->add_songs(Subsonic_XML_Data::getAmpacheIds($songsIdToAdd)); + if ($songsIdToAdd) { + if (!is_array($songsIdToAdd)) { + $songsIdToAdd = array($songsIdToAdd); + } + if (count($songsIdToAdd) > 0) { + for ($i = 0; $i < count($songsIdToAdd); ++$i) { + $songsIdToAdd[$i] = Subsonic_XML_Data::getAmpacheId($songsIdToAdd[$i]); + } + $playlist->add_songs($songsIdToAdd); + } } - if (!is_array($songIndexToRemove)) { - $songIndexToRemove = array($songIndexToRemove); - } - if (is_array($songIndexToRemove) && count($songIndexToRemove) > 0) { - $tracks = Subsonic_XML_Data::getAmpacheIds($songIndexToRemove); - foreach ($tracks as $track) { - $playlist->delete_track_number($track); + if ($songIndexToRemove) { + if (!is_array($songIndexToRemove)) { + $songIndexToRemove = array($songIndexToRemove); + } + if (count($songIndexToRemove) > 0) { + foreach ($songIndexToRemove as $track) { + $playlist->delete_track_number($track); + } + $playlist->regenerate_track_numbers(); } } } @@ -777,12 +873,18 @@ class Subsonic_Api $name = $input['name']; $comment = $input['comment']; // Not supported. - $public = boolean($input['public']); - echo $public; - $songIdToAdd = $input['songIdToAdd']; - $songIndexToRemove = $input['songIndexToRemove']; + $public = ($input['public'] === "true"); - $r = Subsonic_XML_Data::createSuccessResponse(); + if (!Subsonic_XML_Data::isSmartPlaylist($playlistId)) { + $songIdToAdd = $input['songIdToAdd']; + $songIndexToRemove = $input['songIndexToRemove']; + + self::_updatePlaylist(Subsonic_XML_Data::getAmpacheId($playlistId), $name, $songIdToAdd, $songIndexToRemove, $public); + + $r = Subsonic_XML_Data::createSuccessResponse(); + } else { + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_UNAUTHORIZED, 'Cannot edit a smart playlist.'); + } self::apiOutput($input, $r); } @@ -795,10 +897,15 @@ class Subsonic_Api { self::check_version($input, "1.2.0"); - $playlistId = self::check_parameter($input, 'playlistId'); + $playlistId = self::check_parameter($input, 'id'); - $playlist = new Playlist($playlistId); - $playlist->delete(); + if (Subsonic_XML_Data::isSmartPlaylist($playlistId)) { + $playlist = new Search(Subsonic_XML_Data::getAmpacheId($playlistId), 'song'); + $playlist->delete(); + } else { + $playlist = new Playlist(Subsonic_XML_Data::getAmpacheId($playlistId)); + $playlist->delete(); + } $r = Subsonic_XML_Data::createSuccessResponse(); self::apiOutput($input, $r); @@ -815,19 +922,36 @@ class Subsonic_Api $fileid = self::check_parameter($input, 'id', true); - $maxBitRate = $input['maxBitRate']; // Not supported. - $format = $input['format']; // mp3, flv or raw. Not supported. - $timeOffset = $input['timeOffset']; // For video streaming. Not supported. + $maxBitRate = $input['maxBitRate']; + $format = $input['format']; // mp3, flv or raw + $timeOffset = $input['timeOffset']; $size = $input['size']; // For video streaming. Not supported. - $maxBitRate = $input['maxBitRate']; // For video streaming. Not supported. $estimateContentLength = $input['estimateContentLength']; // Force content-length guessing if transcode - $params = '&client=' . $input['c']; + $params = '&client=' . rawurlencode($input['c']) . '&noscrobble=1'; if ($estimateContentLength == 'true') { $params .= '&content_length=required'; } - $url = Song::play_url(Subsonic_XML_Data::getAmpacheId($fileid), $params); - self::follow_stream($url); + if ($format && $format != "raw") { + $params .= '&transcode_to=' . $format; + } + if ($maxBitRate) { + $params .= '&bitrate=' . $maxBitRate; + } + if ($timeOffset) { + $params .= '&frame=' . $timeOffset; + } + + $url = ''; + if (Subsonic_XML_Data::isVideo($fileid)) { + $url = Video::play_url(Subsonic_XML_Data::getAmpacheId($fileid), $params, 'api', function_exists('curl_version')); + } else if (Subsonic_XML_Data::isSong($fileid)) { + $url = Song::play_url(Subsonic_XML_Data::getAmpacheId($fileid), $params, 'api', function_exists('curl_version')); + } + + if (!empty($url)) { + self::follow_stream($url); + } } /** @@ -841,7 +965,7 @@ class Subsonic_Api $fileid = self::check_parameter($input, 'id', true); - $url = Song::play_url(Subsonic_XML_Data::getAmpacheId($fileid), '&action=download' . '&client=' . $input['c']); + $url = Song::play_url(Subsonic_XML_Data::getAmpacheId($fileid), '&action=download' . '&client=' . rawurlencode($input['c']) . '&noscrobble=1', 'api', function_exists('curl_version')); self::follow_stream($url); } @@ -856,7 +980,7 @@ class Subsonic_Api $fileid = self::check_parameter($input, 'id', true); - $bitRate = $input['bitRate']; // Not supported. + $bitRate = $input['bitRate']; $media = array(); $media['object_type'] = 'song'; @@ -865,7 +989,12 @@ class Subsonic_Api $medias = array(); $medias[] = $media; $stream = new Stream_Playlist(); - $stream->add($medias); + $additional_params = ''; + if ($bitRate) { + $additional_params .= '&bitrate=' . $bitRate; + } + //$additional_params .= '&transcode_to=ts'; + $stream->add($medias, $additional_params); header('Content-Type: application/vnd.apple.mpegurl;'); $stream->create_m3u(); @@ -894,19 +1023,22 @@ class Subsonic_Api if ($art != null) { $art->get_db(); - if (!$size) { - header('Content-type: ' . $art->raw_mime); - header('Content-Length: ' . strlen($art->raw)); - echo $art->raw; - } else { + if ($size) { $dim = array(); $dim['width'] = $size; $dim['height'] = $size; $thumb = $art->get_thumb($dim); - header('Content-type: ' . $thumb['thumb_mime']); - header('Content-Length: ' . strlen($thumb['thumb'])); - echo $thumb['thumb']; + if ($thumb) { + header('Content-type: ' . $thumb['thumb_mime']); + header('Content-Length: ' . strlen($thumb['thumb'])); + echo $thumb['thumb']; + return; + } } + + header('Content-type: ' . $art->raw_mime); + header('Content-Length: ' . strlen($art->raw)); + echo $art->raw; } } @@ -1093,6 +1225,41 @@ class Subsonic_Api self::apiOutput($input, $r); } + /** + * getAvatar + * Return the user avatar in bytes. + */ + public static function getavatar($input) + { + $username = self::check_parameter($input, 'username'); + + $r = null; + if ($GLOBALS['user']->access >= 100 || $GLOBALS['user']->username == $username) { + if ($GLOBALS['user']->username == $username) { + $user = $GLOBALS['user']; + } else { + $user = User::get_from_username($username); + } + + if ($user !== null) { + $avatar = $user->get_avatar(true); + if (isset($avatar['url']) && !empty($avatar['url'])) { + $request = Requests::get($avatar['url'], array(), Core::requests_options()); + header("Content-Type: " . $request->headers['Content-Type']); + echo $request->body; + } + } else { + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND); + } + } else { + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_UNAUTHORIZED, $GLOBALS['user']->username . ' is not authorized to get avatar for other users.'); + } + + if ($r != null) { + self::apiOutput($input, $r); + } + } + /** * getInternetRadioStations * Get all internet radio stations @@ -1103,7 +1270,7 @@ class Subsonic_Api self::check_version($input, "1.9.0"); $r = Subsonic_XML_Data::createSuccessResponse(); - $radios = Radio::get_all_radios(); + $radios = Live_Stream::get_all_radios(); Subsonic_XML_Data::addRadios($r, $radios); self::apiOutput($input, $r); } @@ -1134,11 +1301,15 @@ class Subsonic_Api $id = self::check_parameter($input, 'id'); $description = $input['description']; - $expires = $input['expires']; if (AmpConfig::get('share')) { - if ($expires) { - $expire_days = round((($expires / 1000) - time()) / 86400, 0, PHP_ROUND_HALF_EVEN); + if (isset($input['expires'])) { + $expires = $input['expires']; + // Parse as a string to work on 32-bit computers + if (strlen($expires) > 3) { + $expires = intval(substr($expires, 0, - 3)); + } + $expire_days = round(($expires - time()) / 86400, 0, PHP_ROUND_HALF_EVEN); } else { $expire_days = AmpConfig::get('share_expire'); } @@ -1187,21 +1358,6 @@ class Subsonic_Api self::apiOutput($input, $r); } - /**** CURRENT UNSUPPORTED FUNCTIONS ****/ - - /** - * getLyrics - * Searches and returns lyrics for a given song. - * Takes the optional artist and title in parameters. - */ - public static function getlyrics($input) - { - self::check_version($input, "1.2.0"); - - $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND); - self::apiOutput($input, $r); - } - /** * updateShare * Update the description and/or expiration date for an existing share. @@ -1212,22 +1368,44 @@ class Subsonic_Api { self::check_version($input, "1.6.0"); - $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND); - self::apiOutput($input, $r); - } + $id = self::check_parameter($input, 'id'); + $description = $input['description']; - /** - * scrobble - * Scrobbles a given music file on last.fm. - * Takes the file id with optional time and submission parameters. - * Not supported. Already done by Ampache if plugin enabled. - */ - public static function scrobble($input) - { - self::check_version($input, "1.5.0"); + if (AmpConfig::get('share')) { + $share = new Share($id); + if ($share->id > 0) { + $expires = $share->expire_days; + if (isset($input['expires'])) { + // Parse as a string to work on 32-bit computers + $expires = $input['expires']; + if (strlen($expires) > 3) { + $expires = intval(substr($expires, 0, - 3)); + } + if ($expires > 0) { + $expires = ($expires - $share->creation_date) / 86400; + $expires = ceil($expires); + } + } + + $data = array( + 'max_counter' => $share->max_counter, + 'expire' => $expires, + 'allow_stream' => $share->allow_stream, + 'allow_download' => $share->allow_download, + 'description' => $description ?: $share->description, + ); + if ($share->update($data)) { + $r = Subsonic_XML_Data::createSuccessResponse(); + } else { + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_UNAUTHORIZED); + } + } else { + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND); + } + } else { + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_UNAUTHORIZED); + } - // Ignore error to not break clients - $r = Subsonic_XML_Data::createSuccessResponse(); self::apiOutput($input, $r); } @@ -1235,13 +1413,54 @@ class Subsonic_Api * createUser * Create a new user. * Takes the username, password and email with optional roles in parameters. - * Not supported. */ public static function createuser($input) { self::check_version($input, "1.1.0"); - $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND); + $username = self::check_parameter($input, 'username'); + $password = self::check_parameter($input, 'password'); + $email = self::check_parameter($input, 'email'); + $ldapAuthenticated = $input['ldapAuthenticated']; + $adminRole = ($input['adminRole'] == 'true'); + //$settingsRole = $input['settingsRole']; + //$streamRole = $input['streamRole']; + //$jukeboxRole = $input['jukeboxRole']; + $downloadRole = ($input['downloadRole'] == 'true'); + $uploadRole = ($input['uploadRole'] == 'true'); + //$playlistRole = $input['playlistRole']; + $coverArtRole = ($input['coverArtRole'] == 'true'); + //$commentRole = $input['commentRole']; + //$podcastRole = $input['podcastRole']; + $shareRole = ($input['shareRole'] == 'true'); + + if (Access::check('interface', 100)) { + $access = 25; + if ($adminRole) { + $access = 100; + } elseif ($coverArtRole) { + $access = 75; + } + $password = self::decrypt_password($password); + $user_id = User::create($username, $username, $email, null, $password, $access); + if ($user_id > 0) { + if ($downloadRole) { + Preference::update('download', $user_id, '1'); + } + if ($uploadRole) { + Preference::update('allow_upload', $user_id, '1'); + } + if ($shareRole) { + Preference::update('share', $user_id, '1'); + } + $r = Subsonic_XML_Data::createSuccessResponse(); + } else { + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND); + } + } else { + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_UNAUTHORIZED); + } + self::apiOutput($input, $r); } @@ -1249,13 +1468,24 @@ class Subsonic_Api * deleteUser * Delete an existing user. * Takes the username in parameter. - * Not supported. */ public static function deleteuser($input) { self::check_version($input, "1.3.0"); - $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND); + $username = self::check_parameter($input, 'username'); + if (Access::check('interface', 100)) { + $user = User::get_from_username($username); + if ($user->id) { + $user->delete(); + $r = Subsonic_XML_Data::createSuccessResponse(); + } else { + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND); + } + } else { + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_UNAUTHORIZED); + } + self::apiOutput($input, $r); } @@ -1263,16 +1493,284 @@ class Subsonic_Api * changePassword * Change the password of an existing user. * Takes the username with new password in parameters. - * Not supported. */ public static function changepassword($input) { self::check_version($input, "1.1.0"); - $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND); + $username = self::check_parameter($input, 'username'); + $password = self::check_parameter($input, 'password'); + $password = self::decrypt_password($password); + + if ($GLOBALS['user']->username == $username || Access::check('interface', 100)) { + $user = User::get_from_username($username); + if ($user->id) { + $user->update_password($password); + $r = Subsonic_XML_Data::createSuccessResponse(); + } else { + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND); + } + } else { + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_UNAUTHORIZED); + } self::apiOutput($input, $r); } + /** + * jukeboxControl + * Control the jukebox. + * Takes the action with optional index, offset, song id and volume gain in parameters. + * Not supported. + */ + public static function jukeboxcontrol($input) + { + self::check_version($input, "1.2.0"); + $action = self::check_parameter($input, 'action'); + $id = $input['id']; + $gain = $input['gain']; + + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND); + debug_event('subsonic', 'Using Localplay controller: ' . AmpConfig::get('localplay_controller'), 5); + $localplay = new Localplay(AmpConfig::get('localplay_controller')); + + if ($localplay->connect()) { + $ret = false; + switch ($action) { + case 'get': + case 'status': + $ret = true; + break; + case 'start': + $ret = $localplay->play(); + break; + case 'stop': + $ret = $localplay->stop(); + break; + case 'skip': + if (isset($input['index'])) { + if ($localplay->skip($input['index'])) + $ret = $localplay->play(); + } elseif (isset($input['offset'])) { + debug_event('subsonic', 'Skip with offset is not supported on JukeboxControl.', 5); + } else { + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_MISSINGPARAM); + } + break; + case 'set': + $localplay->delete_all(); + case 'add': + if ($id) { + if (!is_array($id)) { + $rid = array(); + $rid[] = $id; + $id = $rid; + } + + foreach ($id as $i) { + $url = null; + if (Subsonic_XML_Data::isSong($i)) { + $url = Song::generic_play_url('song', Subsonic_XML_Data::getAmpacheId($i), '', 'api'); + } elseif (Subsonic_XML_Data::isVideo($i)) { + $url = Song::generic_play_url('video', Subsonic_XML_Data::getAmpacheId($i), '', 'api'); + } + + if ($url) { + debug_event('subsonic', 'Adding ' . $url, 5); + $stream = array(); + $stream['url'] = $url; + $ret = $localplay->add_url(new Stream_URL($stream)); + } + } + } + break; + case 'clear': + $ret = $localplay->delete_all(); + break; + case 'remove': + if (isset($input['index'])) { + $ret = $localplay->delete_track($input['index']); + } else { + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_MISSINGPARAM); + } + break; + case 'shuffle': + $ret = $localplay->random(true); + break; + case 'setGain': + $ret = $localplay->volume_set($gain * 100); + break; + } + + if ($ret) { + $r = Subsonic_XML_Data::createSuccessResponse(); + if ($action == 'get') { + Subsonic_XML_Data::addJukeboxPlaylist($r, $localplay); + } else { + Subsonic_XML_Data::createJukeboxStatus($r, $localplay); + } + } + } + + self::apiOutput($input, $r); + } + + /** + * scrobble + * Scrobbles a given music file on last.fm. + * Takes the file id with optional time and submission parameters. + */ + public static function scrobble($input) + { + self::check_version($input, "1.5.0"); + + $id = self::check_parameter($input, 'id'); + //$time = $input['time']; + //$submission = $input['submission']; + + if (!is_array($id)) { + $rid = array(); + $rid[] = $id; + $id = $rid; + } + + foreach ($id as $i) { + $aid = Subsonic_XML_Data::getAmpacheId($i); + if (Subsonic_XML_Data::isVideo($i)) { + $type = 'video'; + } else { + $type = 'song'; + } + + $media = new $type($aid); + $media->format(); + $GLOBALS['user']->save_mediaplay($GLOBALS['user'], $media); + } + + $r = Subsonic_XML_Data::createSuccessResponse(); + self::apiOutput($input, $r); + } + + /** + * getLyrics + * Searches and returns lyrics for a given song. + * Takes the optional artist and title in parameters. + */ + public static function getlyrics($input) + { + self::check_version($input, "1.2.0"); + + $artist = $input['artist']; + $title = $input['title']; + + if (!$artist && !$title) { + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_MISSINGPARAM); + } else { + $search = array(); + $search['limit'] = 1; + $search['offset'] = 0; + $search['type'] = "song"; + + $i = 0; + if ($artist) { + $search['rule_'.$i.'_input'] = $artist; + $search['rule_'.$i.'_operator'] = 5; + $search['rule_'.$i.''] = "artist"; + ++$i; + } + if ($title) { + $search['rule_'.$i.'_input'] = $title; + $search['rule_'.$i.'_operator'] = 5; + $search['rule_'.$i.''] = "title"; + ++$i; + } + + $query = new Search(null, 'song'); + $songs = $query->run($search); + + $r = Subsonic_XML_Data::createSuccessResponse(); + if (count($songs) > 0) { + Subsonic_XML_Data::addLyrics($r, $artist, $title, $songs[0]); + } + } + + self::apiOutput($input, $r); + } + + /** + * getArtistInfo + * Returns artist info with biography, image URLs and similar artists, using data from last.fm. + * Takes artist id in parameter with optional similar artist count and if not present similar artist should be returned. + */ + public static function getartistinfo($input) + { + $id = self::check_parameter($input, 'id'); + $count = $input['count'] ?: 20; + $includeNotPresent = ($input['includeNotPresent'] === "true"); + + if (Subsonic_XML_Data::isArtist($id)) { + $artist_id = Subsonic_XML_Data::getAmpacheId($id); + $info = Recommendation::get_artist_info($artist_id); + $similars = Recommendation::get_artists_like($artist_id, $count, !$includeNotPresent); + $r = Subsonic_XML_Data::createSuccessResponse(); + Subsonic_XML_Data::addArtistInfo($r, $info, $similars); + } else { + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND); + } + + self::apiOutput($input, $r); + } + + /** + * getArtistInfo2 + * See getArtistInfo. + */ + public static function getartistinfo2($input) + { + return self::getartistinfo($input); + } + + /** + * getSimilarSongs + * Returns a random collection of songs from the given artist and similar artists, using data from last.fm. Typically used for artist radio features. + * Takes song/album/artist id in parameter with optional similar songs count. + */ + public static function getsimilarsongs($input) + { + $id = self::check_parameter($input, 'id'); + $count = $input['count'] ?: 50; + + $songs = null; + if (Subsonic_XML_Data::isArtist($id)) { + // TODO: support similar songs for artists + } elseif (Subsonic_XML_Data::isAlbum($id)) { + // TODO: support similar songs for albums + } elseif (Subsonic_XML_Data::isSong($id)) { + if (AmpConfig::get('show_similar')) { + $songs = Recommendation::get_songs_like(Subsonic_XML_Data::getAmpacheId($id)); + } + } + + if ($songs === null) { + $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND); + } else { + $r = Subsonic_XML_Data::createSuccessResponse(); + Subsonic_XML_Data::addSimilarSongs($r, $songs); + } + + self::apiOutput($input, $r); + } + + /** + * getSimilarSongs2 + * See getSimilarSongs. + */ + public static function getsimilarsongs2($input) + { + return self::getsimilarsongs($input); + } + + /**** CURRENT UNSUPPORTED FUNCTIONS ****/ + /** * getPodcasts * Get all podcast channels. @@ -1357,20 +1855,6 @@ class Subsonic_Api self::apiOutput($input, $r); } - /** - * jukeboxControl - * Control the jukebox. - * Takes the action with optional index, offset, song id and volume gain in parameters. - * Not supported. - */ - public static function jukeboxcontrol($input) - { - self::check_version($input, "1.2.0"); - - $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND); - self::apiOutput($input, $r); - } - /** * getChatMessages * Get the current chat messages. diff --git a/sources/lib/class/subsonic_xml_data.class.php b/sources/lib/class/subsonic_xml_data.class.php index dddc885..675b8fd 100644 --- a/sources/lib/class/subsonic_xml_data.class.php +++ b/sources/lib/class/subsonic_xml_data.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,7 +30,7 @@ */ class Subsonic_XML_Data { - const API_VERSION = "1.10.1"; + const API_VERSION = "1.11.0"; const SSERROR_GENERIC = 0; const SSERROR_MISSINGPARAM = 10; @@ -45,6 +45,8 @@ class Subsonic_XML_Data const AMPACHEID_ARTIST = 100000000; const AMPACHEID_ALBUM = 200000000; const AMPACHEID_SONG = 300000000; + const AMPACHEID_SMARTPL = 400000000; + const AMPACHEID_VIDEO = 500000000; /** * constructor @@ -70,6 +72,16 @@ class Subsonic_XML_Data return $id + Subsonic_XML_Data::AMPACHEID_SONG; } + public static function getSmartPlId($id) + { + return $id + Subsonic_XML_Data::AMPACHEID_SMARTPL; + } + + public static function getVideoId($id) + { + return $id + Subsonic_XML_Data::AMPACHEID_VIDEO; + } + public static function getAmpacheId($id) { return ($id % Subsonic_XML_Data::AMPACHEID_ARTIST); @@ -99,6 +111,16 @@ class Subsonic_XML_Data return ($id >= Subsonic_XML_Data::AMPACHEID_SONG); } + public static function isSmartPlaylist($id) + { + return ($id >= Subsonic_XML_Data::AMPACHEID_SMARTPL); + } + + public static function isVideo($id) + { + return ($id >= Subsonic_XML_Data::AMPACHEID_VIDEO); + } + public static function createFailedResponse($version = "") { $response = self::createResponse($version); @@ -118,6 +140,7 @@ class Subsonic_XML_Data if (empty($version)) $version = Subsonic_XML_Data::API_VERSION; $response = new SimpleXMLElement(''); $response->addAttribute('xmlns', 'http://subsonic.org/restapi'); + $response->addAttribute('type', 'ampache'); $response->addAttribute('version', $version); return $response; } @@ -135,7 +158,7 @@ class Subsonic_XML_Data * * @param SimpleXMLElement $xml Parent node * @param integer $code Error code - * @param string $string Error message + * @param string $message Error message */ public static function setError($xml, $code, $message = "") { @@ -181,7 +204,7 @@ class Subsonic_XML_Data public static function addArtistsIndexes($xml, $artists, $lastModified) { $xindexes = $xml->addChild('indexes'); - $xindexes->addAttribute('lastModified', $lastModified * 1000); + $xindexes->addAttribute('lastModified', number_format($lastModified * 1000, 0, '.', '')); self::addArtists($xindexes, $artists); } @@ -217,7 +240,9 @@ class Subsonic_XML_Data } } - self::addArtist($xlastcat, $artist, $extra); + if ($xlastcat != null) { + self::addArtist($xlastcat, $artist, $extra); + } } } @@ -246,7 +271,7 @@ class Subsonic_XML_Data public static function addAlbumList($xml, $albums, $elementName="albumList") { - $xlist = $xml->addChild($elementName); + $xlist = $xml->addChild(htmlspecialchars($elementName)); foreach ($albums as $id) { $album = new Album($id); self::addAlbum($xlist, $album); @@ -255,10 +280,10 @@ class Subsonic_XML_Data public static function addAlbum($xml, $album, $songs=false, $elementName="album") { - $xalbum = $xml->addChild($elementName); + $xalbum = $xml->addChild(htmlspecialchars($elementName)); $xalbum->addAttribute('id', self::getAlbumId($album->id)); $xalbum->addAttribute('album', $album->name); - $xalbum->addAttribute('title', self::formatAlbum($album)); + $xalbum->addAttribute('title', self::formatAlbum($album, $elementName === "album")); $xalbum->addAttribute('name', $album->name); $xalbum->addAttribute('isDir', 'true'); $album->format(); @@ -270,10 +295,24 @@ class Subsonic_XML_Data $xalbum->addAttribute('artistId', self::getArtistId($album->artist_id)); $xalbum->addAttribute('parent', self::getArtistId($album->artist_id)); $xalbum->addAttribute('artist', $album->artist_name); + if ($album->year > 0) { + $xalbum->addAttribute('year', $album->year); + } + if (count($album->tags) > 0) { + $tag_values = array_values($album->tags); + $tag = array_shift($tag_values); + $xalbum->addAttribute('genre', $tag['name']); + } $rating = new Rating($album->id, "album"); - $rating_value = $rating->get_average_rating(); - $xalbum->addAttribute('averageRating', ($rating_value) ? $rating_value : 0); + $user_rating = $rating->get_user_rating(); + if ($user_rating > 0) { + $xalbum->addAttribute('userRating', ceil($user_rating)); + } + $avg_rating = $rating->get_average_rating(); + if ($avg_rating > 0) { + $xalbum->addAttribute('averageRating', ceil($avg_rating)); + } if ($songs) { $allsongs = $album->get_songs(); @@ -291,7 +330,7 @@ class Subsonic_XML_Data public static function createSong($xml, $song, $elementName='song') { - $xsong = $xml->addChild($elementName); + $xsong = $xml->addChild(htmlspecialchars($elementName)); $xsong->addAttribute('id', self::getSongId($song->id)); $xsong->addAttribute('parent', self::getAlbumId($song->album)); //$xsong->addAttribute('created', ); @@ -303,11 +342,20 @@ class Subsonic_XML_Data $xsong->addAttribute('albumId', self::getAlbumId($album->id)); $xsong->addAttribute('album', $album->name); $artist = new Artist($song->artist); - $xsong->addAttribute('artistId', self::getArtistId($album->id)); + $xsong->addAttribute('artistId', self::getArtistId($song->artist)); $xsong->addAttribute('artist', $artist->name); $xsong->addAttribute('coverArt', self::getAlbumId($album->id)); $xsong->addAttribute('duration', $song->time); $xsong->addAttribute('bitRate', intval($song->bitrate / 1000)); + $rating = new Rating($song->id, "song"); + $user_rating = $rating->get_user_rating(); + if ($user_rating > 0) { + $xsong->addAttribute('userRating', ceil($user_rating)); + } + $avg_rating = $rating->get_average_rating(); + if ($avg_rating > 0) { + $xsong->addAttribute('averageRating', ceil($avg_rating)); + } if ($song->track > 0) { $xsong->addAttribute('track', $song->track); } @@ -326,9 +374,9 @@ class Subsonic_XML_Data // Set transcoding information if required $transcode_cfg = AmpConfig::get('transcode'); - $transcode_mode = AmpConfig::get('transcode_' . $song->type); - if ($transcode_cfg == 'always' || ($transcode_cfg != 'never' && $transcode_mode == 'required')) { - $transcode_settings = $song->get_transcode_settings(null); + $valid_types = Song::get_stream_types_for_type($song->type, 'api'); + if ($transcode_cfg == 'always' || ($transcode_cfg != 'never' && !in_array('native', $valid_types))) { + $transcode_settings = $song->get_transcode_settings(null, 'api'); if ($transcode_settings) { $transcode_type = $transcode_settings['format']; $xsong->addAttribute('transcodedSuffix', $transcode_type); @@ -339,14 +387,14 @@ class Subsonic_XML_Data return $xsong; } - private static function formatAlbum($album) + private static function formatAlbum($album, $checkDisk = true) { $name = $album->name; if ($album->year > 0) { $name .= " [" . $album->year . "]"; } - if ($album->disk) { + if (($checkDisk || !AmpConfig::get('album_group')) && $album->disk) { $name .= " [" . T_('Disk') . " " . $album->disk . "]"; } @@ -359,7 +407,7 @@ class Subsonic_XML_Data $xdir->addAttribute('id', self::getArtistId($artist->id)); $xdir->addAttribute('name', $artist->name); - $allalbums = $artist->get_albums(null, true); + $allalbums = $artist->get_albums(); foreach ($allalbums as $id) { $album = new Album($id); self::addAlbum($xdir, $album, false, "child"); @@ -370,14 +418,20 @@ class Subsonic_XML_Data { $xdir = $xml->addChild('directory'); $xdir->addAttribute('id', self::getAlbumId($album->id)); - $xdir->addAttribute('name', self::formatAlbum($album)); + $xdir->addAttribute('name', self::formatAlbum($album, false)); $album->format(); - //$xdir->addAttribute('parent', self::getArtistId($album->artist_id)); + if ($album->artist_id) { + $xdir->addAttribute('parent', self::getArtistId($album->artist_id)); + } - $allsongs = $album->get_songs(); - foreach ($allsongs as $id) { - $song = new Song($id); - self::addSong($xdir, $song, "child"); + $disc_ids = $album->get_group_disks_ids(); + foreach ($disc_ids as $id) { + $disc = new Album($id); + $allsongs = $disc->get_songs(); + foreach ($allsongs as $id) { + $song = new Song($id); + self::addSong($xdir, $song, "child"); + } } } @@ -387,23 +441,65 @@ class Subsonic_XML_Data foreach ($tags as $tag) { $otag = new Tag($tag['id']); - $xgenres->addChild('genre', $otag->name); + $xgenres->addChild('genre', htmlspecialchars($otag->name)); } } - public static function addVideos($xml) + public static function addVideos($xml, $videos) { - // Not supported yet - $xml->addChild('videos'); + $xvideos = $xml->addChild('videos'); + foreach ($videos as $video) { + $video->format(); + self::addVideo($xvideos, $video); + } } - public static function addPlaylists($xml, $playlists) + public static function addVideo($xml, $video) + { + $xvideo = $xml->addChild('video'); + $xvideo->addAttribute('id', self::getVideoId($video->id)); + $xvideo->addAttribute('title', $video->f_full_title); + $xvideo->addAttribute('isDir', 'false'); + $xvideo->addAttribute('coverArt', self::getVideoId($video->id)); + $xvideo->addAttribute('isVideo', 'true'); + $xvideo->addAttribute('type', 'video'); + $xvideo->addAttribute('duration', $video->time); + if ($video->year > 0) { + $xvideo->addAttribute('year', $video->year); + } + $tags = Tag::get_object_tags('video', $video->id); + if (count($tags) > 0) $xvideo->addAttribute('genre', $tags[0]['name']); + $xvideo->addAttribute('size', $video->size); + $xvideo->addAttribute('suffix', $video->type); + $xvideo->addAttribute('contentType', $video->mime); + // Create a clean fake path instead of song real file path to have better offline mode storage on Subsonic clients + $path = basename($video->file); + $xvideo->addAttribute('path', $path); + + // Set transcoding information if required + $transcode_cfg = AmpConfig::get('transcode'); + $valid_types = Song::get_stream_types_for_type($video->type, 'api'); + if ($transcode_cfg == 'always' || ($transcode_cfg != 'never' && !in_array('native', $valid_types))) { + $transcode_settings = $video->get_transcode_settings(null, 'api'); + if ($transcode_settings) { + $transcode_type = $transcode_settings['format']; + $xvideo->addAttribute('transcodedSuffix', $transcode_type); + $xvideo->addAttribute('transcodedContentType', Video::type_to_mime($transcode_type)); + } + } + } + + public static function addPlaylists($xml, $playlists, $smartplaylists = array()) { $xplaylists = $xml->addChild('playlists'); foreach ($playlists as $id) { $playlist = new Playlist($id); self::addPlaylist($xplaylists, $playlist); } + foreach ($smartplaylists as $id) { + $smartplaylist = new Search($id, 'song'); + self::addSmartPlaylist($xplaylists, $smartplaylist); + } } public static function addPlaylist($xml, $playlist, $songs=false) @@ -427,6 +523,24 @@ class Subsonic_XML_Data } } + public static function addSmartPlaylist($xml, $playlist, $songs=false) + { + $xplaylist = $xml->addChild('playlist'); + $xplaylist->addAttribute('id', self::getSmartPlId($playlist->id)); + $xplaylist->addAttribute('name', $playlist->name); + $user = new User($playlist->user); + $xplaylist->addAttribute('owner', $user->username); + $xplaylist->addAttribute('public', ($playlist->type != "private") ? "true" : "false"); + + if ($songs) { + $allitems = $playlist->get_items(); + foreach ($allitems as $item) { + $song = new Song($item['object_id']); + self::addSong($xplaylist, $song, "entry"); + } + } + } + public static function addRandomSongs($xml, $songs) { $xsongs = $xml->addChild('randomSongs'); @@ -458,7 +572,7 @@ class Subsonic_XML_Data public static function addSearchResult($xml, $artists, $albums, $songs, $elementName = "searchResult2") { - $xresult = $xml->addChild($elementName); + $xresult = $xml->addChild(htmlspecialchars($elementName)); foreach ($artists as $id) { $artist = new Artist($id); self::addArtist($xresult, $artist); @@ -475,7 +589,7 @@ class Subsonic_XML_Data public static function addStarred($xml, $artists, $albums, $songs, $elementName="starred") { - $xstarred = $xml->addChild($elementName); + $xstarred = $xml->addChild(htmlspecialchars($elementName)); foreach ($artists as $id) { $artist = new Artist($id); @@ -498,11 +612,11 @@ class Subsonic_XML_Data $xuser = $xml->addChild('user'); $xuser->addAttribute('username', $user->username); $xuser->addAttribute('email', $user->email); - $xuser->addAttribute('scrobblingEnabled', 'false'); + $xuser->addAttribute('scrobblingEnabled', 'true'); $isManager = ($user->access >= 75); $isAdmin = ($user->access >= 100); $xuser->addAttribute('adminRole', $isAdmin ? 'true' : 'false'); - $xuser->addAttribute('settingsRole', $isAdmin ? 'true' : 'false'); + $xuser->addAttribute('settingsRole', 'true'); $xuser->addAttribute('downloadRole', Preference::get_by_user($user->id, 'download') ? 'true' : 'false'); $xuser->addAttribute('playlistRole', 'true'); $xuser->addAttribute('coverArtRole', $isManager ? 'true' : 'false'); @@ -510,7 +624,7 @@ class Subsonic_XML_Data $xuser->addAttribute('podcastRole', 'false'); $xuser->addAttribute('streamRole', 'true'); $xuser->addAttribute('jukeboxRole', 'false'); - $xuser->addAttribute('shareRole', 'false'); + $xuser->addAttribute('shareRole', Preference::get_by_user($user->id, 'share') ? 'true' : 'false'); } public static function addUsers($xml, $users) @@ -535,14 +649,14 @@ class Subsonic_XML_Data { $xradios = $xml->addChild('internetRadioStations'); foreach ($radios as $id) { - $radio = new Radio($id); + $radio = new Live_Stream($id); self::addRadio($xradios, $radio); } } public static function addShare($xml, $share) { - $xshare = $xml->addChild('share '); + $xshare = $xml->addChild('share'); $xshare->addAttribute('id', $share->id); $xshare->addAttribute('url', $share->public_url); $xshare->addAttribute('description', $share->description); @@ -588,4 +702,79 @@ class Subsonic_XML_Data } } } + + public static function addJukeboxPlaylist($xml, Localplay $localplay) + { + $xjbox = self::createJukeboxStatus($xml, $localplay, 'jukeboxPlaylist'); + $tracks = $localplay->get(); + foreach ($tracks as $track) { + if ($track['oid']) { + $song = new Song($track['oid']); + self::createSong($xjbox, $song, 'entry'); + } + } + } + + public static function createJukeboxStatus($xml, Localplay $localplay, $elementName = 'jukeboxStatus') + { + $xjbox = $xml->addChild($elementName); + $status = $localplay->status(); + $xjbox->addAttribute('currentIndex', 0); // Not supported + $xjbox->addAttribute('playing', ($status['state'] == 'play') ? 'true' : 'false'); + $xjbox->addAttribute('gain', $status['volume']); + $xjbox->addAttribute('position', 0); // Not supported + + return $xjbox; + } + + public static function addLyrics($xml, $artist, $title, $song_id) + { + $song = new Song($song_id); + $song->format(); + $song->fill_ext_info(); + $lyrics = $song->get_lyrics(); + + if ($lyrics && $lyrics['text']) { + $text = preg_replace('/\/i', "\n", $lyrics['text']); + $text = str_replace("\r", '', $text); + $xlyrics = $xml->addChild("lyrics", $text); + if ($artist) { + $xlyrics->addAttribute("artist", $artist); + } + if ($title) { + $xlyrics->addAttribute("title", $title); + } + } + } + + public static function addArtistInfo($xml, $info, $similars) + { + $artist = new Artist($info['id']); + + $xartist = $xml->addChild("artistInfo"); + $xartist->addChild("biography", trim($info['summary'])); + $xartist->addChild("musicBrainzId", $artist->mbid); + //$xartist->addChild("lastFmUrl", ""); + $xartist->addChild("smallImageUrl", htmlentities($info['smallphoto'])); + $xartist->addChild("mediumImageUrl", htmlentities($info['mediumphoto'])); + $xartist->addChild("largeImageUrl", htmlentities($info['largephoto'])); + + foreach ($similars as $similar) { + $xsimilar = $xartist->addChild("similarArtist"); + $xsimilar->addAttribute("id", ($similar['id'] !== null ? self::getArtistId($similar['id']) : "-1")); + $xsimilar->addAttribute("name", $similar['name']); + } + } + + public static function addSimilarSongs($xml, $similar_songs) + { + $xsimilar = $xml->addChild("similarSongs"); + foreach ($similar_songs as $similar_song) { + $song = new Song($similar_song['id']); + $song->format(); + if ($song->id) { + self::addSong($xsimilar, $song); + } + } + } } diff --git a/sources/lib/class/tag.class.php b/sources/lib/class/tag.class.php index a318432..b83151c 100644 --- a/sources/lib/class/tag.class.php +++ b/sources/lib/class/tag.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -26,10 +26,11 @@ * This class hnadles all of the tag relation operations * */ -class Tag extends database_object +class Tag extends database_object implements library_item { public $id; public $name; + public $is_hidden; /** * constructor @@ -90,7 +91,9 @@ class Tag extends database_object { if (!is_array($ids) OR !count($ids)) { return false; } - $type = self::validate_type($type); + if (!Core::is_library_item($type)) + return false; + $idlist = '(' . implode(',',$ids) . ')'; $sql = "SELECT `tag_map`.`id`,`tag_map`.`tag_id`, `tag`.`name`,`tag_map`.`object_id`,`tag_map`.`user` FROM `tag` " . @@ -128,8 +131,8 @@ class Tag extends database_object */ public static function add($type, $id, $value, $user=false) { - // Validate the tag type - if (!self::validate_type($type)) { return false; } + if (!Core::is_library_item($type)) + return false; if (!is_numeric($id)) { return false; } @@ -166,10 +169,8 @@ class Tag extends database_object { if (!strlen($value)) { return false; } - $value = Dba::escape($value); - - $sql = "REPLACE INTO `tag` SET `name`='$value'"; - Dba::write($sql); + $sql = "REPLACE INTO `tag` SET `name` = ?"; + Dba::write($sql, array($value)); $insert_id = Dba::insert_id(); parent::add_to_cache('tag_name', $value, $insert_id); @@ -182,18 +183,88 @@ class Tag extends database_object * update * Update the name of the tag */ - public function update($name) + public function update(array $data) { //debug_event('tag.class', 'Updating tag {'.$this->id.'} with name {'.$name.'}...', '5'); - if (!strlen($name)) { return false; } - - $name = Dba::escape($name); + if (!strlen($data['name'])) { return false; } $sql = 'UPDATE `tag` SET `name` = ? WHERE `id` = ?'; - Dba::write($sql, array($name, $this->id)); + Dba::write($sql, array($data[name], $this->id)); + + if ($data['edit_tags']) { + $tag_names = explode(',', $data['edit_tags']); + foreach ($tag_names as $tag) { + $merge_to = Tag::construct_from_name($tag); + if ($merge_to->id == 0) { + Tag::add_tag($tag); + $merge_to = Tag::construct_from_name($tag); + } + $this->merge($merge_to->id, $data['merge_persist'] == '1'); + } + if ($data['keep_existing'] != '1') { + $sql = "DELETE FROM `tag_map` WHERE `tag_map`.`tag_id` = ? "; + Dba::write($sql, array($this->id)); + if ($data['merge_persist'] != '1') { + $this->delete(); + } else { + $sql = "UPDATE `tag` SET `is_hidden` = true WHERE `tag`.`tag` = ? "; + Dba::write($sql, array($this->id)); + } + } + } + return $this->id; } // add_tag + /** + * merge + * merges this tag to another one. + */ + public function merge($merge_to, $is_persistent) + { + if ($this->id != $merge_to) { + debug_event('tag', 'Merging tag ' . $this->id . ' into ' . $merge_to . ')...', '5'); + + $sql = "INSERT INTO `tag_map` (`tag_id`,`user`,`object_type`,`object_id`) " . + "SELECT ?,`user`,`object_type`,`object_id` " . + "FROM `tag_map` AS `tm`" . + "WHERE `tm`.`tag_id` = ? AND NOT EXISTS ( " . + "SELECT 1 FROM `tag_map` ". + "WHERE `tag_map`.`tag_id` = ? " . + "AND `tag_map`.`object_id` = `tm`.`object_id` " . + "AND `tag_map`.`object_type` = `tm`.`object_type` " . + "AND `tag_map`.`user` = `tm`.`user`" . + ")"; + Dba::write($sql, array($merge_to, $this->id, $merge_to)); + if ($is_persistent) { + $sql = 'INSERT INTO `tag_merge` (`tag_id`, `merged_to`) VALUES (?, ?)'; + Dba::write($sql, array($this->id, $merge_to)); + } + } + } + + /** + * get_merged_tags + * Get merged tags to this tag. + */ + public function get_merged_tags() + { + $sql = "SELECT `tag`.`id`, `tag`.`name`" . + "FROM `tag_merge` " . + "INNER JOIN `tag` ON `tag`.`id` = `tag_merge`.`merged_to` ". + "WHERE `tag_merge`.`tag_id` = ? " . + "ORDER BY `tag`.`name` "; + + $db_results = Dba::read($sql, array($this->id)); + + $results = array(); + while ($row = Dba::fetch_assoc($db_results)) { + $results[$row['id']] = array('id'=>$row['id'], 'name'=>$row['name']); + } + + return $results; + } + /** * add_tag_map * This adds a specific tag to the map for specified object @@ -202,14 +273,23 @@ class Tag extends database_object { $uid = ($user == '') ? intval($GLOBALS['user']->id) : intval($user); $tag_id = intval($tag_id); - if (!self::validate_type($type)) { return false; } + if (!Core::is_library_item($type)) + return false; $id = intval($object_id); if (!$tag_id || !$id) { return false; } - $sql = "INSERT INTO `tag_map` (`tag_id`,`user`,`object_type`,`object_id`) " . - "VALUES ('$tag_id','$uid','$type','$id')"; - Dba::write($sql); + // If tag merged to another one, add reference to the merge destination + $parent = new Tag($tag_id); + $merges = $parent->get_merged_tags(); + if ($parent->is_hidden == false) { + $merges[] = array('id' => $parent->id, 'name' => $parent->name); + } + foreach ($merges as $tag) { + $sql = "INSERT INTO `tag_map` (`tag_id`,`user`,`object_type`,`object_id`) " . + "VALUES (?, ?, ?, ?)"; + Dba::write($sql, array($tag['id'], $uid, $type, $id)); + } $insert_id = Dba::insert_id(); parent::add_to_cache('tag_map_' . $type,$insert_id,array('tag_id'=>$tag_id,'user'=>$uid,'object_type'=>$type,'object_id'=>$id)); @@ -242,9 +322,18 @@ class Tag extends database_object "WHERE `tag_map`.`object_type`='video' AND `video`.`id` IS NULL"; Dba::write($sql); + $sql = "DELETE FROM `tag_map` USING `tag_map` LEFT JOIN `tvshow` ON `tvshow`.`id`=`tag_map`.`object_id` " . + "WHERE `tag_map`.`object_type`='tvshow' AND `tvshow`.`id` IS NULL"; + Dba::write($sql); + + $sql = "DELETE FROM `tag_map` USING `tag_map` LEFT JOIN `tvshow_season` ON `tvshow_season`.`id`=`tag_map`.`object_id` " . + "WHERE `tag_map`.`object_type`='tvshow_season' AND `tvshow_season`.`id` IS NULL"; + Dba::write($sql); + // Now nuke the tags themselves $sql = "DELETE FROM `tag` USING `tag` LEFT JOIN `tag_map` ON `tag`.`id`=`tag_map`.`tag_id` " . - "WHERE `tag_map`.`id` IS NULL"; + "WHERE `tag_map`.`id` IS NULL " . + "AND NOT EXISTS (SELECT 1 FROM `tag_merge` where `tag_merge`.`tag_id` = `tag`.`id`)"; Dba::write($sql); } @@ -255,11 +344,15 @@ class Tag extends database_object */ public function delete() { - $sql = "DELETE FROM `tag_map` WHERE `tag_map`.`tag_id`='".$this->id."'"; - Dba::write($sql); + $sql = "DELETE FROM `tag_map` WHERE `tag_map`.`tag_id` = ?"; + Dba::write($sql, array($this->id)); - $sql = "DELETE FROM `tag` WHERE `tag`.`id`='".$this->id."'"; - Dba::write($sql); + $sql = "DELETE FROM `tag_merge` " . + "WHERE `tag_merge`.`tag_id` = ?"; + Dba::write($sql, array($this->id)); + + $sql = "DELETE FROM `tag` WHERE `tag`.`id` = ? "; + Dba::write($sql, array($this->id, $this->id)); // Call the garbage collector to clean everything Tag::gc(); @@ -277,9 +370,8 @@ class Tag extends database_object return parent::get_from_cache('tag_name',$value); } - $value = Dba::escape($value); - $sql = "SELECT * FROM `tag` WHERE `name`='$value'"; - $db_results = Dba::read($sql); + $sql = "SELECT * FROM `tag` WHERE `name` = ?"; + $db_results = Dba::read($sql, array($value)); $results = Dba::fetch_assoc($db_results); @@ -296,15 +388,12 @@ class Tag extends database_object */ public static function tag_map_exists($type,$object_id,$tag_id,$user) { - if (!self::validate_type($type)) { return false; } + if (!Core::is_library_item($type)) + return false; - $object_id = Dba::escape($object_id); - $tag_id = Dba::escape($tag_id); - $user = Dba::escape($user); - $type = Dba::escape($type); - - $sql = "SELECT * FROM `tag_map` WHERE `tag_id`='$tag_id' AND `user`='$user' AND `object_id`='$object_id' AND `object_type`='$type'"; - $db_results = Dba::read($sql); + $sql = "SELECT * FROM `tag_map` LEFT JOIN `tag` ON `tag`.`id` = `tag_map`.`tag_id` LEFT JOIN `tag_merge` ON `tag`.`id`=`tag_merge`.`tag_id` " . + "WHERE (`tag_map`.`tag_id` = ? OR `tag_map`.`tag_id` = `tag_merge`.`merged_to`) AND `tag_map`.`user` = ? AND `tag_map`.`object_id` = ? AND `tag_map`.`object_type` = ?"; + $db_results = Dba::read($sql, array($tag_id, $user, $object_id, $type)); $results = Dba::fetch_assoc($db_results); @@ -318,8 +407,8 @@ class Tag extends database_object */ public static function get_top_tags($type, $object_id, $limit = 10) { - //debug_event('tag.class', 'Getting tags for type {'.$type.'} object_id {'.$object_id.'}...', '5'); - if (!self::validate_type($type)) { return false; } + if (!Core::is_library_item($type)) + return array(); $object_id = intval($object_id); @@ -348,16 +437,15 @@ class Tag extends database_object */ public static function get_object_tags($type, $id) { - if (!self::validate_type($type)) { return array(); } - - $id = Dba::escape($id); + if (!Core::is_library_item($type)) + return false; $sql = "SELECT `tag_map`.`id`, `tag`.`name`, `tag_map`.`user` FROM `tag` " . "LEFT JOIN `tag_map` ON `tag_map`.`tag_id`=`tag`.`id` " . - "WHERE `tag_map`.`object_type`='$type' AND `tag_map`.`object_id`='$id'"; + "WHERE `tag_map`.`object_type` = ? AND `tag_map`.`object_id` = ?"; $results = array(); - $db_results = Dba::read($sql); + $db_results = Dba::read($sql, array($type, $id)); while ($row = Dba::fetch_assoc($db_results)) { $results[] = $row; @@ -372,7 +460,8 @@ class Tag extends database_object */ public static function get_tag_objects($type,$tag_id,$count='',$offset='') { - if (!self::validate_type($type)) { return array(); } + if (!Core::is_library_item($type)) + return false; $limit_sql = ""; if ($count) { @@ -402,7 +491,7 @@ class Tag extends database_object * This is a non-object non type dependent function that just returns tags * we've got, it can take filters (this is used by the tag cloud) */ - public static function get_tags($limit = 0) + public static function get_tags($type = '', $limit = 0, $order = 'count') { //debug_event('tag.class.php', 'Get tags list called...', '5'); if (parent::is_cached('tags_list', 'no_name')) { @@ -412,10 +501,18 @@ class Tag extends database_object $results = array(); - $sql = "SELECT `tag_map`.`tag_id`, `tag`.`name`, COUNT(`tag_map`.`object_id`) AS `count` " . + $sql = "SELECT `tag_map`.`tag_id`, `tag`.`name`, `tag`.`is_hidden`, COUNT(`tag_map`.`object_id`) AS `count` " . "FROM `tag_map` " . "LEFT JOIN `tag` ON `tag`.`id`=`tag_map`.`tag_id` " . - "GROUP BY `tag`.`name` ORDER BY `count` DESC "; + "WHERE `tag`.`is_hidden` = false "; + if (!empty($type)) { + $sql .= "AND `tag_map`.`object_type` = '" . scrub_in($type) . "' "; + } + $order = "`" . $order . "`"; + if ($order == 'count') { + $order .= " DESC"; + } + $sql .="GROUP BY `tag`.`name` ORDER BY " . $order; if ($limit > 0) { $sql .= " LIMIT $limit"; @@ -424,7 +521,7 @@ class Tag extends database_object $db_results = Dba::read($sql); while ($row = Dba::fetch_assoc($db_results)) { - $results[$row['tag_id']] = array('id'=>$row['tag_id'], 'name'=>$row['name'], 'count'=>$row['count']); + $results[$row['tag_id']] = array('id'=>$row['tag_id'], 'name'=>$row['name'], 'is_hidden'=>$row['is_hidden'], 'count'=>$row['count']); } parent::add_to_cache('tags_list', 'no_name', $results); @@ -434,11 +531,11 @@ class Tag extends database_object /** * get_display - * This returns a human formated version of the tags that we are given + * This returns a csv formated version of the tags that we are given * it also takes a type so that it knows how to return it, this is used * by the formating functions of the different objects */ - public static function get_display($tags) + public static function get_display($tags, $link=false, $filter_type='') { //debug_event('tag.class.php', 'Get display tags called...', '5'); if (!is_array($tags)) { return ''; } @@ -451,7 +548,14 @@ class Tag extends database_object foreach ($value as $vid=>$v) { debug_event('tag.class.php', $vid.' = {'.$v.'}', '5'); }*/ - $results .= $value['name'] . ', '; + if ($link) { + $results .= ''; + } + $results .= $value['name']; + if ($link) { + $results .= ''; + } + $results .= ', '; } $results = rtrim($results, ', '); @@ -464,7 +568,7 @@ class Tag extends database_object * update_tag_list * Update the tags list based on commated list (ex. tag1,tag2,tag3,..) */ - public static function update_tag_list($tags_comma, $type, $object_id) + public static function update_tag_list($tags_comma, $type, $object_id, $overwrite) { debug_event('tag.class', 'Updating tags for values {'.$tags_comma.'} type {'.$type.'} object_id {'.$object_id.'}', '5'); @@ -488,7 +592,7 @@ class Tag extends database_object if ($found) { debug_event('tag.class', 'Already found. Do nothing.', '5'); unset($editedTags[$tk]); - } else { + } else if ($overwrite) { debug_event('tag.class', 'Not found in the new list. Delete it.', '5'); $ctag->remove_map($type, $object_id); } @@ -498,13 +602,40 @@ class Tag extends database_object // Look if we need to add some new tags foreach ($editedTags as $tk => $tv) { - debug_event('tag.class', 'Adding new tag {'.$tv.'}', '5'); if ($tv != '') { + debug_event('tag.class', 'Adding new tag {'.$tv.'}', '5'); Tag::add($type, $object_id, $tv, false); } } } // update_tag_list + /** + * clean_to_existing + * Clean tag list to existing tag list only + * @param array|string $tags + * @return array|string + */ + public static function clean_to_existing($tags) + { + if (is_array($tags)) { + $ar = $tags; + } else { + $ar = explode(",", $tags); + } + + $ret = array(); + foreach ($ar as $tag) { + $tag = trim($tag); + if (!empty($tag)) { + if (Tag::tag_exists($tag)) { + $ret[] = $tag; + } + } + } + + return (is_array($tags) ? $ret : implode(",", $ret)); + } + /** * count * This returns the count for the all objects associated with this tag @@ -534,30 +665,103 @@ class Tag extends database_object * remove_map * This will only remove tag maps for the current user */ - public function remove_map($type,$object_id) + public function remove_map($type, $object_id) { - if (!self::validate_type($type)) { return false; } + if (!Core::is_library_item($type)) + return false; - $sql = "DELETE FROM `tag_map` WHERE `tag_id` = ? AND `object_type` = ? AND `object_id` = ? AND `user` = ?"; - Dba::write($sql, array($this->id, $type, $object_id, $GLOBALS['user']->id)); + // TODO: Review the tag edition per user. + + $sql = "DELETE FROM `tag_map` WHERE `tag_id` = ? AND `object_type` = ? AND `object_id` = ? "; //AND `user` = ?"; + Dba::write($sql, array($this->id, $type, $object_id));//, $GLOBALS['user']->id)); return true; } // remove_map - /** - * validate_type - * This validates the type of the object the user wants to tag, we limit this to types - * we currently support - */ - public static function validate_type($type) + public function format($details = true) { - $valid_array = array('song','artist','album','video','playlist','live_stream','channel','broadcast'); - if (in_array($type,$valid_array)) { return $type; } + } - return false; + public function get_keywords() + { + $keywords = array(); + $keywords['tag'] = array('important' => true, + 'label' => T_('Tag'), + 'value' => $this->name); - } // validate_type + return $keywords; + } + + public function get_fullname() + { + return $this->name; + } + + public function get_parent() + { + return null; + } + + public function get_childrens() + { + return array(); + } + + public function search_childrens($name) + { + return array(); + } + + public function get_medias($filter_type = null) + { + $medias = array(); + if ($filter_type) { + $ids = Tag::get_tag_objects($filter_type, $this->id); + if ($ids) { + foreach ($ids as $id) { + $medias[] = array( + 'object_type' => $filter_type, + 'object_id' => $id + ); + } + } + } + return $medias; + } + + /** + * get_catalogs + * + * Get all catalog ids related to this item. + * @return int[] + */ + public function get_catalogs() + { + return array(); + } + + public function get_user_owner() + { + return null; + } + + public function get_default_art_kind() + { + return 'default'; + } + + public function get_description() + { + return null; + } + + public function display_art($thumb = 2) + { + if (Art::has_db($this->id, 'tag')) { + Art::display('tag', $this->id, $this->get_fullname(), $thumb, $this->link); + } + } } // end of Tag class diff --git a/sources/lib/class/tmp_playlist.class.php b/sources/lib/class/tmp_playlist.class.php index d0127fa..85896bc 100644 --- a/sources/lib/class/tmp_playlist.class.php +++ b/sources/lib/class/tmp_playlist.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -137,22 +137,22 @@ class Tmp_Playlist extends database_object */ public function get_items() { - $id = Dba::escape($this->id); - /* Select all objects from this playlist */ $sql = "SELECT `object_type`, `id`, `object_id` " . "FROM `tmp_playlist_data` " . - "WHERE `tmp_playlist`='$id' ORDER BY `id` ASC"; - $db_results = Dba::read($sql); + "WHERE `tmp_playlist` = ? ORDER BY `id` ASC"; + $db_results = Dba::read($sql, array($this->id)); /* Define the array */ $items = array(); + $i = 1; while ($results = Dba::fetch_assoc($db_results)) { - $key = $results['id']; - $items[$key] = array( + $items[] = array( 'object_type' => $results['object_type'], - 'object_id' => $results['object_id'] + 'object_id' => $results['object_id'], + 'track_id' => $results['id'], + 'track' => $i++, ); } @@ -271,7 +271,7 @@ class Tmp_Playlist extends database_object { self::prune_playlists(); self::prune_tracks(); - Dba::write("DELETE FROM `tmp_playlist_data` USING `tmp_playlist_data` LEFT JOIN `song` ON `tmp_playlist_data`.`object_id` = `song`.`id` WHERE `song`.`id` IS NULL"); + //Dba::write("DELETE FROM `tmp_playlist_data` USING `tmp_playlist_data` LEFT JOIN `song` ON `tmp_playlist_data`.`object_id` = `song`.`id` WHERE `song`.`id` IS NULL"); } /** @@ -324,6 +324,13 @@ class Tmp_Playlist extends database_object } // add_object + public function add_medias($medias) + { + foreach ($medias as $media) { + $this->add_object($media['object_id'], $media['object_type']); + } + } + /** * vote_active * This checks to see if this playlist is a voting playlist diff --git a/sources/lib/class/tvshow.class.php b/sources/lib/class/tvshow.class.php new file mode 100644 index 0000000..9cc5dfa --- /dev/null +++ b/sources/lib/class/tvshow.class.php @@ -0,0 +1,440 @@ +get_info($id); + + foreach ($info as $key=>$value) { + $this->$key = $value; + } // foreach info + + return true; + + } //constructor + + /** + * gc + * + * This cleans out unused tv shows + */ + public static function gc() + { + $sql = "DELETE FROM `tvshow` USING `tvshow` LEFT JOIN `tvshow_season` ON `tvshow_season`.`tvshow` = `tvshow`.`id` " . + "WHERE `tvshow_season`.`id` IS NULL"; + Dba::write($sql); + } + + /** + * get_from_name + * This gets a tv show object based on the tv show name + */ + public static function get_from_name($name) + { + $sql = "SELECT `id` FROM `tvshow` WHERE `name` = ?'"; + $db_results = Dba::read($sql, array($name)); + + $row = Dba::fetch_assoc($db_results); + + $object = new TVShow($row['id']); + return $object; + + } // get_from_name + + /** + * get_seasons + * gets the tv show seasons + * of + */ + public function get_seasons() + { + $sql = "SELECT `id` FROM `tvshow_season` WHERE `tvshow` = ? ORDER BY `season_number`"; + $db_results = Dba::read($sql, array($this->id)); + $results = array(); + while ($r = Dba::fetch_assoc($db_results)) { + $results[] = $r['id']; + } + + return $results; + + } // get_seasons + + /** + * get_songs + * gets all episodes for this tv show + */ + public function get_episodes() + { + $sql = "SELECT `tvshow_episode`.`id` FROM `tvshow_episode` "; + if (AmpConfig::get('catalog_disable')) { + $sql .= "LEFT JOIN `video` ON `video`.`id` = `tvshow_episode`.`id` "; + $sql .= "LEFT JOIN `catalog` ON `catalog`.`id` = `video`.`catalog` "; + } + $sql .= "LEFT JOIN `tvshow_season` ON `tvshow_season`.`id` = `tvshow_episode`.`season` "; + $sql .= "WHERE `tvshow_season`.`tvshow`='" . Dba::escape($this->id) . "' "; + if (AmpConfig::get('catalog_disable')) { + $sql .= "AND `catalog`.`enabled` = '1' "; + } + $sql .= "ORDER BY `tvshow_season`.`season_number`, `tvshow_episode`.`episode_number`"; + $db_results = Dba::read($sql); + + $results = array(); + while ($r = Dba::fetch_assoc($db_results)) { + $results[] = $r['id']; + } + + return $results; + + } // get_episodes + + /** + * _get_extra info + * This returns the extra information for the tv show, this means totals etc + */ + private function _get_extra_info() + { + // Try to find it in the cache and save ourselves the trouble + if (parent::is_cached('tvshow_extra', $this->id) ) { + $row = parent::get_from_cache('tvshow_extra', $this->id); + } else { + $sql = "SELECT COUNT(`tvshow_episode`.`id`) AS `episode_count`, `video`.`catalog` as `catalog_id` FROM `tvshow_season` " . + "LEFT JOIN `tvshow_episode` ON `tvshow_episode`.`season` = `tvshow_season`.`id` " . + "LEFT JOIN `video` ON `video`.`id` = `tvshow_episode`.`id` " . + "WHERE `tvshow_season`.`tvshow` = ?"; + $db_results = Dba::read($sql, array($this->id)); + $row = Dba::fetch_assoc($db_results); + + $sql = "SELECT COUNT(`tvshow_season`.`id`) AS `season_count` FROM `tvshow_season` " . + "WHERE `tvshow_season`.`tvshow` = ?"; + $db_results = Dba::read($sql, array($this->id)); + $row2 = Dba::fetch_assoc($db_results); + $row['season_count'] = $row2['season_count']; + + parent::add_to_cache('tvshow_extra',$this->id,$row); + } + + /* Set Object Vars */ + $this->episodes = $row['episode_count']; + $this->seasons = $row['season_count']; + $this->catalog_id = $row['catalog_id']; + + return $row; + + } // _get_extra_info + + /** + * format + * this function takes the object and reformats some values + */ + public function format($details = true) + { + $this->f_name = trim($this->prefix . " " . $this->name); + $this->link = AmpConfig::get('web_path') . '/tvshows.php?action=show&tvshow=' . $this->id; + $this->f_link = '' . $this->f_name . ''; + + if ($details) { + $this->_get_extra_info(); + $this->tags = Tag::get_top_tags('tvshow', $this->id); + $this->f_tags = Tag::get_display($this->tags, true, 'tvshow'); + } + + return true; + } + + public function get_keywords() + { + $keywords = array(); + $keywords['tvshow'] = array('important' => true, + 'label' => T_('TV Show'), + 'value' => $this->f_name); + $keywords['type'] = array('important' => false, + 'label' => null, + 'value' => 'tvshow' + ); + + return $keywords; + } + + public function get_fullname() + { + return $this->f_name; + } + + public function get_parent() + { + return null; + } + + public function get_childrens() + { + return array('tvshow_season' => $this->get_seasons()); + } + + public function search_childrens($name) + { + return array(); + } + + public function get_medias($filter_type = null) + { + $medias = array(); + if (!$filter_type || $filter_type == 'video') { + $episodes = $this->get_episodes(); + foreach ($episodes as $episode_id) { + $medias[] = array( + 'object_type' => 'video', + 'object_id' => $episode_id + ); + } + } + return $medias; + } + + /** + * get_catalogs + * + * Get all catalog ids related to this item. + * @return int[] + */ + public function get_catalogs() + { + return array($this->catalog_id); + } + + public function get_user_owner() + { + return null; + } + + public function get_default_art_kind() + { + return 'default'; + } + + public function get_description() + { + return $this->summary; + } + + public function display_art($thumb = 2) + { + if (Art::has_db($this->id, 'tvshow')) { + Art::display('tvshow', $this->id, $this->get_fullname(), $thumb, $this->link); + } + } + + /** + * check + * + * Checks for an existing tv show; if none exists, insert one. + */ + public static function check($name, $year, $readonly = false) + { + // null because we don't have any unique id like mbid for now + if (isset(self::$_mapcache[$name]['null'])) { + return self::$_mapcache[$name]['null']; + } + + $id = 0; + $exists = false; + + $trimmed = Catalog::trim_prefix(trim($name)); + $name = $trimmed['string']; + $prefix = $trimmed['prefix']; + + if (!$exists) { + $sql = 'SELECT `id` FROM `tvshow` WHERE `name` LIKE ? AND `year` = ?'; + $db_results = Dba::read($sql, array($name, $year)); + + $id_array = array(); + while ($row = Dba::fetch_assoc($db_results)) { + $key = 'null'; + $id_array[$key] = $row['id']; + } + + if (count($id_array)) { + $id = array_shift($id_array); + $exists = true; + } + } + + if ($exists) { + self::$_mapcache[$name]['null'] = $id; + return $id; + } + + if ($readonly) { + return null; + } + + $sql = 'INSERT INTO `tvshow` (`name`, `prefix`, `year`) ' . + 'VALUES(?, ?, ?)'; + + $db_results = Dba::write($sql, array($name, $prefix, $year)); + if (!$db_results) { + return null; + } + $id = Dba::insert_id(); + + self::$_mapcache[$name]['null'] = $id; + return $id; + + } + + /** + * update + * This takes a key'd array of data and updates the current tv show + */ + public function update(array $data) + { + // Save our current ID + $current_id = $this->id; + $name = isset($data['name']) ? $data['name'] : $this->name; + $year = isset($data['year']) ? $data['year'] : $this->year; + $summary = isset($data['summary']) ? $data['summary'] : $this->summary; + + // Check if name is different than current name + if ($this->name != $name || $this->year != $year) { + $tvshow_id = self::check($name, $year, true); + + // If it's changed we need to update + if ($tvshow_id != $this->id && $tvshow_id != null) { + $seasons = $this->get_seasons(); + foreach ($seasons as $season_id) { + Season::update_tvshow($tvshow_id, $season_id); + } + $current_id = $tvshow_id; + Stats::migrate('tvshow', $this->id, $tvshow_id); + Art::migrate('tvshow', $this->id, $tvshow_id); + self::gc(); + } // end if it changed + } + + $trimmed = Catalog::trim_prefix(trim($name)); + $name = $trimmed['string']; + $prefix = $trimmed['prefix']; + + $sql = 'UPDATE `tvshow` SET `name` = ?, `prefix` = ?, `year` = ?, `summary` = ? WHERE `id` = ?'; + Dba::write($sql, array($name, $prefix, $year, $summary, $current_id)); + + $this->name = $name; + $this->prefix = $prefix; + $this->year = $year; + $this->summary = $summary; + + $override_childs = false; + if ($data['overwrite_childs'] == 'checked') { + $override_childs = true; + } + + $add_to_childs = false; + if ($data['add_to_childs'] == 'checked') { + $add_to_childs = true; + } + + if (isset($data['edit_tags'])) { + $this->update_tags($data['edit_tags'], $override_childs, $add_to_childs, $current_id, true); + } + + return $current_id; + + } // update + + /** + * update_tags + * + * Update tags of tv shows + */ + public function update_tags($tags_comma, $override_childs, $add_to_childs, $current_id = null, $force_update = false) + { + if ($current_id == null) { + $current_id = $this->id; + } + + Tag::update_tag_list($tags_comma, 'tvshow', $current_id, $force_update ? true : $override_childs); + + if ($override_childs || $add_to_childs) { + $episodes = $this->get_episodes(); + foreach ($episodes as $ep_id) { + Tag::update_tag_list($tags_comma, 'episode', $ep_id, $override_childs); + } + } + } + + public function remove_from_disk() + { + $deleted = true; + $season_ids = $this->get_seasons(); + foreach ($season_ids as $id) { + $season = new TVShow_Season($id); + $deleted = $season->remove_from_disk(); + if (!$deleted) { + debug_event('tvshow', 'Error when deleting the season `' . $id .'`.', 1); + break; + } + } + + if ($deleted) { + $sql = "DELETE FROM `tvshow` WHERE `id` = ?"; + $deleted = Dba::write($sql, array($this->id)); + if ($deleted) { + Art::gc('tvshow', $this->id); + Userflag::gc('tvshow', $this->id); + Rating::gc('tvshow', $this->id); + Shoutbox::gc('tvshow', $this->id); + } + } + + return $deleted; + } + +} // end of tvshow class diff --git a/sources/lib/class/tvshow_episode.class.php b/sources/lib/class/tvshow_episode.class.php new file mode 100644 index 0000000..b89ec76 --- /dev/null +++ b/sources/lib/class/tvshow_episode.class.php @@ -0,0 +1,255 @@ +get_info($id); + foreach ($info as $key=>$value) { + $this->$key = $value; + } + + return true; + + } + + /** + * gc + * + * This cleans out unused tv shows episodes + */ + public static function gc() + { + $sql = "DELETE FROM `tvshow_episode` USING `tvshow_episode` LEFT JOIN `video` ON `video`.`id` = `tvshow_episode`.`id` WHERE `video`.`id` IS NULL"; + Dba::write($sql); + } + + /** + * insert + * Insert a new tv show episode and related entities. + */ + public static function insert(array $data, $gtypes = array(), $options = array()) + { + if (empty($data['tvshow'])) { + $data['tvshow'] = T_('Unknown'); + } + $tags = $data['genre']; + + $tvshow = TVShow::check($data['tvshow'], $data['year']); + if ($options['gather_art'] && $tvshow && $data['tvshow_art'] && !Art::has_db($tvshow, 'tvshow')) { + $art = new Art($tvshow, 'tvshow'); + $art->insert_url($data['tvshow_art']); + } + $tvshow_season = TVShow_Season::check($tvshow, $data['tvshow_season']); + if ($options['gather_art'] && $tvshow_season && $data['tvshow_season_art'] && !Art::has_db($tvshow_season, 'tvshow_season')) { + $art = new Art($tvshow_season, 'tvshow_season'); + $art->insert_url($data['tvshow_season_art']); + } + + if (is_array($tags)) { + foreach ($tags as $tag) { + $tag = trim($tag); + if (!empty($tag)) { + Tag::add('tvshow_season', $tvshow_season, $tag, false); + Tag::add('tvshow', $tvshow, $tag, false); + } + } + } + + $sdata = $data; + // Replace relation name with db ids + $sdata['tvshow'] = $tvshow; + $sdata['tvshow_season'] = $tvshow_season; + return self::create($sdata); + } + + /** + * create + * This takes a key'd array of data as input and inserts a new tv show episode entry, it returns the record id + */ + public static function create($data) + { + $sql = "INSERT INTO `tvshow_episode` (`id`, `original_name`, `season`, `episode_number`, `summary`) " . + "VALUES (?, ?, ?, ?, ?)"; + Dba::write($sql, array($data['id'], $data['original_name'], $data['tvshow_season'], $data['tvshow_episode'], $data['summary'])); + + return $data['id']; + + } + + /** + * update + * This takes a key'd array of data as input and updates a tv show episode entry + */ + public function update(array $data) + { + parent::update($data); + + $original_name = isset($data['original_name']) ? $data['original_name'] : $this->original_name; + $tvshow_season = isset($data['tvshow_season']) ? $data['tvshow_season'] : $this->season; + $tvshow_episode = isset($data['tvshow_episode']) ? $data['tvshow_episode'] : $this->episode_number; + $summary = isset($data['summary']) ? $data['summary'] : $this->summary; + + $sql = "UPDATE `tvshow_episode` SET `original_name` = ?, `season` = ?, `episode_number` = ?, `summary` = ? WHERE `id` = ?"; + Dba::write($sql, array($original_name, $tvshow_season, $tvshow_episode, $summary, $this->id)); + + $this->original_name = $original_name; + $this->season = $tvshow_season; + $this->episode_number = $tvshow_episode; + $this->summary = $summary; + + return $this->id; + + } + + /** + * format + * this function takes the object and reformats some values + */ + public function format($details = true) + { + parent::format($details); + + $season = new TVShow_Season($this->season); + $season->format($details); + + $this->f_title = ($this->original_name ?: $this->f_title); + $this->f_link = '' . $this->f_title . ''; + $this->f_season = $season->f_name; + $this->f_season_link = $season->f_link; + $this->f_tvshow = $season->f_tvshow; + $this->f_tvshow_link = $season->f_tvshow_link; + + $this->f_file = $this->f_tvshow; + if ($this->episode_number) { + $this->f_file .= ' - S'. sprintf('%02d', $season->season_number) . 'E'. sprintf('%02d', $this->episode_number); + } + $this->f_file .= ' - ' . $this->f_title; + $this->f_full_title = $this->f_file; + + return true; + } + + /** + * Get item keywords for metadata searches. + * @return array + */ + public function get_keywords() + { + $keywords = parent::get_keywords(); + $keywords['tvshow'] = array('important' => true, + 'label' => T_('TV Show'), + 'value' => $this->f_tvshow); + $keywords['tvshow_season'] = array('important' => false, + 'label' => T_('Season'), + 'value' => $this->f_season); + if ($this->episode_number) { + $keywords['tvshow_episode'] = array('important' => false, + 'label' => T_('Episode'), + 'value' => $this->episode_number); + } + $keywords['type'] = array('important' => false, + 'label' => null, + 'value' => 'tvshow' + ); + + return $keywords; + } + + public function get_parent() + { + return array('object_type' => 'tvshow_season', 'object_id' => $this->season); + } + + public function get_release_item_art() + { + return array('object_type' => 'tvshow_season', + 'object_id' => $this->season + ); + } + + public function get_description() + { + if (!empty($this->summary)) + return $this->summary; + + $season = new TVShow_Season($this->season); + return $season->get_description(); + } + + public function display_art($thumb = 2) + { + $id = null; + $type = null; + + if (Art::has_db($this->id, 'video')) { + $id = $this->id; + $type = 'video'; + } else if (Art::has_db($this->season, 'tvshow_season')) { + $id = $this->season; + $type = 'tvshow_season'; + } else { + $season = new TVShow_Season($this->season); + if (Art::has_db($season->tvshow, 'tvshow')) { + $id = $season->tvshow; + $type = 'tvshow'; + } + } + + if ($id !== null && $type !== null) { + Art::display($type, $id, $this->get_fullname(), $thumb, $this->link); + } + } + + /** + * Remove the video from disk. + */ + public function remove_from_disk() + { + $deleted = parent::remove_from_disk(); + if ($deleted) { + $sql = "DELETE FROM `tvshow_episode` WHERE `id` = ?"; + $deleted = Dba::write($sql, array($this->id)); + } + + return $deleted; + } +} diff --git a/sources/lib/class/tvshow_season.class.php b/sources/lib/class/tvshow_season.class.php new file mode 100644 index 0000000..cd6e41a --- /dev/null +++ b/sources/lib/class/tvshow_season.class.php @@ -0,0 +1,349 @@ +get_info($id); + + foreach ($info as $key=>$value) { + $this->$key = $value; + } // foreach info + + return true; + + } //constructor + + /** + * gc + * + * This cleans out unused tv shows seasons + */ + public static function gc() + { + $sql = "DELETE FROM `tvshow_season` USING `tvshow_season` LEFT JOIN `tvshow_episode` ON `tvshow_episode`.`season` = `tvshow_season`.`id` " . + "WHERE `tvshow_episode`.`id` IS NULL"; + Dba::write($sql); + } + + /** + * get_songs + * gets all episodes for this tv show season + */ + public function get_episodes() + { + $sql = "SELECT `tvshow_episode`.`id` FROM `tvshow_episode` "; + if (AmpConfig::get('catalog_disable')) { + $sql .= "LEFT JOIN `video` ON `video`.`id` = `tvshow_episode`.`id` "; + $sql .= "LEFT JOIN `catalog` ON `catalog`.`id` = `video`.`catalog` "; + } + $sql .= "WHERE `tvshow_episode`.`season`='" . Dba::escape($this->id) . "' "; + if (AmpConfig::get('catalog_disable')) { + $sql .= "AND `catalog`.`enabled` = '1' "; + } + $sql .= "ORDER BY `tvshow_episode`.`episode_number`"; + $db_results = Dba::read($sql); + + $results = array(); + while ($r = Dba::fetch_assoc($db_results)) { + $results[] = $r['id']; + } + + return $results; + + } // get_episodes + + /** + * _get_extra info + * This returns the extra information for the tv show season, this means totals etc + */ + private function _get_extra_info() + { + // Try to find it in the cache and save ourselves the trouble + if (parent::is_cached('tvshow_extra', $this->id) ) { + $row = parent::get_from_cache('tvshow_extra', $this->id); + } else { + $sql = "SELECT COUNT(`tvshow_episode`.`id`) AS `episode_count`, `video`.`catalog` as `catalog_id` FROM `tvshow_episode` " . + "LEFT JOIN `video` ON `video`.`id` = `tvshow_episode`.`id` " . + "WHERE `tvshow_episode`.`season` = ?"; + + $db_results = Dba::read($sql, array($this->id)); + $row = Dba::fetch_assoc($db_results); + parent::add_to_cache('tvshow_extra',$this->id,$row); + } + + /* Set Object Vars */ + $this->episodes = $row['episode_count']; + $this->catalog_id = $row['catalog_id']; + + return $row; + + } // _get_extra_info + + /** + * format + * this function takes the object and reformats some values + */ + public function format($details = true) + { + $this->f_name = T_('Season') . ' ' . $this->season_number; + + $tvshow = new TVShow($this->tvshow); + $tvshow->format($details); + $this->f_tvshow = $tvshow->f_name; + $this->f_tvshow_link = $tvshow->f_link; + + $this->link = AmpConfig::get('web_path') . '/tvshow_seasons.php?action=show&season=' . $this->id; + $this->f_link = '' . $this->f_name . ''; + + if ($details) { + $this->_get_extra_info(); + } + + return true; + } + + public function get_keywords() + { + $keywords = array(); + $keywords['tvshow'] = array('important' => true, + 'label' => T_('TV Show'), + 'value' => $this->f_tvshow); + $keywords['tvshow_season'] = array('important' => false, + 'label' => T_('Season'), + 'value' => $this->season_number); + $keywords['type'] = array('important' => false, + 'label' => null, + 'value' => 'tvshow' + ); + + return $keywords; + } + + public function get_fullname() + { + return $this->f_name; + } + + public function get_parent() + { + return array('object_type' => 'tvshow', 'object_id' => $this->tvshow); + } + + public function get_childrens() + { + return array('tvshow_episode' => $this->get_episodes()); + } + + public function search_childrens($name) + { + return array(); + } + + public function get_medias($filter_type = null) + { + $medias = array(); + if (!$filter_type || $filter_type == 'video') { + $episodes = $this->get_episodes(); + foreach ($episodes as $episode_id) { + $medias[] = array( + 'object_type' => 'video', + 'object_id' => $episode_id + ); + } + } + return $medias; + } + + /** + * get_catalogs + * + * Get all catalog ids related to this item. + * @return int[] + */ + public function get_catalogs() + { + return array($this->catalog_id); + } + + public function get_user_owner() + { + return null; + } + + public function get_default_art_kind() + { + return 'default'; + } + + public function get_description() + { + // No season description for now, always return tvshow description + $tvshow = new TVShow($this->tvshow); + return $tvshow->get_description(); + } + + public function display_art($thumb = 2) + { + $id = null; + $type = null; + + if (Art::has_db($this->id, 'tvshow_season')) { + $id = $this->id; + $type = 'tvshow_season'; + } else if (Art::has_db($this->tvshow, 'tvshow')) { + $id = $this->tvshow; + $type = 'tvshow'; + } + + if ($id !== null && $type !== null) { + Art::display($type, $id, $this->get_fullname(), $thumb, $this->link); + } + } + + /** + * check + * + * Checks for an existing tv show season; if none exists, insert one. + */ + public static function check($tvshow, $season_number, $readonly = false) + { + $name = $tvshow . '_' . $season_number; + // null because we don't have any unique id like mbid for now + if (isset(self::$_mapcache[$name]['null'])) { + return self::$_mapcache[$name]['null']; + } + + $id = 0; + $exists = false; + + if (!$exists) { + $sql = 'SELECT `id` FROM `tvshow_season` WHERE `tvshow` = ? AND `season_number` = ?'; + $db_results = Dba::read($sql, array($tvshow, $season_number)); + + $id_array = array(); + while ($row = Dba::fetch_assoc($db_results)) { + $key = 'null'; + $id_array[$key] = $row['id']; + } + + if (count($id_array)) { + $id = array_shift($id_array); + $exists = true; + } + } + + if ($exists) { + self::$_mapcache[$name]['null'] = $id; + return $id; + } + + if ($readonly) { + return null; + } + + $sql = 'INSERT INTO `tvshow_season` (`tvshow`, `season_number`) ' . + 'VALUES(?, ?)'; + + $db_results = Dba::write($sql, array($tvshow, $season_number)); + if (!$db_results) { + return null; + } + $id = Dba::insert_id(); + + self::$_mapcache[$name]['null'] = $id; + return $id; + + } + + /** + * update + * This takes a key'd array of data and updates the current tv show + */ + public function update(array $data) + { + $sql = 'UPDATE `tvshow_season` SET `season_number` = ?, `tvshow` = ? WHERE `id` = ?'; + Dba::write($sql, array($data['season_number'], $data['tvshow'], $this->id)); + + return $this->id; + } // update + + public function remove_from_disk() + { + $deleted = true; + $video_ids = $this->get_episodes(); + foreach ($video_ids as $id) { + $video = Video::create_from_id($id); + $deleted = $video->remove_from_disk(); + if (!$deleted) { + debug_event('tvshow_season', 'Error when deleting the video `' . $id .'`.', 1); + break; + } + } + + if ($deleted) { + $sql = "DELETE FROM `tvshow_season` WHERE `id` = ?"; + $deleted = Dba::write($sql, array($this->id)); + if ($deleted) { + Art::gc('tvshow_season', $this->id); + Userflag::gc('tvshow_season', $this->id); + Rating::gc('tvshow_season', $this->id); + Shoutbox::gc('tvshow_season', $this->id); + } + } + + return $deleted; + } + + public static function update_tvshow($tvshow_id, $season_id) + { + $sql = "UPDATE `tvshow_season` SET `tvshow` = ? WHERE `id` = ?"; + return Dba::write($sql, array($tvshow_id, $season_id)); + } + +} // end of tvshow_season class diff --git a/sources/lib/class/ui.class.php b/sources/lib/class/ui.class.php index a58acec..563db1a 100644 --- a/sources/lib/class/ui.class.php +++ b/sources/lib/class/ui.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -277,16 +277,6 @@ END; require_once AmpConfig::get('prefix') . '/templates/header.inc.php'; } - /** - * show_mainframes - * - * For now this just shows the mainframes template - */ - public static function show_mainframes() - { - require_once AmpConfig::get('prefix') . '/templates/mainframes.inc.php'; - } - /** * show_footer * @@ -294,6 +284,18 @@ END; */ public static function show_footer() { + if (!defined("TABLE_RENDERED")) { + show_table_render(); + } + + $plugins = Plugin::get_plugins('display_on_footer'); + foreach ($plugins as $plugin_name) { + $plugin = new Plugin($plugin_name); + if ($plugin->load($GLOBALS['user'])) { + $plugin->_plugin->display_on_footer(); + } + } + require_once AmpConfig::get('prefix') . '/templates/footer.inc.php'; if (isset($_REQUEST['profiling'])) { Dba::show_profile(); @@ -320,6 +322,16 @@ END; require AmpConfig::get('prefix') . '/templates/show_box_bottom.inc.php'; } + public static function show_custom_style() + { + if (AmpConfig::get('custom_login_logo')) { + echo ""; + } + + $favicon = AmpConfig::get('custom_favicon') ?: AmpConfig::get('web_path') . "/favicon.ico"; + echo "\n"; + } + /** * update_text * @@ -333,10 +345,30 @@ END; return; } - echo '\n"; + static $id = 1; + + if (defined('SSE_OUTPUT')) { + echo "id: " . $id . "\n"; + echo "data: displayNotification('" . json_encode($value) . "', 5000)\n\n"; + } else { + if (!empty($field)) { + echo "\n"; + } else { + echo "
" . $value . "

\n"; + } + } + ob_flush(); flush(); + $id++; + } + + public static function get_logo_url() + { + if (AmpConfig::get('custom_logo')) { + return AmpConfig::get('custom_logo'); + } else { + return AmpConfig::get('web_path') . AmpConfig::get('theme_path') . '/images/ampache.png'; + } } } diff --git a/sources/lib/class/update.class.php b/sources/lib/class/update.class.php index b84e311..688939a 100644 --- a/sources/lib/class/update.class.php +++ b/sources/lib/class/update.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -341,7 +341,7 @@ class Update $update_string = '- New table to store song previews.
'; $version[] = array('version' => '360030','description' => $update_string); - $update_string = '- Add option to fix header/sidebars position on compatible themes.
'; + $update_string = '- Add option to fix header position on compatible themes.
'; $version[] = array('version' => '360031','description' => $update_string); $update_string = '- Add check update automatically option.
'; @@ -401,6 +401,111 @@ class Update $update_string = '- Copy default .htaccess configurations.
'; $version[] = array('version' => '360051','description' => $update_string); + $update_string = '- Drop unused dynamic_playlist tables and add session id to votes.
'; + $version[] = array('version' => '370001','description' => $update_string); + + $update_string = '- Add tag persistent merge reference.
'; + $version[] = array('version' => '370002','description' => $update_string); + + $update_string = '- Add show/hide donate button preference.
'; + $version[] = array('version' => '370003','description' => $update_string); + + $update_string = '- Add license information and user\'s artist association.
'; + $version[] = array('version' => '370004','description' => $update_string); + + $update_string = '- Add new column album_artist into table song.
'; + $version[] = array('version' => '370005','description' => $update_string); + + $update_string = '- Add random and limit options to smart playlists.
'; + $version[] = array('version' => '370006','description' => $update_string); + + $update_string = '- Add DAAP backend preference.
'; + $version[] = array('version' => '370007','description' => $update_string); + + $update_string = '- Add UPnP backend preference.
'; + $version[] = array('version' => '370008','description' => $update_string); + + $update_string = '- Enhance video support with TVShows and Movies.
'; + $version[] = array('version' => '370009','description' => $update_string); + + $update_string = '- Add MusicBrainz Album Release Group identifier.
'; + $version[] = array('version' => '370010','description' => $update_string); + + $update_string = '- Add Prefix to TVShows and Movies.
'; + $version[] = array('version' => '370011','description' => $update_string); + + $update_string = '- Add metadata information to albums / songs / videos.
'; + $version[] = array('version' => '370012','description' => $update_string); + + $update_string = '- Replace iframe with ajax page load.
'; + $version[] = array('version' => '370013','description' => $update_string); + + $update_string = '- Modified release_date in video table to signed int.
'; + $version[] = array('version' => '370014','description' => $update_string); + + $update_string = '- Add session_remember table to store remember tokens.
'; + $version[] = array('version' => '370015','description' => $update_string); + + $update_string = '- Add limit of media count for direct play preference.
'; + $version[] = array('version' => '370016','description' => $update_string); + + $update_string = '- Add home display settings.
'; + $version[] = array('version' => '370017','description' => $update_string); + + $update_string = '- Enhance tag persistent merge reference.
'; + $version[] = array('version' => '370018','description' => $update_string); + + $update_string = '- Add album group order setting.
'; + $version[] = array('version' => '370019','description' => $update_string); + + $update_string = '- Add webplayer browser notification settings.
'; + $version[] = array('version' => '370020','description' => $update_string); + + $update_string = '- Add rating to playlists, tvshows and tvshows seasons.
'; + $version[] = array('version' => '370021','description' => $update_string); + + $update_string = '- Add users geolocation.
'; + $version[] = array('version' => '370022','description' => $update_string); + + $update_string = " - Add Aurora.js webplayer option.
"; + $version[] = array('version' => '370023','description' => $update_string); + + $update_string = " - Add count_type column to object_count table.
"; + $version[] = array('version' => '370024','description' => $update_string); + + $update_string = " - Add state and city fields to user table.
"; + $version[] = array('version' => '370025','description' => $update_string); + + $update_string = " - Add replay gain fields to song_data table.
"; + $version[] = array('version' => '370026','description' => $update_string); + + $update_string = " - Move column album_artist from table song to table album.
"; + $version[] = array('version' => '370027','description' => $update_string); + + $update_string = " - Add width and height in table image.
"; + $version[] = array('version' => '370028','description' => $update_string); + + $update_string = " - Set image column from image table as nullable.
"; + $version[] = array('version' => '370029','description' => $update_string); + + $update_string = " - Add an option to allow users to remove uploaded songs.
"; + $version[] = array('version' => '370030','description' => $update_string); + + $update_string = " - Add an option to customize login art, favicon and text footer.
"; + $version[] = array('version' => '370031','description' => $update_string); + + $update_string = " - Add WebDAV backend preference.
"; + $version[] = array('version' => '370032','description' => $update_string); + + $update_string = " - Add Label tables.
"; + $version[] = array('version' => '370033','description' => $update_string); + + $update_string = " - Add User messages and user follow tables.
"; + $version[] = array('version' => '370034','description' => $update_string); + + $update_string = " - Add option on user fullname to show/hide it publicly.
"; + $version[] = array('version' => '370035','description' => $update_string); + return $version; } @@ -441,7 +546,7 @@ class Update if (!defined('CLI')) { echo '

'; } echo T_('No updates needed.'); if (!defined('CLI')) { - echo '[Return]

'; + echo ' [', T_('Return to main page'), ']

'; } else { echo "\n"; } @@ -1630,16 +1735,18 @@ class Update */ public static function update_360015() { + $retval = true; + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('iframes','1','Iframes',25,'boolean','interface')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /* @@ -1649,16 +1756,18 @@ class Update */ public static function update_360016() { + $retval = true; + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('now_playing_per_user','1','Now playing filtered per user',50,'boolean','interface')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /** @@ -1687,16 +1796,18 @@ class Update */ public static function update_360018() { + $retval = true; + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('album_sort','0','Album Default Sort',25,'string','interface')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /** @@ -1706,16 +1817,18 @@ class Update */ public static function update_360019() { + $retval = true; + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('show_played_times','0','Show # played',25,'string','interface')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /** @@ -1725,6 +1838,8 @@ class Update */ public static function update_360020() { + $retval = true; + $sql = "SELECT `id`, `catalog_type`, `path`, `remote_username`, `remote_password` FROM `catalog`"; $db_results = Dba::read($sql); @@ -1736,29 +1851,29 @@ class Update while ($results = Dba::fetch_assoc($db_results)) { if ($results['catalog_type'] == 'local') { $sql = "INSERT INTO `catalog_local` (`path`, `catalog_id`) VALUES (?, ?)"; - Dba::write($sql, array($results['path'], $results['id'])); + $retval = Dba::write($sql, array($results['path'], $results['id'])) ? $retval : false; } elseif ($results['catalog_type'] == 'remote') { $sql = "INSERT INTO `catalog_remote` (`uri`, `username`, `password`, `catalog_id`) VALUES (?, ?, ?, ?)"; - Dba::write($sql, array($results['path'], $results['remote_username'], $results['remote_password'], $results['id'])); + $retval = Dba::write($sql, array($results['path'], $results['remote_username'], $results['remote_password'], $results['id'])) ? $retval : false; } } $sql = "ALTER TABLE `catalog` DROP `path`, DROP `remote_username`, DROP `remote_password`"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $sql = "ALTER TABLE `catalog` MODIFY COLUMN `catalog_type` varchar(128)"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $sql = "UPDATE `artist` SET `mbid` = null WHERE `mbid` = ''"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $sql = "UPDATE `album` SET `mbid` = null WHERE `mbid` = ''"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $sql = "UPDATE `song` SET `mbid` = null WHERE `mbid` = ''"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; - return true; + return $retval; } /** @@ -1768,19 +1883,21 @@ class Update */ public static function update_360021() { + $retval = true; + $sql = "ALTER TABLE `now_playing` ADD `insertion` INT (11) AFTER `expire`"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('song_page_title','1','Show current song in Web player page title',25,'boolean','interface')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /** @@ -1790,13 +1907,15 @@ class Update */ public static function update_360022() { + $retval = true; + $sql = "ALTER TABLE `live_stream` ADD `codec` VARCHAR(32) NULL AFTER `catalog`, DROP `frequency`, DROP `call_sign`"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $sql = "ALTER TABLE `stream_playlist` ADD `codec` VARCHAR(32) NULL AFTER `time`"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; - return true; + return $retval; } /** @@ -1806,25 +1925,27 @@ class Update */ public static function update_360023() { + $retval = true; + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . - "VALUES ('subsonic_backend','1','Use SubSonic backend',25,'boolean','system')"; - Dba::write($sql); + "VALUES ('subsonic_backend','1','Use SubSonic backend',100,'boolean','system')"; + $retval = Dba::write($sql) ? $retval: false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . - "VALUES ('plex_backend','0','Use Plex backend',25,'boolean','system')"; - Dba::write($sql); + "VALUES ('plex_backend','0','Use Plex backend',100,'boolean','system')"; + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /** @@ -1834,10 +1955,8 @@ class Update */ public static function update_360024() { - $sql = "DROP TABLE `flagged`"; - Dba::write($sql); - - return true; + $sql = "DROP TABLE IF EXISTS `flagged`"; + return Dba::write($sql); } /** @@ -1847,25 +1966,27 @@ class Update */ public static function update_360025() { + $retval = true; + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('webplayer_flash','1','Authorize Flash Web Player(s)',25,'boolean','streaming')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('webplayer_html5','1','Authorize HTML5 Web Player(s)',25,'boolean','streaming')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /** @@ -1876,9 +1997,7 @@ class Update public static function update_360026() { $sql = "ALTER TABLE `object_count` ADD `agent` VARCHAR(255) NULL AFTER `user`"; - Dba::write($sql); - - return true; + return Dba::write($sql); } /** @@ -1888,16 +2007,18 @@ class Update */ public static function update_360027() { + $retval = true; + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('allow_personal_info','1','Allow to show my personal info to other users (now playing, recently played)',25,'boolean','interface')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /** @@ -1909,35 +2030,37 @@ class Update */ public static function update_360028() { + $retval = true; + // Update previous update preference $sql = "UPDATE `preference` SET `name`='allow_personal_info_now', `description`='Personal information visibility - Now playing' WHERE `name`='allow_personal_info'"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; // Insert new recently played preference $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('allow_personal_info_recent','1','Personal information visibility - Recently played',25,'boolean','interface')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; // Insert streaming time preference $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('allow_personal_info_time','1','Personal information visibility - Recently played - Allow to show streaming date/time',25,'boolean','interface')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; // Insert streaming agent preference $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('allow_personal_info_agent','1','Personal information visibility - Recently played - Allow to show streaming agent',25,'boolean','interface')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /** @@ -1991,14 +2114,16 @@ class Update */ public static function update_360031() { + $retval = true; + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . - "VALUES ('ui_fixed','0','Fix header/sidebars position on compatible themes',25,'boolean','interface')"; - Dba::write($sql); + "VALUES ('ui_fixed','0','Fix header position on compatible themes',25,'boolean','interface')"; + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /** @@ -2008,18 +2133,20 @@ class Update */ public static function update_360032() { + $retval = true; + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('autoupdate','1','Check for Ampache updates automatically',25,'boolean','system')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; Preference::insert('autoupdate_lastcheck','AutoUpdate last check time','','25','string','internal'); Preference::insert('autoupdate_lastversion','AutoUpdate last version from last check','','25','string','internal'); Preference::insert('autoupdate_lastversion_new','AutoUpdate last version from last check is newer','','25','boolean','internal'); - return true; + return $retval; } /** @@ -2029,13 +2156,15 @@ class Update */ public static function update_360033() { + $retval = true; + $sql = "ALTER TABLE `song_data` ADD `waveform` MEDIUMBLOB NULL AFTER `language`"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $sql = "ALTER TABLE `user_shout` ADD `data` VARCHAR(256) NULL AFTER `object_type`"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; - return true; + return $retval; } /** @@ -2045,21 +2174,23 @@ class Update */ public static function update_360034() { + $retval = true; + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('webplayer_confirmclose','0','Confirmation when closing current playing window',25,'boolean','interface')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('webplayer_pausetabs','1','Auto-pause betweens tabs',25,'boolean','interface')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /** @@ -2069,14 +2200,16 @@ class Update */ public static function update_360035() { + $retval = true; + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . - "VALUES ('stream_beautiful_url','0','Use beautiful stream url',25,'boolean','streaming')"; - Dba::write($sql); + "VALUES ('stream_beautiful_url','0','Enable url rewriting',100,'boolean','streaming')"; + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /** @@ -2086,22 +2219,24 @@ class Update */ public static function update_360036() { + $retval = true; + $sql = "DELETE FROM `preference` WHERE `name` LIKE 'ellipse_threshold_%'"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $sql = "DELETE FROM `preference` WHERE `name` = 'min_object_count'"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $sql = "DELETE FROM `preference` WHERE `name` = 'bandwidth'"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $sql = "DELETE FROM `preference` WHERE `name` = 'features'"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $sql = "DELETE FROM `preference` WHERE `name` = 'tags_userlist'"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; - return true; + return $retval; } /** @@ -2111,6 +2246,8 @@ class Update */ public static function update_360037() { + $retval = true; + $sql = "CREATE TABLE `share` (" . "`id` int(11) unsigned NOT NULL AUTO_INCREMENT," . "`user` int(11) unsigned NOT NULL," . @@ -2127,23 +2264,23 @@ class Update "`public_url` varchar(255) CHARACTER SET utf8 NULL," . "`description` varchar(255) CHARACTER SET utf8 NULL," . "PRIMARY KEY (`id`)) ENGINE = MYISAM"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . - "VALUES ('share','0','Allow Share',25,'boolean','system')"; - Dba::write($sql); + "VALUES ('share','0','Allow Share',100,'boolean','system')"; + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . - "VALUES ('share_expire','7','Share links default expiration days (0=never)',25,'integer','system')"; - Dba::write($sql); + "VALUES ('share_expire','7','Share links default expiration days (0=never)',100,'integer','system')"; + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'7')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /** @@ -2153,19 +2290,21 @@ class Update */ public static function update_360038() { + $retval = true; + $sql = "ALTER TABLE `wanted` ADD `artist_mbid` varchar(1369) CHARACTER SET utf8 NULL AFTER `artist`"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $sql = "ALTER TABLE `wanted` MODIFY `artist` int(11) NULL"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $sql = "ALTER TABLE `song_preview` ADD `artist_mbid` varchar(1369) CHARACTER SET utf8 NULL AFTER `artist`"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $sql = "ALTER TABLE `song_preview` MODIFY `artist` int(11) NULL"; - Dba::write($sql); + $retval= Dba::write($sql) ? $retval : false; - return true; + return $retval; } /** @@ -2176,9 +2315,7 @@ class Update public static function update_360039() { $sql = "ALTER TABLE `user` ADD `website` varchar(255) CHARACTER SET utf8 NULL AFTER `email`"; - Dba::write($sql); - - return true; + return Dba::write($sql); } /** @@ -2225,6 +2362,8 @@ class Update */ public static function update_360042() { + $retval = true; + $sql = "CREATE TABLE `broadcast` (" . "`id` int(11) unsigned NOT NULL AUTO_INCREMENT," . "`user` int(11) unsigned NOT NULL," . @@ -2236,7 +2375,7 @@ class Update "`listeners` int(11) unsigned NOT NULL DEFAULT '0'," . "`key` varchar(32) CHARACTER SET utf8 NULL," . "PRIMARY KEY (`id`)) ENGINE = MYISAM"; - Dba::write($sql); + $retval= Dba::write($sql) ? $retval : false; $sql = "CREATE TABLE `player_control` (" . "`id` int(11) unsigned NOT NULL AUTO_INCREMENT," . @@ -2247,8 +2386,9 @@ class Update "`object_id` int(11) unsigned NOT NULL," . "`send_date` int(11) unsigned NOT NULL DEFAULT '0'," . "PRIMARY KEY (`id`)) ENGINE = MYISAM"; + $retval = Dba::write($sql) ? $retval : false; - return Dba::write($sql); + return $retval; } /** @@ -2258,14 +2398,16 @@ class Update */ public static function update_360043() { + $retval = true; + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('slideshow_time','0','Artist slideshow inactivity time',25,'integer','interface')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /** @@ -2275,11 +2417,13 @@ class Update */ public static function update_360044() { + $retval = true; + $sql = "ALTER TABLE `artist` ADD `summary` TEXT CHARACTER SET utf8 NULL," . "ADD `placeformed` varchar(64) NULL," . "ADD `yearformed` int(4) NULL," . "ADD `last_update` int(11) unsigned NOT NULL DEFAULT '0'"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $sql = "CREATE TABLE `recommendation` (" . "`id` int(11) unsigned NOT NULL AUTO_INCREMENT," . @@ -2287,7 +2431,7 @@ class Update "`object_id` int(11) unsigned NOT NULL," . "`last_update` int(11) unsigned NOT NULL DEFAULT '0'," . "PRIMARY KEY (`id`)) ENGINE = MYISAM"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $sql = "CREATE TABLE `recommendation_item` (" . "`id` int(11) unsigned NOT NULL AUTO_INCREMENT," . @@ -2297,9 +2441,9 @@ class Update "`rel` varchar(256) NULL," . "`mbid` varchar(1369) NULL," . "PRIMARY KEY (`id`)) ENGINE = MYISAM"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; - return true; + return $retval; } /** @@ -2310,9 +2454,7 @@ class Update public static function update_360045() { $sql = "ALTER TABLE `playlist` MODIFY `user` int(11) NULL"; - Dba::write($sql); - - return true; + return Dba::write($sql); } /** @@ -2322,14 +2464,16 @@ class Update */ public static function update_360046() { + $retval = true; + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('broadcast_by_default','0','Broadcast web player by default',25,'boolean','streaming')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /** @@ -2340,9 +2484,7 @@ class Update public static function update_360047() { $sql = "ALTER TABLE `user` ADD `apikey` varchar(255) CHARACTER SET utf8 NULL AFTER `website`"; - Dba::write($sql); - - return true; + return Dba::write($sql); } /** @@ -2352,21 +2494,23 @@ class Update */ public static function update_360048() { + $retval = true; + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('concerts_limit_future','0','Limit number of future events',25,'integer','interface')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('concerts_limit_past','0','Limit number of past events',25,'integer','interface')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /** @@ -2376,14 +2520,16 @@ class Update */ public static function update_360049() { + $retval = true; + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('album_group','0','Album - Group multiple disks',25,'boolean','interface')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /** @@ -2393,14 +2539,16 @@ class Update */ public static function update_360050() { + $retval = true; + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . "VALUES ('topmenu','0','Top menu',25,'boolean','interface')"; - Dba::write($sql); + $retval = Dba::write($sql) ? $retval : false; $id = Dba::insert_id(); $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; - Dba::write($sql, array($id)); + $retval = Dba::write($sql, array($id)) ? $retval : false; - return true; + return $retval; } /** @@ -2419,6 +2567,7 @@ class Update $htaccess_play_file = AmpConfig::get('prefix') . '/play/.htaccess'; $htaccess_rest_file = AmpConfig::get('prefix') . '/rest/.htaccess'; + $htaccess_channel_file = AmpConfig::get('prefix') . '/channel/.htaccess'; $ret = true; if (!is_readable($htaccess_play_file)) { @@ -2453,6 +2602,906 @@ class Update } } + if (!is_readable($htaccess_channel_file)) { + $created = false; + if (check_htaccess_channel_writable()) { + if (!install_rewrite_rules($htaccess_channel_file, AmpConfig::get('raw_web_path'), false)) { + Error::add('general', T_('File copy error.')); + } else { + $created = true; + } + } + + if (!$created) { + Error::add('general', T_('Cannot copy default .htaccess file.') . ' Please copy ' . $htaccess_channel_file . '.dist to ' . $htaccess_channel_file . '.'); + $ret = false; + } + } + return $ret; } + + /** + * update_370001 + * + * Drop unused dynamic_playlist tables and add session id to votes + */ + public static function update_370001() + { + $retval = true; + + $sql = "DROP TABLE dynamic_playlist"; + $retval = Dba::write($sql) ? $retval : false; + $sql = "DROP TABLE dynamic_playlist_data"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "ALTER TABLE `user_vote` ADD `sid` varchar(256) CHARACTER SET utf8 NULL AFTER `date`"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('demo_clear_sessions','0','Clear democratic votes of expired user sessions',25,'boolean','playlist')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + return $retval; + } + + /** + * update_370002 + * + * Add tag persistent merge reference + */ + public static function update_370002() + { + $sql = "ALTER TABLE `tag` ADD `merged_to` int(11) NULL AFTER `name`"; + return Dba::write($sql); + } + + /** + * update_370003 + * + * Add show/hide donate button preference + */ + public static function update_370003() + { + $retval = true; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('show_donate','1','Show donate button in footer',25,'boolean','interface')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + return $retval; + } + + /** + * update_370004 + * + * Add license information and user's artist association + */ + public static function update_370004() + { + $retval = true; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('upload_catalog','-1','Uploads catalog destination',75,'integer','system')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'-1')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('allow_upload','0','Allow users to upload media',75,'boolean','system')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('upload_subdir','1','Upload: create a subdirectory per user (recommended)',75,'boolean','system')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('upload_user_artist','0','Upload: consider the user sender as the track\'s artist',75,'boolean','system')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('upload_script','','Upload: run the following script after upload (current directory = upload target directory)',75,'string','system')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('upload_allow_edit','1','Upload: allow users to edit uploaded songs',75,'boolean','system')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + $sql = "ALTER TABLE `artist` ADD `user` int(11) NULL AFTER `last_update`"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "CREATE TABLE `license` (" . + "`id` int(11) unsigned NOT NULL AUTO_INCREMENT," . + "`name` varchar(80) NOT NULL," . + "`description` varchar(256) NULL," . + "`external_link` varchar(256) NOT NULL," . + "PRIMARY KEY (`id`)) ENGINE = MYISAM"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "INSERT INTO `license`(`name`, `external_link`) VALUES ('_default', '')"; + $retval = Dba::write($sql) ? $retval : false; + $sql = "INSERT INTO `license`(`name`, `external_link`) VALUES ('CC BY', 'https://creativecommons.org/licenses/by/3.0/')"; + $retval = Dba::write($sql) ? $retval : false; + $sql = "INSERT INTO `license`(`name`, `external_link`) VALUES ('CC BY NC', 'https://creativecommons.org/licenses/by-nc/3.0/')"; + $retval = Dba::write($sql) ? $retval : false; + $sql = "INSERT INTO `license`(`name`, `external_link`) VALUES ('CC BY NC ND', 'https://creativecommons.org/licenses/by-nc-nd/3.0/')"; + $retval = Dba::write($sql) ? $retval : false; + $sql = "INSERT INTO `license`(`name`, `external_link`) VALUES ('CC BY NC SA', 'https://creativecommons.org/licenses/by-nc-sa/3.0/')"; + $retval = Dba::write($sql) ? $retval : false; + $sql = "INSERT INTO `license`(`name`, `external_link`) VALUES ('CC BY ND', 'https://creativecommons.org/licenses/by-nd/3.0/')"; + $retval = Dba::write($sql) ? $retval : false; + $sql = "INSERT INTO `license`(`name`, `external_link`) VALUES ('CC BY SA', 'https://creativecommons.org/licenses/by-sa/3.0/')"; + $retval= Dba::write($sql) ? $retval : false; + $sql = "INSERT INTO `license`(`name`, `external_link`) VALUES ('Licence Art Libre', 'http://artlibre.org/licence/lal/')"; + $retval = Dba::write($sql) ? $retval : false; + $sql = "INSERT INTO `license`(`name`, `external_link`) VALUES ('Yellow OpenMusic', 'http://openmusic.linuxtag.org/yellow.html')"; + $retval = Dba::write($sql) ? $retval : false; + $sql = "INSERT INTO `license`(`name`, `external_link`) VALUES ('Green OpenMusic', 'http://openmusic.linuxtag.org/green.html')"; + $retval= Dba::write($sql) ? $retval : false; + $sql = "INSERT INTO `license`(`name`, `external_link`) VALUES ('Gnu GPL Art', 'http://gnuart.org/english/gnugpl.html')"; + $retval = Dba::write($sql) ? $retval : false; + $sql = "INSERT INTO `license`(`name`, `external_link`) VALUES ('WTFPL', 'https://en.wikipedia.org/wiki/WTFPL')"; + $retval = Dba::write($sql) ? $retval : false; + $sql = "INSERT INTO `license`(`name`, `external_link`) VALUES ('FMPL', 'http://www.fmpl.org/fmpl.html')"; + $retval = Dba::write($sql) ? $retval : false; + $sql = "INSERT INTO `license`(`name`, `external_link`) VALUES ('C Reaction', 'http://morne.free.fr/Necktar7/creaction.htm')"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "ALTER TABLE `song` ADD `user_upload` int(11) NULL AFTER `addition_time`, ADD `license` int(11) NULL AFTER `user_upload`"; + $retval = Dba::write($sql) ? $retval : false; + + return $retval; + } + + /** + * update_370005 + * + * Add new column album_artist into table album + * + */ + public static function update_370005() + { + $sql = "ALTER TABLE `song` ADD `album_artist` int(11) unsigned DEFAULT NULL AFTER `artist`"; + return Dba::write($sql); + } + + /** + * update_370006 + * + * Add random and limit options to smart playlists + * + */ + public static function update_370006() + { + $sql = "ALTER TABLE `search` ADD `random` tinyint(1) unsigned NOT NULL DEFAULT '0' AFTER `logic_operator`, ADD `limit` int(11) unsigned NOT NULL DEFAULT '0' AFTER `random`"; + return Dba::write($sql); + } + + /** + * update_370007 + * + * Add DAAP backend preference + */ + public static function update_370007() + { + $retval = true; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('daap_backend','0','Use DAAP backend',100,'boolean','system')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('daap_pass','','DAAP backend password',100,'string','system')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + $sql = "CREATE TABLE `daap_session` (" . + "`id` int(11) unsigned NOT NULL AUTO_INCREMENT," . + "`creationdate` int(11) unsigned NOT NULL," . + "PRIMARY KEY (`id`)) ENGINE = MYISAM"; + $retval = Dba::write($sql) ? $retval : false; + + return $retval; + } + + /** + * update_370008 + * + * Add UPnP backend preference + * + */ + public static function update_370008() + { + $retval = true; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('upnp_backend','0','Use UPnP backend',100,'boolean','system')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + return $retval; + } + + /** + * update_370009 + * + * Enhance video support with TVShows and Movies + */ + public static function update_370009() + { + $retval = true; + + $sql = "ALTER TABLE `video` ADD `release_date` date NULL AFTER `enabled`, " . + "ADD `played` tinyint(1) unsigned DEFAULT '1' NOT NULL AFTER `enabled`"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "CREATE TABLE `tvshow` (" . + "`id` int(11) unsigned NOT NULL AUTO_INCREMENT," . + "`name` varchar(80) NOT NULL," . + "`summary` varchar(256) NULL," . + "`year` int(11) unsigned NULL," . + "PRIMARY KEY (`id`)) ENGINE = MYISAM"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "CREATE TABLE `tvshow_season` (" . + "`id` int(11) unsigned NOT NULL AUTO_INCREMENT," . + "`season_number` int(11) unsigned NOT NULL," . + "`tvshow` int(11) unsigned NOT NULL," . + "PRIMARY KEY (`id`)) ENGINE = MYISAM"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "CREATE TABLE `tvshow_episode` (" . + "`id` int(11) unsigned NOT NULL," . + "`original_name` varchar(80) NULL," . + "`season` int(11) unsigned NOT NULL," . + "`episode_number` int(11) unsigned NOT NULL," . + "`summary` varchar(256) NULL," . + "PRIMARY KEY (`id`)) ENGINE = MYISAM"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "CREATE TABLE `movie` (" . + "`id` int(11) unsigned NOT NULL," . + "`original_name` varchar(80) NULL," . + "`summary` varchar(256) NULL," . + "`year` int(11) unsigned NULL," . + "PRIMARY KEY (`id`)) ENGINE = MYISAM"; + $retval= Dba::write($sql) ? $retval : false; + + $sql = "CREATE TABLE `personal_video` (" . + "`id` int(11) unsigned NOT NULL," . + "`location` varchar(256) NULL," . + "`summary` varchar(256) NULL," . + "PRIMARY KEY (`id`)) ENGINE = MYISAM"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "CREATE TABLE `clip` (" . + "`id` int(11) unsigned NOT NULL," . + "`artist` int(11) NULL," . + "`song` int(11) NULL," . + "PRIMARY KEY (`id`)) ENGINE = MYISAM"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('allow_video','1','Allow video features',25,'integer','system')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + $sql = "ALTER TABLE `image` ADD `kind` VARCHAR( 32 ) NULL DEFAULT 'default' AFTER `object_id`"; + $retval = Dba::write($sql) ? $retval : false; + + return $retval; + } + + /** + * update_370010 + * + * Add MusicBrainz Album Release Group identifier + */ + public static function update_370010() + { + $sql = "ALTER TABLE `album` ADD `mbid_group` varchar(36) CHARACTER SET utf8 NULL"; + return Dba::write($sql); + } + + /** + * update_370011 + * + * Add Prefix to TVShows and Movies + */ + public static function update_370011() + { + $retval = true; + + $sql = "ALTER TABLE `tvshow` ADD `prefix` varchar(32) CHARACTER SET utf8 NULL"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "ALTER TABLE `movie` ADD `prefix` varchar(32) CHARACTER SET utf8 NULL"; + $retval = Dba::write($sql) ? $retval : false; + + return $retval; + } + + /** + * update_370012 + * + * Add metadata information to albums / songs / videos + */ + public static function update_370012() + { + $retval = true; + + $sql = "ALTER TABLE `album` ADD `release_type` varchar(32) CHARACTER SET utf8 NULL"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "ALTER TABLE `song` ADD `composer` varchar(256) CHARACTER SET utf8 NULL, ADD `channels` MEDIUMINT NULL"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "ALTER TABLE `video` ADD `channels` MEDIUMINT NULL, ADD `bitrate` MEDIUMINT(8) NULL, ADD `video_bitrate` MEDIUMINT(8) NULL, ADD `display_x` MEDIUMINT(8) NULL, ADD `display_y` MEDIUMINT(8) NULL, ADD `frame_rate` FLOAT NULL, ADD `mode` ENUM( 'abr', 'vbr', 'cbr' ) NULL DEFAULT 'cbr'"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('album_release_type','1','Album - Group per release type',25,'boolean','interface')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + return $retval; + } + + /** + * update_370013 + * + * Replace iframe with ajax page load + */ + public static function update_370013() + { + $retval = true; + + $sql = "DELETE FROM `preference` WHERE `name` = 'iframes'"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('ajax_load','1','Ajax page load',25,'boolean','interface')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + return $retval; + } + + /** + * update 370014 + * + * Modified release_date of table video to signed int(11) + */ + public static function update_370014() + { + $retval = true; + $sql="ALTER TABLE `video` CHANGE COLUMN `release_date` `release_date` INT NULL DEFAULT NULL" ; + $retval = Dba::write($sql) ? $retval : false; + return $retval; + } + + /** + * update 370015 + * + * Add session_remember table to store remember tokens + */ + public static function update_370015() + { + $retval = true; + $sql = "CREATE TABLE `session_remember` (" . + "`username` varchar(16) NOT NULL," . + "`token` varchar(32) NOT NULL," . + "`expire` int(11) NULL," . + "PRIMARY KEY (`username`, `token`)) ENGINE = MYISAM"; + $retval = Dba::write($sql) ? $retval : false; + return $retval; + } + + /** + * update 370016 + * + * Add limit of media count for direct play preference + */ + public static function update_370016() + { + $retval = true; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('direct_play_limit','0','Limit direct play to maximum media count',25,'integer','interface')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + return $retval; + } + + /** + * update 370017 + * + * Add home display settings + */ + public static function update_370017() + { + $retval = true; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('home_moment_albums','1','Show Albums of the moment at home page',25,'integer','interface')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('home_moment_videos','1','Show Videos of the moment at home page',25,'integer','interface')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('home_recently_played','1','Show Recently Played at home page',25,'integer','interface')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('home_now_playing','1','Show Now Playing at home page',25,'integer','interface')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('custom_logo','','Custom logo url',25,'string','interface')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + return $retval; + } + + /* + * update 370018 + * + * Enhance tag persistent merge reference. + */ + public static function update_370018() + { + $retval = true; + $sql = "CREATE TABLE IF NOT EXISTS `tag_merge` ( " . + "`tag_id` int(11) NOT NULL, " . + "`merged_to` int(11) NOT NULL, " . + "FOREIGN KEY (`tag_id`) REFERENCES `tag` (`tag_id`), " . + "FOREIGN KEY (`merged_to`) REFERENCES `tag` (`tag_id`), " . + "PRIMARY KEY (`tag_id`, `merged_to`)) ENGINE = MYISAM"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "INSERT INTO `tag_merge` (`tag_id`, `merged_to`) " . + "SELECT `tag`.`id`, `tag`.`merged_to` " . + "FROM `tag` " . + "WHERE `merged_to` IS NOT NULL"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "ALTER TABLE `tag` DROP COLUMN `merged_to`"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "ALTER TABLE `tag` ADD COLUMN `is_hidden` TINYINT(1) NOT NULL DEFAULT 0"; + $retval = Dba::write($sql) ? $retval : false; + + return $retval; + } + + /** + * update 370019 + * + * Add album group order setting + */ + public static function update_370019() + { + $retval = true; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('album_release_type_sort','album,ep,live,single','Album - Group per release type Sort',25,'string','interface')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'album,ep,live,single')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + return $retval; + } + + /** + * update 370020 + * + * Add webplayer browser notification settings + */ + public static function update_370020() + { + $retval = true; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('browser_notify','1','WebPlayer browser notifications',25,'integer','interface')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('browser_notify_timeout','10','WebPlayer browser notifications timeout (seconds)',25,'integer','interface')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'10')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + return $retval; + } + + /** + * update 370021 + * + * Add rating to playlists, tvshows and tvshows seasons + */ + public static function update_370021() + { + $retval = true; + + $sql = "ALTER TABLE `rating` CHANGE `object_type` `object_type` ENUM ('artist','album','song','stream','video','playlist','tvshow','tvshow_season') NULL"; + $retval = Dba::write($sql) ? $retval : false; + + return $retval; + } + + /** + * update 370022 + * + * Add users geolocation + */ + public static function update_370022() + { + $retval = true; + + $sql = "ALTER TABLE `session` ADD COLUMN `geo_latitude` DECIMAL(10,6) NULL, ADD COLUMN `geo_longitude` DECIMAL(10,6) NULL, ADD COLUMN `geo_name` VARCHAR(255) NULL"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "ALTER TABLE `object_count` ADD COLUMN `geo_latitude` DECIMAL(10,6) NULL, ADD COLUMN `geo_longitude` DECIMAL(10,6) NULL, ADD COLUMN `geo_name` VARCHAR(255) NULL"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('geolocation','0','Allow geolocation',25,'integer','options')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + return $retval; + } + + /** + * update 370023 + * + * Add Aurora.js webplayer option + */ + public static function update_370023() + { + $retval = true; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('webplayer_aurora','1','Authorize JavaScript decoder (Aurora.js) in Web Player(s)',25,'boolean','streaming')"; + $retval = Dba::write($sql) ? $retval : false; + + $id = Dba::insert_id(); + + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + return $retval; + } + + /** + * update 370024 + * + * Add count_type column to object_count table + */ + public static function update_370024() + { + $retval = true; + + $sql = "ALTER TABLE `object_count` ADD COLUMN `count_type` VARCHAR(16) NOT NULL DEFAULT 'stream'"; + $retval = Dba::write($sql) ? $retval : false; + + return $retval; + } + + /** + * update 370025 + * + * Add state and city fields to user table + */ + public static function update_370025() + { + $retval = true; + + $sql = "ALTER TABLE `user` ADD COLUMN `state` VARCHAR(64) NULL, ADD COLUMN `city` VARCHAR(64) NULL"; + $retval = Dba::write($sql) ? $retval : false; + + return $retval; + } + + /** + * update 370026 + * + * Add replay gain fields to song_data table + */ + public static function update_370026() + { + $retval = true; + + $sql = "ALTER TABLE `song_data` ADD COLUMN `replaygain_track_gain` DECIMAL(10,6) NULL, ADD COLUMN `replaygain_track_peak` DECIMAL(10,6) NULL, " . + "ADD COLUMN `replaygain_album_gain` DECIMAL(10,6) NULL, ADD COLUMN `replaygain_album_peak` DECIMAL(10,6) NULL"; + $retval = Dba::write($sql) ? $retval : false; + + return $retval; + } + + /** + * update_370027 + * + * Move column album_artist from table song to table album + * + */ + public static function update_370027() + { + $retval = true; + + $sql = "ALTER TABLE `album` ADD `album_artist` int(11) unsigned DEFAULT NULL AFTER `release_type`"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "UPDATE `album` INNER JOIN `song` ON `album`.`id` = `song`.`album` SET `album`.`album_artist` = `song`.`album_artist`"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "ALTER TABLE `song` DROP COLUMN `album_artist`"; + $retval = Dba::write($sql) ? $retval : false; + + return $retval; + } + + + + /** + * update_370028 + * + * Add width and height in table image + * + */ + public static function update_370028() + { + $retval = true; + + $sql = "select `width` from `image`"; + $db_results = Dba::read($sql); + if (!$db_results) { + $sql = "ALTER TABLE `image` ADD `width` int(4) unsigned DEFAULT 0 AFTER `image`"; + $retval = Dba::write($sql) ? $retval : false; + } + + $sql = "select `height` from `image`"; + $db_results = Dba::read($sql); + if (!$db_results) { + $sql = "ALTER TABLE `image` ADD `height` int(4) unsigned DEFAULT 0 AFTER `width`"; + $retval = Dba::write($sql) ? $retval : false; + } + + return $retval; + } + + /** + * update_370029 + * + * Set image column from image table as nullable. + * + */ + public static function update_370029() + { + $retval = true; + + $sql = "ALTER TABLE `image` CHANGE COLUMN `image` `image` MEDIUMBLOB NULL DEFAULT NULL" ; + $retval = Dba::write($sql) ? $retval : false; + + return $retval; + } + + /** + * update_370030 + * + * Add an option to allow users to remove uploaded songs. + */ + public static function update_370030() + { + $retval = true; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('upload_allow_remove','1','Upload: allow users to remove uploaded songs',75,'boolean','system')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'1')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + return $retval; + } + + /** + * update_370031 + * + * Add an option to customize login art, favicon and text footer. + */ + public static function update_370031() + { + $retval = true; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('custom_login_logo','','Custom login page logo url',75,'string','interface')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('custom_favicon','','Custom favicon url',75,'string','interface')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('custom_text_footer','','Custom text footer',75,'string','interface')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + return $retval; + } + + /** + * update_370032 + * + * Add WebDAV backend preference. + */ + public static function update_370032() + { + $retval = true; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('webdav_backend','0','Use WebDAV backend',100,'boolean','system')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + return $retval; + } + + /** + * update_370033 + * + * Add Label tables. + */ + public static function update_370033() + { + $retval = true; + + $sql = "CREATE TABLE `label` (" . + "`id` int(11) unsigned NOT NULL AUTO_INCREMENT," . + "`name` varchar(80) NOT NULL," . + "`category` varchar(40) NULL," . + "`summary` TEXT CHARACTER SET utf8 NULL," . + "`address` varchar(256) NULL," . + "`email` varchar(128) NULL," . + "`website` varchar(256) NULL," . + "`user` int(11) unsigned NULL," . + "`creation_date` int(11) unsigned NULL," . + "PRIMARY KEY (`id`)) ENGINE = MYISAM"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "CREATE TABLE `label_asso` (" . + "`id` int(11) unsigned NOT NULL AUTO_INCREMENT," . + "`label` int(11) unsigned NOT NULL," . + "`artist` int(11) unsigned NOT NULL," . + "`creation_date` int(11) unsigned NULL," . + "PRIMARY KEY (`id`)) ENGINE = MYISAM"; + $retval = Dba::write($sql) ? $retval : false; + + return $retval; + } + + /** + * update_370034 + * + * Add User messages and user follow tables. + */ + public static function update_370034() + { + $retval = true; + + $sql = "CREATE TABLE `user_pvmsg` (" . + "`id` int(11) unsigned NOT NULL AUTO_INCREMENT," . + "`subject` varchar(80) NOT NULL," . + "`message` TEXT CHARACTER SET utf8 NULL," . + "`from_user` int(11) unsigned NOT NULL," . + "`to_user` int(11) unsigned NOT NULL," . + "`is_read` tinyint(1) unsigned NOT NULL DEFAULT '0'," . + "`creation_date` int(11) unsigned NULL," . + "PRIMARY KEY (`id`)) ENGINE = MYISAM"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "CREATE TABLE `user_follower` (" . + "`id` int(11) unsigned NOT NULL AUTO_INCREMENT," . + "`user` int(11) unsigned NOT NULL," . + "`follow_user` int(11) unsigned NOT NULL," . + "`follow_date` int(11) unsigned NULL," . + "PRIMARY KEY (`id`)) ENGINE = MYISAM"; + $retval = Dba::write($sql) ? $retval : false; + + $sql = "INSERT INTO `preference` (`name`,`value`,`description`,`level`,`type`,`catagory`) " . + "VALUES ('notify_email','0','Receive notifications by email (shouts, private messages, ...)',25,'boolean','options')"; + $retval = Dba::write($sql) ? $retval : false; + $id = Dba::insert_id(); + $sql = "INSERT INTO `user_preference` VALUES (-1,?,'0')"; + $retval = Dba::write($sql, array($id)) ? $retval : false; + + return $retval; + } + + /** + * update_370035 + * + * Add option on user fullname to show/hide it publicly + */ + public static function update_370035() + { + $retval = true; + + $sql = "ALTER TABLE `user` ADD COLUMN `fullname_public` TINYINT( 1 ) UNSIGNED NOT NULL DEFAULT '0'"; + $retval = Dba::write($sql) ? $retval : false; + + return $retval; + } } diff --git a/sources/lib/class/upload.class.php b/sources/lib/class/upload.class.php new file mode 100644 index 0000000..5943c6a --- /dev/null +++ b/sources/lib/class/upload.class.php @@ -0,0 +1,221 @@ + 0) { + $catalog = Catalog::create_from_id($catalog_id); + if ($catalog->catalog_type == "local") { + $allowed = explode('|', AmpConfig::get('catalog_file_pattern')); + + if (isset($_FILES['upl']) && $_FILES['upl']['error'] == 0) { + $extension = pathinfo($_FILES['upl']['name'], PATHINFO_EXTENSION); + + if (!in_array(strtolower($extension), $allowed)) { + debug_event('upload', 'File extension `' . $extension . '` not allowed.', '2'); + return self::rerror(); + } + + $rootdir = self::get_root($catalog); + $targetdir = $rootdir; + $folder = $_POST['folder']; + if ($folder == '..') { + $folder = ''; + } + if (!empty($folder)) { + $targetdir .= DIRECTORY_SEPARATOR . $folder; + } + + $targetdir = realpath($targetdir); + if (strpos($targetdir, $rootdir) === FALSE) { + debug_event('upload', 'Something wrong with final upload path.', '1'); + return self::rerror(); + } + + $targetfile = $targetdir . DIRECTORY_SEPARATOR . time() . '_' . $_FILES['upl']['name']; + if (Core::is_readable($targetfile)) { + debug_event('upload', 'File `' . $targetfile . '` already exists.', '1'); + return self::rerror(); + } + + if (move_uploaded_file($_FILES['upl']['tmp_name'], $targetfile)) { + debug_event('upload', 'File uploaded to `' . $targetfile . '`.', '5'); + + if (AmpConfig::get('upload_script')) { + chdir($targetdir); + exec(AmpConfig::get('upload_script')); + } + + $options = array(); + $options['user_upload'] = $GLOBALS['user']->id; + if (isset($_POST['license'])) { + $options['license'] = $_POST['license']; + } + $artist_id = intval($_REQUEST['artist']); + $album_id = intval($_REQUEST['album']); + + // Override artist information with artist's user + if (AmpConfig::get('upload_user_artist')) { + $artists = $GLOBALS['user']->get_artists(); + $artist = null; + // No associated artist yet, we create a default one for the user sender + if (count($artists) == 0) { + $artists[] = Artist::check($GLOBALS['user']->f_name); + $artist = new Artist($artists[0]); + $artist->update_artist_user($GLOBALS['user']->id); + } else { + $artist = new Artist($artists[0]); + } + $artist_id = $artist->id; + } else { + // Try to create a new artist + if (isset($_REQUEST['artist_name'])) { + $artist_id = Artist::check($_REQUEST['artist_name'], null, true); + if ($artist_id && !Access::check('interface', 50)) { + debug_event('upload', 'An artist with the same name already exists, uploaded song skipped.', 3); + return self::rerror($targetfile); + } else { + $artist_id = Artist::check($_REQUEST['artist_name']); + $artist = new Artist($artist_id); + if (!$artist->get_user_owner()) { + $artist->update_artist_user($GLOBALS['user']->id); + } + } + } + if (!Access::check('interface', 50)) { + // If the user doesn't have privileges, check it is assigned to an artist he owns + if (!$artist_id) { + debug_event('upload', 'Artist information required, uploaded song skipped.', 3); + return self::rerror($targetfile); + } + $artist = new Artist($artist_id); + if ($artist->get_user_owner() != $GLOBALS['user']->id) { + debug_event('upload', 'Artist owner doesn\'t match the current user.', 3); + return self::rerror($targetfile); + } + } + } + // Try to create a new album + if (isset($_REQUEST['album_name'])) { + $album_id = Album::check($_REQUEST['album_name'], 0, 0, null, null, $artist_id); + } + + if (!Access::check('interface', 50)) { + // If the user doesn't have privileges, check it is assigned to an album he owns + if (!$album_id) { + debug_event('upload', 'Album information required, uploaded song skipped.', 3); + return self::rerror($targetfile); + } + $album = new Album($album_id); + if ($album->get_user_owner() != $GLOBALS['user']->id) { + debug_event('upload', 'Album owner doesn\'t match the current user.', 3); + return self::rerror($targetfile); + } + } + + if ($artist_id) { + $options['artist_id'] = $artist_id; + } + if ($album_id) { + $options['album_id'] = $album_id; + } + + $catalog->add_file($targetfile, $options); + + ob_get_contents(); + ob_end_clean(); + echo '{"status":"success"}'; + return true; + } else { + debug_event('upload', 'Cannot copy the file to target directory. Please check write access.', '1'); + } + } + } else { + debug_event('upload', 'The catalog must be local to upload files on it.', '1'); + } + } else { + debug_event('upload', 'No catalog target upload configured.', '1'); + } + + return self::rerror(); + } + + public static function rerror($file = null) + { + if ($file) { + @unlink($file); + } + @header($_SERVER['SERVER_PROTOCOL'] . ' 500 File Upload Error', true, 500); + ob_get_contents(); + ob_end_clean(); + echo '{"status":"error"}'; + return false; + } + + public static function get_root($catalog = null, $username = null) + { + if ($catalog == null) { + $catalog_id = AmpConfig::get('upload_catalog'); + if ($catalog_id > 0) { + $catalog = Catalog::create_from_id($catalog_id); + } + } + + if (is_null($username)) { + $username = $GLOBALS['user']->username; + } + + $rootdir = ""; + if ($catalog != null && $catalog->id) { + $rootdir = realpath($catalog->path); + if (!empty($rootdir)) { + if (AmpConfig::get('upload_subdir')) { + $rootdir .= DIRECTORY_SEPARATOR . $username; + if (!Core::is_readable($rootdir)) { + debug_event('upload', 'Target user directory `' . $rootdir . '` doesn\'t exists. Creating it...', '5'); + mkdir($rootdir); + } + } + } + } + + return $rootdir; + } + +} // Upload class diff --git a/sources/lib/class/upnp_api.class.php b/sources/lib/class/upnp_api.class.php new file mode 100644 index 0000000..fe87275 --- /dev/null +++ b/sources/lib/class/upnp_api.class.php @@ -0,0 +1,1220 @@ +XML($prmRequest); + while ($reader->read()) { + if (($reader->nodeType == XMLReader::ELEMENT) && !$reader->isEmptyElement) { + switch ($reader->localName) { + case 'Browse': + $retArr['action'] = 'browse'; + break; + case 'Search': + $retArr['action'] = 'search'; + break; + case 'ObjectID': + $reader->read(); + if ($reader->nodeType == XMLReader::TEXT) { + $retArr['objectid'] = $reader->value; + } # end if + break; + case 'BrowseFlag': + $reader->read(); + if ($reader->nodeType == XMLReader::TEXT) { + $retArr['browseflag'] = $reader->value; + } # end if + break; + case 'Filter': + $reader->read(); + if ($reader->nodeType == XMLReader::TEXT) { + $retArr['filter'] = $reader->value; + } # end if + break; + case 'StartingIndex': + $reader->read(); + if ($reader->nodeType == XMLReader::TEXT) { + $retArr['startingindex'] = $reader->value; + } # end if + break; + case 'RequestedCount': + $reader->read(); + if ($reader->nodeType == XMLReader::TEXT) { + $retArr['requestedcount'] = $reader->value; + } # end if + break; + case 'SearchCriteria': + $reader->read(); + if ($reader->nodeType == XMLReader::TEXT) { + $retArr['searchcriteria'] = $reader->value; + } # end if + break; + case 'SortCriteria': + $reader->read(); + if ($reader->nodeType == XMLReader::TEXT) { + $retArr['sortcriteria'] = $reader->value; + } # end if + break; + } # end switch + } # end if + } #end while + return $retArr; + } #end function + + + public static function createDIDL($prmItems) + { + $xmlDoc = new DOMDocument('1.0', 'utf-8'); + $xmlDoc->formatOutput = true; + + # Create root element and add namespaces: + $ndDIDL = $xmlDoc->createElementNS('urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/', 'DIDL-Lite'); + $ndDIDL->setAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/'); + $ndDIDL->setAttribute('xmlns:upnp', 'urn:schemas-upnp-org:metadata-1-0/upnp/'); + $xmlDoc->appendChild($ndDIDL); + + # Return empty DIDL if no items present: + if ( (!isset($prmItems)) || (!is_array($prmItems)) ) { + return $xmlDoc; + } + + # sometimes here comes only one single item, not an array. Convert it to array. (TODO - UGLY) + if ( (count($prmItems) > 0) && (!is_array($prmItems[0])) ) { + $prmItems = array($prmItems); + } + + # Add each item in $prmItems array to $ndDIDL: + foreach ($prmItems as $item) { + if (!is_array($item)) { + debug_event('upnp_class', 'item is not array', 2); + debug_event('upnp_class', $item, '5'); + continue; + } + + if ($item['upnp:class'] == 'object.container') { + $ndItem = $xmlDoc->createElement('container'); + } else { + $ndItem = $xmlDoc->createElement('item'); + } + $useRes = false; + $ndRes = $xmlDoc->createElement('res'); + $ndRes_text = $xmlDoc->createTextNode($item['res']); + $ndRes->appendChild($ndRes_text); + + # Add each element / attribute in $item array to item node: + foreach ($item as $key => $value) { + # Handle attributes. Better solution? + switch ($key) { + case 'id': + $ndItem->setAttribute('id', $value); + break; + case 'parentID': + $ndItem->setAttribute('parentID', $value); + break; + case 'childCount': + $ndItem->setAttribute('childCount', $value); + break; + case 'restricted': + $ndItem->setAttribute('restricted', $value); + break; + case 'res': + break; + case 'duration': + $ndRes->setAttribute('duration', $value); + $useRes = true; + break; + case 'size': + $ndRes->setAttribute('size', $value); + $useRes = true; + break; + case 'bitrate': + $ndRes->setAttribute('bitrate', $value); + $useRes = true; + break; + case 'protocolInfo': + $ndRes->setAttribute('protocolInfo', $value); + $useRes = true; + break; + case 'resolution': + $ndRes->setAttribute('resolution', $value); + $useRes = true; + break; + case 'colorDepth': + $ndRes->setAttribute('colorDepth', $value); + $useRes = true; + break; + case 'sampleFrequency': + $ndRes->setAttribute('sampleFrequency', $value); + $useRes = true; + break; + case 'nrAudioChannels': + $ndRes->setAttribute('nrAudioChannels', $value); + $useRes = true; + break; + default: + $ndTag = $xmlDoc->createElement($key); + $ndItem->appendChild($ndTag); + # check if string is already utf-8 encoded + $ndTag_text = $xmlDoc->createTextNode((mb_detect_encoding($value,'auto')=='UTF-8')?$value:utf8_encode($value)); + $ndTag->appendChild($ndTag_text); + } + if ($useRes) { + $ndItem->appendChild($ndRes); + } + } + $ndDIDL->appendChild($ndItem); + } + return $xmlDoc; + } + + + public static function createSOAPEnvelope($prmDIDL, $prmNumRet, $prmTotMatches, $prmResponseType = 'u:BrowseResponse', $prmUpdateID = '0') + { + # $prmDIDL is DIDL XML string + # XML-Layout: + # + # -s:Envelope + # -s:Body + # -u:BrowseResponse + # Result (DIDL) + # NumberReturned + # TotalMatches + # UpdateID + # + $doc = new DOMDocument('1.0', 'utf-8'); + $doc->formatOutput = true; + $ndEnvelope = $doc->createElementNS('http://schemas.xmlsoap.org/soap/envelope/', 's:Envelope'); + $ndEnvelope->setAttribute("s:encodingStyle", "http://schemas.xmlsoap.org/soap/encoding/"); + $doc->appendChild($ndEnvelope); + $ndBody = $doc->createElement('s:Body'); + $ndEnvelope->appendChild($ndBody); + $ndBrowseResp = $doc->createElementNS('urn:schemas-upnp-org:service:ContentDirectory:1', $prmResponseType); + $ndBody->appendChild($ndBrowseResp); + $ndResult = $doc->createElement('Result',$prmDIDL); + $ndBrowseResp->appendChild($ndResult); + $ndNumRet = $doc->createElement('NumberReturned', $prmNumRet); + $ndBrowseResp->appendChild($ndNumRet); + $ndTotMatches = $doc->createElement('TotalMatches', $prmTotMatches); + $ndBrowseResp->appendChild($ndTotMatches); + $ndUpdateID = $doc->createElement('UpdateID', $prmUpdateID); # seems to be ignored by the WDTVL + #$ndUpdateID = $doc->createElement('UpdateID', (string) mt_rand(); # seems to be ignored by the WDTVL + $ndBrowseResp->appendChild($ndUpdateID); + + Return $doc; + } + + public static function _musicMetadata($prmPath, $prmQuery = '') + { + $root = 'amp://music'; + $pathreq = explode('/', $prmPath); + if ($pathreq[0] == '' && count($pathreq) > 0) { + array_shift($pathreq); + } + + $meta = null; + switch ($pathreq[0]) { + case 'artists': + switch (count($pathreq)) { + case 1: + $counts = Catalog::count_medias(); + $meta = array( + 'id' => $root . '/artists', + 'parentID' => $root, + 'restricted' => '1', + 'childCount' => $counts['artists'], + 'dc:title' => T_('Artists'), + 'upnp:class' => 'object.container', + ); + break; + + case 2: + $artist = new Artist($pathreq[1]); + if ($artist->id) { + $artist->format(); + $meta = self::_itemArtist($artist, $root . '/artists'); + } + break; + } + break; + + case 'albums': + switch (count($pathreq)) { + case 1: + $counts = Catalog::count_medias(); + $meta = array( + 'id' => $root . '/albums', + 'parentID' => $root, + 'restricted' => '1', + 'childCount' => $counts['albums'], + 'dc:title' => T_('Albums'), + 'upnp:class' => 'object.container', + ); + break; + + case 2: + $album = new Album($pathreq[1]); + if ($album->id) { + $album->format(); + $meta = self::_itemAlbum($album, $root . '/albums'); + } + break; + } + break; + + case 'songs': + switch (count($pathreq)) { + case 1: + $counts = Catalog::count_medias(); + $meta = array( + 'id' => $root . '/songs', + 'parentID' => $root, + 'restricted' => '1', + 'childCount' => $counts['songs'], + 'dc:title' => T_('Songs'), + 'upnp:class' => 'object.container', + ); + break; + + case 2: + $song = new Song($pathreq[1]); + if ($song->id) { + $song->format(); + $meta = self::_itemSong($song, $root . '/songs'); + } + break; + } + break; + + case 'playlists': + switch (count($pathreq)) { + case 1: + $counts = Catalog::count_medias(); + $meta = array( + 'id' => $root . '/playlists', + 'parentID' => $root, + 'restricted' => '1', + 'childCount' => $counts['playlists'], + 'dc:title' => T_('Playlists'), + 'upnp:class' => 'object.container', + ); + break; + + case 2: + $playlist = new Playlist($pathreq[1]); + if ($playlist->id) { + $playlist->format(); + $meta = self::_itemPlaylist($playlist, $root . '/playlists'); + } + break; + } + break; + + case 'smartplaylists': + switch (count($pathreq)) { + case 1: + $counts = Catalog::count_medias(); + $meta = array( + 'id' => $root . '/smartplaylists', + 'parentID' => $root, + 'restricted' => '1', + 'childCount' => $counts['smartplaylists'], + 'dc:title' => T_('Smart Playlists'), + 'upnp:class' => 'object.container', + ); + break; + + case 2: + $playlist = new Search($pathreq[1], 'song'); + if ($playlist->id) { + $playlist->format(); + $meta = self::_itemSmartPlaylist($playlist, $root . '/smartplaylists'); + } + break; + } + break; + + default: + $meta = array( + 'id' => $root, + 'parentID' => '0', + 'restricted' => '1', + 'childCount' => '5', + 'dc:title' => T_('Music'), + 'upnp:class' => 'object.container', + ); + break; + } + + return $meta; + } + + private static function _slice($items, $start, $count) + { + $maxCount = count($items); + debug_event('upnp-api', 'slice: ' . $maxCount . " " . $start . " " . $count, '5'); + return array($maxCount, array_slice($items, $start, ($count == 0 ? $maxCount - $start : $count))); + } + + public static function _musicChilds($prmPath, $prmQuery, $start, $count) + { + $mediaItems = array(); + $maxCount = 0; + $queryData = array(); + parse_str($prmQuery, $queryData); + + $parent = 'amp://music' . $prmPath; + $pathreq = explode('/', $prmPath); + if ($pathreq[0] == '' && count($pathreq) > 0) { + array_shift($pathreq); + } + + switch ($pathreq[0]) { + case 'artists': + switch (count($pathreq)) { + case 1: // Get artists list + //$artists = Catalog::get_artists(); + //list($maxCount, $artists) = self::_slice($artists, $start, $count); + $artists = Catalog::get_artists(null, $count, $start); + list($maxCount, $artists) = array(999999, $artists); + foreach ($artists as $artist) { + $artist->format(); + $mediaItems[] = self::_itemArtist($artist, $parent); + } + break; + case 2: // Get artist's albums list + $artist = new Artist($pathreq[1]); + if ($artist->id) { + $album_ids = $artist->get_albums(); + list($maxCount, $album_ids) = self::_slice($album_ids, $start, $count); + foreach ($album_ids as $album_id) { + $album = new Album($album_id); + $album->format(); + $mediaItems[] = self::_itemAlbum($album, $parent); + } + } + break; + } + break; + + case 'albums': + switch (count($pathreq)) { + case 1: // Get albums list + //!!$album_ids = Catalog::get_albums(); + //!!list($maxCount, $album_ids) = self::_slice($album_ids, $start, $count); + $album_ids = Catalog::get_albums($count, $start); + list($maxCount, $album_ids) = array(999999, $album_ids); + foreach ($album_ids as $album_id) { + $album = new Album($album_id); + $album->format(); + $mediaItems[] = self::_itemAlbum($album, $parent); + } + break; + case 2: // Get album's songs list + $album = new Album($pathreq[1]); + if ($album->id) { + $song_ids = $album->get_songs(); + list($maxCount, $song_ids) = self::_slice($song_ids, $start, $count); + foreach ($song_ids as $song_id) { + $song = new Song($song_id); + $song->format(); + $mediaItems[] = self::_itemSong($song, $parent); + } + } + break; + } + break; + + case 'songs': + switch (count($pathreq)) { + case 1: // Get songs list + $catalogs = Catalog::get_catalogs(); + foreach ($catalogs as $catalog_id) { + $catalog = Catalog::create_from_id($catalog_id); + $songs = $catalog->get_songs(); + list($maxCount, $songs) = self::_slice($songs, $start, $count); + foreach ($songs as $song) { + $song->format(); + $mediaItems[] = self::_itemSong($song, $parent); + } + } + break; + } + break; + + case 'playlists': + switch (count($pathreq)) { + case 1: // Get playlists list + $pl_ids = Playlist::get_playlists(); + list($maxCount, $pl_ids) = self::_slice($pl_ids, $start, $count); + foreach ($pl_ids as $pl_id) { + $playlist = new Playlist($pl_id); + $playlist->format(); + $mediaItems[] = self::_itemPlaylist($playlist, $parent); + } + break; + case 2: // Get playlist's songs list + $playlist = new Playlist($pathreq[1]); + if ($playlist->id) { + $items = $playlist->get_items(); + list($maxCount, $items) = self::_slice($items, $start, $count); + foreach ($items as $item) { + if ($item['object_type'] == 'song') { + $song = new Song($item['object_id']); + $song->format(); + $mediaItems[] = self::_itemSong($song, $parent); + } + } + } + break; + } + break; + + case 'smartplaylists': + switch (count($pathreq)) { + case 1: // Get playlists list + $pl_ids = Search::get_searches(); + list($maxCount, $pl_ids) = self::_slice($pl_ids, $start, $count); + foreach ($pl_ids as $pl_id) { + $playlist = new Search($pl_id, 'song'); + $playlist->format(); + $mediaItems[] = self::_itemPlaylist($playlist, $parent); + } + break; + case 2: // Get playlist's songs list + $playlist = new Search($pathreq[1], 'song'); + if ($playlist->id) { + $items = $playlist->get_items(); + list($maxCount, $items) = self::_slice($items, $start, $count); + foreach ($items as $item) { + if ($item['object_type'] == 'song') { + $song = new Song($item['object_id']); + $song->format(); + $mediaItems[] = self::_itemSong($song, $parent); + } + } + } + break; + } + break; + + default: + $mediaItems[] = self::_musicMetadata('artists'); + $mediaItems[] = self::_musicMetadata('albums'); + $mediaItems[] = self::_musicMetadata('songs'); + $mediaItems[] = self::_musicMetadata('playlists'); + $mediaItems[] = self::_musicMetadata('smartplaylists'); + break; + } + + if ($maxCount == 0) + $maxCount = count($mediaItems); + return array($maxCount, $mediaItems); + } + + public static function _videoMetadata($prmPath, $prmQuery = '') + { + $root = 'amp://video'; + $pathreq = explode('/', $prmPath); + if ($pathreq[0] == '' && count($pathreq) > 0) { + array_shift($pathreq); + } + + $meta = null; + switch ($pathreq[0]) { + case 'tvshows': + switch (count($pathreq)) { + case 1: + $counts = count(Catalog::get_tvshows()); + $meta = array( + 'id' => $root . '/tvshows', + 'parentID' => $root, + 'restricted' => '1', + 'childCount' => $counts, + 'dc:title' => T_('TV Shows'), + 'upnp:class' => 'object.container', + ); + break; + + case 2: + $tvshow = new TVShow($pathreq[1]); + if ($tvshow->id) { + $tvshow->format(); + $meta = self::_itemTVShow($tvshow, $root . '/tvshows'); + } + break; + + case 3: + $season = new TVShow_Season($pathreq[2]); + if ($season->id) { + $season->format(); + $meta = self::_itemTVShowSeason($season, $root . '/tvshows/' . $pathreq[1]); + } + break; + + case 4: + $video = new TVShow_Episode($pathreq[3]); + if ($video->id) { + $video->format(); + $meta = self::_itemVideo($video, $root . '/tvshows/' . $pathreq[1] . '/' . $pathreq[2] ); + } + break; + } + break; + + case 'clips': + switch (count($pathreq)) { + case 1: + $counts = Catalog::get_videos_count(null, 'clip'); + $meta = array( + 'id' => $root . '/clips', + 'parentID' => $root, + 'restricted' => '1', + 'childCount' => $counts, + 'dc:title' => T_('Clips'), + 'upnp:class' => 'object.container', + ); + break; + + case 2: + $video = new Clip($pathreq[1]); + if ($video->id) { + $video->format(); + $meta = self::_itemVideo($video, $root . '/clips'); + } + break; + } + break; + + case 'movies': + switch (count($pathreq)) { + case 1: + $counts = Catalog::get_videos_count(null, 'movie'); + $meta = array( + 'id' => $root . '/movies', + 'parentID' => $root, + 'restricted' => '1', + 'childCount' => $counts, + 'dc:title' => T_('Movies'), + 'upnp:class' => 'object.container', + ); + break; + + case 2: + $video = new Movie($pathreq[1]); + if ($video->id) { + $video->format(); + $meta = self::_itemVideo($video, $root . '/movies'); + } + break; + } + break; + + case 'personal_videos': + switch (count($pathreq)) { + case 1: + $counts = Catalog::get_videos_count(null, 'personal_video'); + $meta = array( + 'id' => $root . '/personal_videos', + 'parentID' => $root, + 'restricted' => '1', + 'childCount' => $counts, + 'dc:title' => T_('Personal Videos'), + 'upnp:class' => 'object.container', + ); + break; + + case 2: + $video = new Personal_Video($pathreq[1]); + if ($video->id) { + $video->format(); + $meta = self::_itemVideo($video, $root . '/personal_videos'); + } + break; + } + break; + + default: + $meta = array( + 'id' => $root, + 'parentID' => '0', + 'restricted' => '1', + 'childCount' => '4', + 'dc:title' => T_('Video'), + 'upnp:class' => 'object.container', + ); + break; + } + + return $meta; + } + + public static function _videoChilds($prmPath, $prmQuery, $start, $count) + { + $mediaItems = array(); + $maxCount = 0; + $queryData = array(); + parse_str($prmQuery, $queryData); + + $parent = 'amp://video' . $prmPath; + $pathreq = explode('/', $prmPath); + if ($pathreq[0] == '' && count($pathreq) > 0) { + array_shift($pathreq); + } + + switch ($pathreq[0]) { + case 'tvshows': + switch (count($pathreq)) { + case 1: // Get tvshow list + $tvshows = Catalog::get_tvshows(); + list($maxCount, $tvshows) = self::_slice($tvshows, $start, $count); + foreach ($tvshows as $tvshow) { + $tvshow->format(); + $mediaItems[] = self::_itemTVShow($tvshow, $parent); + } + break; + case 2: // Get season list + $tvshow = new TVShow($pathreq[1]); + if ($tvshow->id) { + $season_ids = $tvshow->get_seasons(); + list($maxCount, $season_ids) = self::_slice($season_ids, $start, $count); + foreach ($season_ids as $season_id) { + $season = new TVShow_Season($season_id); + $season->format(); + $mediaItems[] = self::_itemTVShowSeason($season, $parent); + } + } + break; + case 3: // Get episode list + $season = new TVShow_Season($pathreq[2]); + if ($season->id) { + $episode_ids = $season->get_episodes(); + list($maxCount, $episode_ids) = self::_slice($episode_ids, $start, $count); + foreach ($episode_ids as $episode_id) { + $video = new Video($episode_id); + $video->format(); + $mediaItems[] = self::_itemVideo($video, $parent); + } + } + break; + } + break; + + case 'clips': + switch (count($pathreq)) { + case 1: // Get clips list + $videos = Catalog::get_videos(null, 'clip'); + list($maxCount, $videos) = self::_slice($videos, $start, $count); + foreach ($videos as $video) { + $video->format(); + $mediaItems[] = self::_itemVideo($video, $parent); + } + break; + } + break; + + case 'movies': + switch (count($pathreq)) { + case 1: // Get clips list + $videos = Catalog::get_videos(null, 'movie'); + list($maxCount, $videos) = self::_slice($videos, $start, $count); + foreach ($videos as $video) { + $video->format(); + $mediaItems[] = self::_itemVideo($video, $parent); + } + break; + } + break; + + case 'personal_videos': + switch (count($pathreq)) { + case 1: // Get clips list + $videos = Catalog::get_videos(null, 'personal_video'); + list($maxCount, $videos) = self::_slice($videos, $start, $count); + foreach ($videos as $video) { + $video->format(); + $mediaItems[] = self::_itemVideo($video, $parent); + } + break; + } + break; + + default: + $mediaItems[] = self::_videoMetadata('clips'); + $mediaItems[] = self::_videoMetadata('tvshows'); + $mediaItems[] = self::_videoMetadata('movies'); + $mediaItems[] = self::_videoMetadata('personal_videos'); + break; + } + + if ($maxCount == 0) + $maxCount = count($mediaItems); + return array($maxCount, $mediaItems); + } + + public static function _callSearch($criteria) + { + // Not supported yet + return array(); + } + + private static function _replaceSpecialSymbols($title) + { + ///debug_event('upnp_class', 'replace <<< ' . $title, 5); + // replace non letter or digits + $title = preg_replace('~[^\\pL\d\.\s\(\)\.\,\'\"]+~u', '-', $title); + ///debug_event('upnp_class', 'replace >>> ' . $title, 5); + + if ($title == "") + $title = '(no title)'; + + return $title; + } + + private static function _itemArtist($artist, $parent) + { + return array( + 'id' => 'amp://music/artists/' . $artist->id, + 'parentID' => $parent, + 'restricted' => '1', + 'childCount' => $artist->albums, + 'dc:title' => self::_replaceSpecialSymbols($artist->f_name), + 'upnp:class' => 'object.container', // object.container.person.musicArtist + ); + } + + private static function _itemAlbum($album, $parent) + { + $api_session = (AmpConfig::get('require_session')) ? Stream::get_session() : false; + $art_url = Art::url($album->id, 'album', $api_session); + + return array( + 'id' => 'amp://music/albums/' . $album->id, + 'parentID' => $parent, + 'restricted' => '1', + 'childCount' => $album->song_count, + 'dc:title' => self::_replaceSpecialSymbols($album->f_title), + 'upnp:class' => 'object.container', // object.container.album.musicAlbum + 'upnp:albumArtURI' => $art_url, + ); + } + + private static function _itemPlaylist($playlist, $parent) + { + return array( + 'id' => 'amp://music/playlists/' . $playlist->id, + 'parentID' => $parent, + 'restricted' => '1', + 'childCount' => count($playlist->get_items()), + 'dc:title' => self::_replaceSpecialSymbols($playlist->f_name), + 'upnp:class' => 'object.container', // object.container.playlistContainer + ); + } + + private static function _itemSmartPlaylist($playlist, $parent) + { + return array( + 'id' => 'amp://music/smartplaylists/' . $playlist->id, + 'parentID' => $parent, + 'restricted' => '1', + 'childCount' => count($playlist->get_items()), + 'dc:title' => self::_replaceSpecialSymbols($playlist->f_name), + 'upnp:class' => 'object.container', + ); + } + + public static function _itemSong($song, $parent) + { + $api_session = (AmpConfig::get('require_session')) ? Stream::get_session() : false; + $art_url = Art::url($song->album, 'album', $api_session); + + $fileTypesByExt = self::_getFileTypes(); + $arrFileType = $fileTypesByExt[$song->type]; + + return array( + 'id' => 'amp://music/songs/' . $song->id, + 'parentID' => $parent, + 'restricted' => '1', + 'dc:title' => self::_replaceSpecialSymbols($song->f_title), + 'upnp:class' => (isset($arrFileType['class'])) ? $arrFileType['class'] : 'object.item.unknownItem', + 'upnp:albumArtURI' => $art_url, + 'upnp:artist' => self::_replaceSpecialSymbols($song->f_artist), + 'upnp:album' => self::_replaceSpecialSymbols($song->f_album), + 'upnp:genre' => Tag::get_display($song->tags, false, 'song'), + //'dc:date' => date("c", $song->addition_time), + 'upnp:originalTrackNumber' => $song->track, + + 'res' => Song::play_url($song->id, '', 'api'), + 'protocolInfo' => $arrFileType['mime'], + 'size' => $song->size, + 'duration' => $song->f_time_h . '.0', + 'bitrate' => $song->bitrate, + 'sampleFrequency' => $song->rate, + //'nrAudioChannels' => '1', + 'description' => self::_replaceSpecialSymbols($song->comment), + ); + } + + private static function _itemTVShow($tvshow, $parent) + { + return array( + 'id' => 'amp://video/tvshows/' . $tvshow->id, + 'parentID' => $parent, + 'restricted' => '1', + 'childCount' => count($tvshow->get_seasons()), + 'dc:title' => self::_replaceSpecialSymbols($tvshow->f_name), + 'upnp:class' => 'object.container', + ); + } + + private static function _itemTVShowSeason($season, $parent) + { + return array( + 'id' => 'amp://video/tvshows/' . $season->tvshow . '/' . $season->id, + 'parentID' => $parent, + 'restricted' => '1', + 'childCount' => count($season->get_episodes()), + 'dc:title' => self::_replaceSpecialSymbols($season->f_name), + 'upnp:class' => 'object.container', + ); + } + + private static function _itemVideo($video, $parent) + { + $api_session = (AmpConfig::get('require_session')) ? Stream::get_session() : false; + $art_url = Art::url($video->id, 'video', $api_session); + + $fileTypesByExt = self::_getFileTypes(); + $arrFileType = $fileTypesByExt[$video->type]; + + return array( + 'id' => $parent . '/' . $video->id, + 'parentID' => $parent, + 'restricted' => '1', + 'dc:title' => self::_replaceSpecialSymbols($video->f_title), + 'upnp:class' => (isset($arrFileType['class'])) ? $arrFileType['class'] : 'object.item.unknownItem', + 'upnp:albumArtURI' => $art_url, + 'upnp:genre' => Tag::get_display($video->tags, false, 'video'), + + 'res' => Video::play_url($video->id, '', 'api'), + 'protocolInfo' => $arrFileType['mime'], + 'size' => $video->size, + 'duration' => $video->f_time_h . '.0', + ); + } + + private static function _getFileTypes() + { + return array( + 'wav' => array( + 'class' => 'object.item.audioItem', + 'mime' => 'http-get:*:audio/x-wav:*', + ), + 'mpa' => array( + 'class' => 'object.item.audioItem', + 'mime' => 'http-get:*:audio/mpeg:*', + ), + '.mp1' => array( + 'class' => 'object.item.audioItem', + 'mime' => 'http-get:*:audio/mpeg:*', + ), + 'mp3' => array( + 'class' => 'object.item.audioItem.musicTrack', + 'mime' => 'http-get:*:audio/mpeg:*', + ), + 'aiff' => array( + 'class' => 'object.item.audioItem', + 'mime' => 'http-get:*:audio/x-aiff:*', + ), + 'aif' => array( + 'class' => 'object.item.audioItem', + 'mime' => 'http-get:*:audio/x-aiff:*', + ), + 'wma' => array( + 'class' => 'object.item.audioItem', + 'mime' => 'http-get:*:audio/x-ms-wma:*', + ), + 'lpcm' => array( + 'class' => 'object.item.audioItem', + 'mime' => 'http-get:*:audio/lpcm:*', + ), + 'aac' => array( + 'class' => 'object.item.audioItem', + 'mime' => 'http-get:*:audio/x-aac:*', + ), + 'm4a' => array( + 'class' => 'object.item.audioItem', + 'mime' => 'http-get:*:audio/x-m4a:*', + ), + 'ac3' => array( + 'class' => 'object.item.audioItem', + 'mime' => 'http-get:*:audio/x-ac3:*', + ), + 'pcm' => array( + 'class' => 'object.item.audioItem', + 'mime' => 'http-get:*:audio/lpcm:*', + ), + 'flac' => array( + 'class' => 'object.item.audioItem', + 'mime' => 'http-get:*:audio/flac:*', + ), + 'ogg' => array( + 'class' => 'object.item.audioItem', + 'mime' => 'http-get:*:application/ogg:*', + ), + 'mka' => array( + 'class' => 'object.item.audioItem', + 'mime' => 'http-get:*:audio/x-matroska:*', + ), + 'mp4a' => array( + 'class' => 'object.item.audioItem', + 'mime' => 'http-get:*:audio/x-m4a:*', + ), + 'mp2' => array( + 'class' => 'object.item.audioItem', + 'mime' => 'http-get:*:audio/mpeg:*', + ), + 'gif' => array( + 'class' => 'object.item.imageItem', + 'mime' => 'http-get:*:image/gif:*', + ), + 'jpg' => array( + 'class' => 'object.item.imageItem', + 'mime' => 'http-get:*:image/jpeg:*', + ), + 'jpe' => array( + 'class' => 'object.item.imageItem', + 'mime' => 'http-get:*:image/jpeg:*', + ), + 'png' => array( + 'class' => 'object.item.imageItem', + 'mime' => 'http-get:*:image/png:*', + ), + 'tiff' => array( + 'class' => 'object.item.imageItem', + 'mime' => 'http-get:*:image/tiff:*', + ), + 'tif' => array( + 'class' => 'object.item.imageItem', + 'mime' => 'http-get:*:image/tiff:*', + ), + 'jpeg' => array( + 'class' => 'object.item.imageItem', + 'mime' => 'http-get:*:image/jpeg:*', + ), + 'bmp' => array( + 'class' => 'object.item.imageItem', + 'mime' => 'http-get:*:image/bmp:*', + ), + 'asf' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/x-ms-asf:*', + ), + 'wmv' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/x-ms-wmv:*', + ), + 'mpeg2' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mpeg2:*', + ), + 'avi' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/x-msvideo:*', + ), + 'divx' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/x-msvideo:*', + ), + 'mpg' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mpeg:*', + ), + 'm1v' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mpeg:*', + ), + 'm2v' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mpeg:*', + ), + 'mp4' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mp4:*', + ), + 'mov' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/quicktime:*', + ), + 'vob' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/dvd:*', + ), + 'dvr-ms' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/x-ms-dvr:*', + ), + 'dat' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mpeg:*', + ), + 'mpeg' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mpeg:*', + ), + 'm1s' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mpeg:*', + ), + 'm2p' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mpeg2:*', + ), + 'm2t' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mpeg2ts:*', + ), + 'm2ts' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mpeg2ts:*', + ), + 'mts' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mpeg2ts:*', + ), + 'ts' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mpeg2ts:*', + ), + 'tp' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mpeg2ts:*', + ), + 'trp' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mpeg2ts:*', + ), + 'm4t' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mpeg2ts:*', + ), + 'm4v' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/MP4V-ES:*', + ), + 'vbs' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mpeg2:*', + ), + 'mod' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mpeg2:*', + ), + 'mkv' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/x-matroska:*', + ), + '3g2' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mp4:*', + ), + '3gp' => array( + 'class' => 'object.item.videoItem', + 'mime' => 'http-get:*:video/mp4:*', + ), + ); + } +} diff --git a/sources/lib/class/user.class.php b/sources/lib/class/user.class.php index 33f4bfc..3149f0d 100644 --- a/sources/lib/class/user.class.php +++ b/sources/lib/class/user.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -31,30 +31,113 @@ class User extends database_object { //Basic Componets + /** + * @var int $id + */ public $id; + /** + * @var string $username + */ public $username; + /** + * @var string $fullname + */ public $fullname; + /** + * @var boolean $fullname_public + */ + public $fullname_public; + /** + * @var int $access + */ public $access; + /** + * @var boolean $disabled + */ public $disabled; + /** + * @var string $email + */ public $email; + /** + * @var int $last_seen + */ public $last_seen; + /** + * @var int $create_date + */ public $create_date; + /** + * @var string $validation + */ public $validation; + /** + * @var string $website + */ public $website; + /** + * @var string $state + */ + public $state; + /** + * @var string city + */ + public $city; + /** + * @var string $apikey + */ public $apikey; // Constructed variables + /** + * @var array $prefs + */ public $prefs = array(); + + /** + * @var Tmp_Playlist $playlist + */ public $playlist; + /** + * @var string $f_name + */ + public $f_name; + /** + * @var string $f_last_seen + */ public $f_last_seen; + /** + * @var string $f_create_date + */ public $f_create_date; + /** + * @var string $link + */ public $link; + /** + * @var string $f_link + */ public $f_link; + /** + * @var string $f_useage + */ public $f_useage; + /** + * @var string $ip_history + */ public $ip_history; + /** + * @var string $f_avatar + */ public $f_avatar; + /** + * @var string $f_avatar_mini + */ public $f_avatar_mini; + /** + * @var string $f_avatar_medium + */ public $f_avatar_medium; /** @@ -472,7 +555,7 @@ class User extends database_object */ public function has_access($needed_level) { - if (!AmpConfig::get('use_auth') || AmpConfig::get('demo_mode')) { return true; } + if (AmpConfig::get('demo_mode')) { return true; } if ($this->access >= $needed_level) { return true; } @@ -480,13 +563,27 @@ class User extends database_object } // has_access + /** + * is_registered + * Check if the user is registered + * @return boolean + */ + public static function is_registered() + { + if (!$GLOBALS['user']->id) return false; + + if (!AmpConfig::get('use_auth') && $GLOBALS['user']->access <= 5) return false; + + return true; + } + /** * update * This function is an all encompasing update function that * calls the mini ones does all the error checking and all that * good stuff */ - public function update($data) + public function update(array $data) { if (empty($data['username'])) { Error::add('username', T_('Error Username Required')); @@ -500,6 +597,10 @@ class User extends database_object return false; } + if (!isset($data['fullname_public'])) { + $data['fullname_public'] = false; + } + foreach ($data as $name => $value) { if ($name == 'password1') { $name = 'password'; @@ -513,19 +614,25 @@ class User extends database_object case 'email': case 'username': case 'fullname': + case 'fullname_public': case 'website': + case 'state': + case 'city': if ($this->$name != $value) { $function = 'update_' . $name; $this->$function($value); } break; + case 'clear_stats': + Stats::clear($this->id); + break; default: // Rien a faire break; } } - return true; + return $this->id; } /** @@ -567,6 +674,17 @@ class User extends database_object } // update_fullname + /** + * update_fullname_public + * updates their fullname public + */ + public function update_fullname_public($new_fullname_public) + { + $sql = "UPDATE `user` SET `fullname_public` = ? WHERE `id` = ?"; + Dba::write($sql, array($new_fullname_public ? '1' : '0', $this->id)); + + } // update_fullname_public + /** * update_email * updates their email address @@ -590,6 +708,26 @@ class User extends database_object } // update_website + /** + * update_state + * updates their state + */ + public function update_state($new_state) + { + $sql = "UPDATE `user` SET `state` = ? WHERE `id` = ?"; + Dba::write($sql, array($new_state, $this->id)); + } // update_state + + /** + * update_city + * updates their city + */ + public function update_city($new_city) + { + $sql = "UPDATE `user` SET `city` = ? WHERE `id` = ?"; + Dba::write($sql, array($new_city, $this->id)); + } // update_city + /** * update_apikey * Updates their api key @@ -694,46 +832,47 @@ class User extends database_object * update_user_stats * updates the playcount mojo for this specific user */ - public function update_stats($song_id, $agent = '') + public function update_stats($media_type, $media_id, $agent = '', $location = array(), $noscrobble = false) { - debug_event('user.class.php', 'Updating stats for {'.$song_id.'} {'.$agent.'}...', '5'); - $song_info = new Song($song_id); - $song_info->format(); + debug_event('user.class.php', 'Updating stats for {'.$media_type.'/'.$media_id.'} {'.$agent.'}...', 5); + $media = new $media_type($media_id); + $media->format(); $user = $this->id; - if (!strlen($song_info->file)) { return false; } + // We shouldn't test on file only + if (!strlen($media->file)) { return false; } - $this->set_preferences(); - - // If pthreads available, we call save_songplay in a new thread to quickly return - if (class_exists("Thread", false)) { - debug_event('user.class.php', 'Calling save_songplay plugins in a new thread...', '5'); - $thread = new scrobbler_async($GLOBALS['user'], $song_info); - if ($thread->start()) { - //$thread->join(); + if (!$noscrobble) { + $this->set_preferences(); + // If pthreads available, we call save_songplay in a new thread to quickly return + if (class_exists("Thread", false)) { + debug_event('user.class.php', 'Calling save_mediaplay plugins in a new thread...', 5); + $thread = new scrobbler_async($GLOBALS['user'], $media); + if ($thread->start()) { + //$thread->join(); + } else { + debug_event('user.class.php', 'Error when starting the thread.', 1); + } } else { - debug_event('user.class.php', 'Error when starting the thread.', '1'); + User::save_mediaplay($GLOBALS['user'], $media); } } else { - User::save_songplay($GLOBALS['user'], $song_info); + debug_event('user.class.php', 'Scrobbling explicitly skipped', 5); } - // Do this last so the 'last played checks are correct' - Stats::insert('song', $song_id, $user, $agent); - Stats::insert('album', $song_info->album, $user, $agent); - Stats::insert('artist', $song_info->artist, $user, $agent); + $media->set_played($user, $agent, $location); return true; } // update_stats - public static function save_songplay($user, $song_info) + public static function save_mediaplay($user, $media) { - foreach (Plugin::get_plugins('save_songplay') as $plugin_name) { + foreach (Plugin::get_plugins('save_mediaplay') as $plugin_name) { try { $plugin = new Plugin($plugin_name); if ($plugin->load($user)) { - $plugin->_plugin->save_songplay($song_info); + $plugin->_plugin->save_mediaplay($media); } } catch (Exception $e) { debug_event('user.class.php', 'Stats plugin error: ' . $e->getMessage(), '1'); @@ -779,7 +918,7 @@ class User extends database_object * create * inserts a new user into ampache */ - public static function create($username, $fullname, $email, $website, $password, $access, $disabled = false) + public static function create($username, $fullname, $email, $website, $password, $access, $state = '', $city = '', $disabled = false) { $website = rtrim($website, "/"); $password = hash('sha256', $password); @@ -789,14 +928,32 @@ class User extends database_object $sql = "INSERT INTO `user` (`username`, `disabled`, " . "`fullname`, `email`, `password`, `access`, `create_date`"; $params = array($username, $disabled, $fullname, $email, $password, $access, time()); + if (!empty($website)) { $sql .= ", `website`"; $params[] = $website; } + if (!empty($state)) { + $sql .= ", `state`"; + $params[] = $state; + } + if (!empty($city)) { + $sql .= ", `city`"; + $params[] = $city; + } + $sql .= ") VALUES(?, ?, ?, ?, ?, ?, ?"; + if (!empty($website)) { $sql .= ", ?"; } + if (!empty($state)) { + $sql .= ", ?"; + } + if (!empty($city)) { + $sql .= ", ?"; + } + $sql .= ")"; $db_results = Dba::write($sql, $params); @@ -835,7 +992,7 @@ class User extends database_object * user for an admin, these should not be normally called when creating a * user object */ - public function format() + public function format($details = true) { /* If they have a last seen date */ if (!$this->last_seen) { $this->f_last_seen = T_('Never'); } else { $this->f_last_seen = date("m\/d\/Y - H:i",$this->last_seen); } @@ -843,26 +1000,31 @@ class User extends database_object /* If they have a create date */ if (!$this->create_date) { $this->f_create_date = T_('Unknown'); } else { $this->f_create_date = date("m\/d\/Y - H:i",$this->create_date); } + $this->f_name = ($this->fullname_public ? $this->fullname : $this->username); + // Base link - $this->f_link = '' . $this->fullname . ''; + $this->link = AmpConfig::get('web_path') . '/stats.php?action=show_user&user_id=' . $this->id; + $this->f_link = '' . $this->f_name . ''; - /* Calculate their total Bandwidth Usage */ - $sql = "SELECT `song`.`size` FROM `song` LEFT JOIN `object_count` ON `song`.`id`=`object_count`.`object_id` " . - "WHERE `object_count`.`user`='$this->id' AND `object_count`.`object_type`='song'"; - $db_results = Dba::read($sql); + if ($details) { + /* Calculate their total Bandwidth Usage */ + $sql = "SELECT `song`.`size` FROM `song` LEFT JOIN `object_count` ON `song`.`id`=`object_count`.`object_id` " . + "WHERE `object_count`.`user`='$this->id' AND `object_count`.`object_type`='song'"; + $db_results = Dba::read($sql); - $total = 0; - while ($r = Dba::fetch_assoc($db_results)) { - $total = $total + $r['size']; - } + $total = 0; + while ($r = Dba::fetch_assoc($db_results)) { + $total = $total + $r['size']; + } - $this->f_useage = UI::format_bytes($total); + $this->f_useage = UI::format_bytes($total); - /* Get Users Last ip */ - if (count($data = $this->get_ip_history(1))) { - $this->ip_history = inet_ntop($data['0']['ip']); - } else { - $this->ip_history = T_('Not Enough Data'); + /* Get Users Last ip */ + if (count($data = $this->get_ip_history(1))) { + $this->ip_history = inet_ntop($data['0']['ip']); + } else { + $this->ip_history = T_('Not Enough Data'); + } } $avatar = $this->get_avatar(); @@ -967,23 +1129,6 @@ class User extends database_object } } // while preferences - /* Let's also clean out any preferences garbage left over */ - $sql = "SELECT DISTINCT(user_preference.user) FROM user_preference " . - "LEFT JOIN user ON user_preference.user = user.id " . - "WHERE user_preference.user!='-1' AND user.id IS NULL"; - $db_results = Dba::read($sql); - - $results = array(); - - while ($r = Dba::fetch_assoc($db_results)) { - $results[] = $r['user']; - } - - foreach ($results as $data) { - $sql = "DELETE FROM user_preference WHERE user='$data'"; - Dba::write($sql); - } - } // fix_preferences /** @@ -998,16 +1143,16 @@ class User extends database_object admin */ if ($this->has_access(100)) { - $sql = "SELECT `id` FROM `user` WHERE `access`='100' AND id !='" . Dba::escape($this->id) . "'"; - $db_results = Dba::read($sql); + $sql = "SELECT `id` FROM `user` WHERE `access`='100' AND id != ?"; + $db_results = Dba::read($sql, array($this->id)); if (!Dba::num_rows($db_results)) { return false; } } // if this is an admin check for others // Delete their playlists - $sql = "DELETE FROM `playlist` WHERE `user`='$this->id'"; - Dba::write($sql); + $sql = "DELETE FROM `playlist` WHERE `user` = ?"; + Dba::write($sql, array($this->id)); // Clean up the playlist data table $sql = "DELETE FROM `playlist_data` USING `playlist_data` " . @@ -1016,47 +1161,55 @@ class User extends database_object Dba::write($sql); // Delete any stats they have - $sql = "DELETE FROM `object_count` WHERE `user`='$this->id'"; - Dba::write($sql); + $sql = "DELETE FROM `object_count` WHERE `user` = ?"; + Dba::write($sql, array($this->id)); // Clear the IP history for this user - $sql = "DELETE FROM `ip_history` WHERE `user`='$this->id'"; - Dba::write($sql); + $sql = "DELETE FROM `ip_history` WHERE `user` = ?"; + Dba::write($sql, array($this->id)); // Nuke any access lists that are specific to this user - $sql = "DELETE FROM `access_list` WHERE `user`='$this->id'"; - Dba::write($sql); + $sql = "DELETE FROM `access_list` WHERE `user` = ?"; + Dba::write($sql, array($this->id)); // Delete their ratings - $sql = "DELETE FROM `rating` WHERE `user`='$this->id'"; - Dba::write($sql); + $sql = "DELETE FROM `rating` WHERE `user` = ?"; + Dba::write($sql, array($this->id)); // Delete their tags - $sql = "DELETE FROM `tag_map` WHERE `user`='$this->id'"; - Dba::write($sql); + $sql = "DELETE FROM `tag_map` WHERE `user` = ?"; + Dba::write($sql, array($this->id)); // Clean out the tags $sql = "DELETE FROM `tags` USING `tag_map` LEFT JOIN `tag_map` ON tag_map.id=tags.map_id AND tag_map.id IS NULL"; Dba::write($sql); // Delete their preferences - $sql = "DELETE FROM `user_preference` WHERE `user`='$this->id'"; - Dba::write($sql); + $sql = "DELETE FROM `user_preference` WHERE `user` = ?"; + Dba::write($sql, array($this->id)); // Delete their voted stuff in democratic play - $sql = "DELETE FROM `user_vote` WHERE `user`='$this->id'"; - Dba::write($sql); + $sql = "DELETE FROM `user_vote` WHERE `user` = ?"; + Dba::write($sql, array($this->id)); // Delete their shoutbox posts - $sql = "DELETE FROM `user_shout` WHERE `user='$this->id'"; - Dba::write($sql); + $sql = "DELETE FROM `user_shout` WHERE `user` = ?"; + Dba::write($sql, array($this->id)); + + // Delete their private messages posts + $sql = "DELETE FROM `user_pvmsg` WHERE `from_user` = ? OR `to_user` = ?"; + Dba::write($sql, array($this->id, $this->id)); + + // Delete their following/followers + $sql = "DELETE FROM `user_follow` WHERE `user` = ? OR `follow_user` = ?"; + Dba::write($sql, array($this->id, $this->id)); // Delete the user itself - $sql = "DELETE FROM `user` WHERE `id`='$this->id'"; - Dba::write($sql); + $sql = "DELETE FROM `user` WHERE `id` = ?"; + Dba::write($sql, array($this->id)); - $sql = "DELETE FROM `session` WHERE `username`='" . Dba::escape($this->username) . "'"; - Dba::write($sql); + $sql = "DELETE FROM `session` WHERE `username` = ?"; + Dba::write($sql, array($this->username)); return true; @@ -1098,9 +1251,9 @@ class User extends database_object { if (!$type) { $type = 'song'; } - $sql = "SELECT * FROM `object_count` WHERE `object_type`='$type' AND `user`='$this->id' " . - "ORDER BY `date` DESC LIMIT $limit"; - $db_results = Dba::read($sql); + $sql = "SELECT * FROM `object_count` WHERE `object_type` = ? AND `user` = ? " . + "ORDER BY `date` DESC LIMIT " . $limit; + $db_results = Dba::read($sql, array($type, $this->id)); $results = array(); while ($row = Dba::fetch_assoc($db_results)) { @@ -1148,17 +1301,17 @@ class User extends database_object * get_avatar * Get the user avatar */ - public function get_avatar() + public function get_avatar($local = false) { $avatar = array(); $avatar['title'] = T_('User avatar'); $upavatar = new Art($this->id, 'user'); if ($upavatar->get_db()) { - $avatar['url'] = AmpConfig::get('web_path') . '/image.php?object_type=user&id=' . $this->id; + $avatar['url'] = ($local ? AmpConfig::get('local_web_path') : AmpConfig::get('web_path')) . '/image.php?object_type=user&object_id=' . $this->id; $avatar['url_mini'] = $avatar['url']; $avatar['url_medium'] = $avatar['url']; - $avatar['url'] .= '&thumb=3'; + $avatar['url'] .= '&thumb=4'; $avatar['url_mini'] .= '&thumb=5'; $avatar['url_medium'] .= '&thumb=3'; } else { @@ -1182,7 +1335,7 @@ class User extends database_object public function upload_avatar() { $upload = array(); - if (!empty($_FILES['avatar']['tmp_name'])) { + if (!empty($_FILES['avatar']['tmp_name']) && $_FILES['avatar']['size'] <= AmpConfig::get('max_upload_size')) { $path_info = pathinfo($_FILES['avatar']['name']); $upload['file'] = $_FILES['avatar']['tmp_name']; $upload['mime'] = 'image/' . $path_info['extension']; @@ -1214,6 +1367,23 @@ class User extends database_object } // activate_user + /** + * get_artists + * Get artists associated with the user + */ + public function get_artists() + { + $sql = "SELECT `id` FROM `artist` WHERE `user` = ?"; + $db_results = Dba::read($sql, array($this->id)); + + $results = array(); + while ($row = Dba::fetch_assoc($db_results)) { + $results[] = $row['id']; + } + + return $results; + } + /** * is_xmlrpc * checks to see if this is a valid xmlrpc user @@ -1232,6 +1402,108 @@ class User extends database_object } // is_xmlrpc + /** + * get_followers + * Get users following this user + * @return int[] + */ + public function get_followers() + { + $sql = "SELECT `user` FROM `user_follower` WHERE `follow_user` = ?"; + $db_results = Dba::read($sql, array($this->id)); + $results = array(); + while ($row = Dba::fetch_assoc($db_results)) { + $results[] = $row['user']; + } + return $results; + } + + /** + * get_following + * Get users followed by this user + * @return int[] + */ + public function get_following() + { + $sql = "SELECT `follow_user` FROM `user_follower` WHERE `user` = ?"; + $db_results = Dba::read($sql, array($this->id)); + $results = array(); + while ($row = Dba::fetch_assoc($db_results)) { + $results[] = $row['follow_user']; + } + return $results; + } + + /** + * is_followed_by + * Get if an user is followed by this user + * @param integer $user_id + * @return boolean + */ + public function is_followed_by($user_id) + { + $sql = "SELECT `id` FROM `user_follower` WHERE `user` = ? AND `follow_user` = ?"; + $db_results = Dba::read($sql, array($user_id, $this->id)); + return (Dba::num_rows($db_results) > 0); + } + + /** + * is_following + * Get if this user is following an user + * @param integer $user_id + * @return boolean + */ + public function is_following($user_id) + { + $sql = "SELECT `id` FROM `user_follower` WHERE `user` = ? AND `follow_user` = ?"; + $db_results = Dba::read($sql, array($this->id, $user_id)); + return (Dba::num_rows($db_results) > 0); + } + + /** + * toggle_follow + * @param integer $user_id + * @return boolean + */ + public function toggle_follow($user_id) + { + if (!$user_id || $user_id === $this->id) + return false; + + $params = array($this->id, $user_id); + if ($this->is_following($user_id)) { + $sql = "DELETE FROM `user_follower` WHERE `user` = ? AND `follow_user` = ?"; + } else { + $sql = "INSERT INTO `user_follower` (`user`, `follow_user`, `follow_date`) VALUES (?, ?, ?)"; + $params[] = time(); + } + + return Dba::write($sql, $params); + } + + /** + * get_display_follow + * Get html code to display the follow/unfollow link + * @param $display_user_id int|null + * @return string + */ + public function get_display_follow($user_id = null) + { + if (!$user_id) { + $user_id = $GLOBALS['user']->id; + } + + if ($user_id === $this->id) + return ""; + + $followed = $this->is_followed_by($user_id); + + $html = ""; + return $html; + } + /** * check_username * This checks to make sure the username passed doesn't already @@ -1258,17 +1530,49 @@ class User extends database_object */ public static function rebuild_all_preferences() { - $sql = "SELECT * FROM `user`"; + // Clean out any preferences garbage left over + $sql = "DELETE `user_preference`.* FROM `user_preference` " . + "LEFT JOIN `user` ON `user_preference`.`user` = `user`.`id` " . + "WHERE `user_preference`.`user` != -1 AND `user`.`id` IS NULL"; + Dba::write($sql); + + // Get only users who has less preferences than excepted + // otherwise it would have significant performance issue with large user database + $sql = "SELECT `user` FROM `user_preference` " . + "GROUP BY `user` HAVING COUNT(*) < (" . + "SELECT COUNT(`id`) FROM `preference` WHERE `catagory` != 'system')"; $db_results = Dba::read($sql); - - User::fix_preferences('-1'); - while ($row = Dba::fetch_assoc($db_results)) { - User::fix_preferences($row['id']); + User::fix_preferences($row['user']); } return true; } // rebuild_all_preferences + /** + * stream_control + * Check all stream control plugins + * @param array $media_ids + * @param User $user + * @return boolean + */ + public static function stream_control($media_ids, User $user = null) + { + if ($user == null) { + $user = $GLOBALS['user']; + } + + foreach (Plugin::get_plugins('stream_control') as $plugin_name) { + $plugin = new Plugin($plugin_name); + if ($plugin->load($user)) { + if (!$plugin->_plugin->stream_control($media_ids)) { + return false; + } + } + } + + return true; + } + } //end user class diff --git a/sources/lib/class/userflag.class.php b/sources/lib/class/userflag.class.php index fae0869..bb1535f 100644 --- a/sources/lib/class/userflag.class.php +++ b/sources/lib/class/userflag.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -23,7 +23,7 @@ /** * Userflag class * - * This user flag/unflag songs, albums and artists as favorite. + * This user flag/unflag songs, albums, artists, videos, tvshows, movies ... as favorite. * */ class Userflag extends database_object @@ -89,10 +89,21 @@ class Userflag extends database_object * * Remove userflag for items that no longer exist. */ - public static function gc() + public static function gc($object_type = null, $object_id = null) { - foreach (array('song', 'album', 'artist', 'video') as $object_type) { - Dba::write("DELETE FROM `user_flag` USING `user_flag` LEFT JOIN `$object_type` ON `$object_type`.`id` = `user_flag`.`object_id` WHERE `object_type` = '$object_type' AND `$object_type`.`id` IS NULL"); + $types = array('song', 'album', 'artist', 'video', 'tvshow', 'tvshow_season'); + + if ($object_type != null) { + if (in_array($object_type, $types)) { + $sql = "DELETE FROM `user_flag` WHERE `object_type` = ? AND `object_id` = ?"; + Dba::write($sql, array($object_type, $object_id)); + } else { + debug_event('userflag', 'Garbage collect on type `' . $object_type . '` is not supported.', 1); + } + } else { + foreach ($types as $type) { + Dba::write("DELETE FROM `user_flag` USING `user_flag` LEFT JOIN `$type` ON `$type`.`id` = `user_flag`.`object_id` WHERE `object_type` = '$type' AND `$type`.`id` IS NULL"); + } } } @@ -165,21 +176,36 @@ class Userflag extends database_object $user_id = $GLOBALS['user']->id; } $user_id = intval($user_id); - $type = Stats::validate_type($type); - $sql = "SELECT `object_id` as `id` FROM user_flag" . - " WHERE object_type = '" . $type . "' AND `user` = '" . $user_id . "'"; - if (AmpConfig::get('catalog_disable')) { - $sql .= " AND " . Catalog::get_enable_filter($type, '`object_id`'); + $sql = "SELECT `user_flag`.`object_id` as `id`, `user_flag`.`object_type` as `type`, `user_flag`.`user` as `user` FROM `user_flag`"; + if ($user_id <= 0) { + // Get latest only from user rights >= content manager + $sql .= " LEFT JOIN `user` ON `user`.`id` = `user_flag`.`user`" . + " WHERE `user`.`access` >= 50"; } - $sql .= " ORDER BY `date` DESC "; + if (!is_null($type)) { + if ($user_id <= 0) { + $sql .= " AND"; + } else { + $sql .= " WHERE"; + } + $type = Stats::validate_type($type); + $sql .= " `user_flag`.`object_type` = '" . $type . "'"; + if ($user_id > 0) { + $sql .= " AND `user_flag`.`user` = '" . $user_id . "'"; + } + if (AmpConfig::get('catalog_disable')) { + $sql .= " AND " . Catalog::get_enable_filter($type, '`object_id`'); + } + } + $sql .= " ORDER BY `user_flag`.`date` DESC "; return $sql; } /** * get_latest * Get the latest user flagged objects */ - public static function get_latest($type, $user_id=null, $count='', $offset='') + public static function get_latest($type=null, $user_id=null, $count='', $offset='') { if (!$count) { $count = AmpConfig::get('popular_threshold'); @@ -194,12 +220,16 @@ class Userflag extends database_object /* Select Top objects counting by # of rows */ $sql = self::get_latest_sql($type, $user_id); $sql .= "LIMIT $limit"; - $db_results = Dba::read($sql, array($type, $user_id)); + $db_results = Dba::read($sql); $results = array(); while ($row = Dba::fetch_assoc($db_results)) { - $results[] = $row['id']; + if (is_null($type)) { + $results[] = $row; + } else { + $results[] = $row['id']; + } } return $results; diff --git a/sources/lib/class/vainfo.class.php b/sources/lib/class/vainfo.class.php index c5c5ab3..2f5d361 100644 --- a/sources/lib/class/vainfo.class.php +++ b/sources/lib/class/vainfo.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -36,9 +36,10 @@ class vainfo public $type = ''; public $tags = array(); public $islocal; + public $gather_types = array(); protected $_raw = array(); - protected $_getID3 = ''; + protected $_getID3 = null; protected $_forcedSize = 0; protected $_file_encoding = ''; @@ -55,10 +56,11 @@ class vainfo * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function __construct($file, $encoding = null, $encoding_id3v1 = null, $encoding_id3v2 = null, $dir_pattern = '', $file_pattern ='', $islocal = true) + public function __construct($file, $gather_types = array(), $encoding = null, $encoding_id3v1 = null, $encoding_id3v2 = null, $dir_pattern = '', $file_pattern ='', $islocal = true) { $this->islocal = $islocal; $this->filename = $file; + $this->gather_types = $gather_types; $this->encoding = $encoding ?: AmpConfig::get('site_charset'); /* These are needed for the filename mojo */ @@ -84,6 +86,7 @@ class vainfo $this->_getID3->option_extra_info = true; $this->_getID3->option_tag_lyrics3 = true; $this->_getID3->option_tags_process = true; + $this->_getID3->option_tag_apetag = true; $this->_getID3->encoding = $this->encoding; // get id3tag encoding (try to work around off-spec id3v1 tags) @@ -92,6 +95,7 @@ class vainfo } catch (Exception $error) { debug_event('getID3', "Broken file detected: $file: " . $error->getMessage(), 1); $this->_broken = true; + return false; } @@ -128,7 +132,7 @@ class vainfo } $this->encoding_id3v2 = self::_detect_encoding($tags, $mb_order); - $this->_getID3->encoding_id3v2 = $this->encoding_id3v2; + $this->_getID3->encoding = $this->encoding_id3v2; } $this->_getID3->encoding_id3v1 = $this->encoding_id3v1; @@ -148,33 +152,40 @@ class vainfo */ private static function _detect_encoding($tags, $mb_order) { - if (function_exists('mb_detect_encoding')) { - $encodings = array(); - if (is_array($tags)) { - foreach ($tags as $tag) { - $encodings[mb_detect_encoding($tag, $mb_order, true)]++; - } - } + if (!function_exists('mb_detect_encoding')) + return 'ISO-8859-1'; - debug_event('vainfo', 'encoding detection: ' . json_encode($encodings), 5); - $high = 0; - $encoding = ''; - foreach ($encodings as $key => $value) { - if ($value > $high) { - $encoding = $key; - $high = $value; - } + $encodings = array(); + if (is_array($tags)) { + foreach ($tags as $tag) { + if (is_array($tag)) + $tag = implode(" ", $tag); + $enc = mb_detect_encoding($tag, $mb_order, true); + if ($enc != false) + $encodings[$enc]++; } + } else { + $enc = mb_detect_encoding($tags, $mb_order, true); + if ($enc != false) + $encodings[$enc]++; + } - if ($encoding != 'ASCII' && $encoding != '0') { - return $encoding; - } else { - return 'ISO-8859-1'; + //!!debug_event('vainfo', 'encoding detection: ' . json_encode($encodings), 5); + $high = 0; + $encoding = 'ISO-8859-1'; + foreach ($encodings as $key => $value) { + if ($value > $high) { + $encoding = $key; + $high = $value; } } - return 'ISO-8859-1'; - } + if ($encoding != 'ASCII') { + return (string) $encoding; + } else { + return 'ISO-8859-1'; + } + } /** * get_info @@ -187,6 +198,7 @@ class vainfo // time, just return their rotting carcass of a media file. if ($this->_broken) { $this->tags = $this->set_broken(); + return true; } @@ -201,7 +213,7 @@ class vainfo /* Figure out what type of file we are dealing with */ $this->type = $this->_get_type(); - $enabled_sources = (array) AmpConfig::get('metadata_order'); + $enabled_sources = (array) $this->get_metadata_order(); if (in_array('filename', $enabled_sources)) { $this->tags['filename'] = $this->_parse_filename($this->filename); @@ -212,9 +224,69 @@ class vainfo } $this->_get_plugin_tags(); - } // get_info + /* + * write_id3 + * This function runs the various steps to gathering the metadata + */ + public function write_id3($data) + { + // Get the Raw file information + $this->read_id3(); + if (isset($this->_raw['tags']['id3v2'])) { + getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'write.php', __FILE__, true); + $tagWriter = new getid3_writetags(); + $tagWriter->filename = $this->filename; + //'id3v2.4' doesn't saves the year; + $tagWriter->tagformats = array('id3v1', 'id3v2.3'); + $tagWriter->overwrite_tags = true; + $tagWriter->remove_other_tags = true; + $tagWriter->tag_encoding = 'UTF-8'; + $TagData = $this->_raw['tags']['id3v2']; + + // Foreach what we've got + foreach ($data as $key=>$value) { + if ($key != 'APIC') { + $TagData[$key][0] = $value; + } + } + + if (isset($data['APIC'])) { + $TagData['attached_picture'][0]['data'] = $data['APIC']['data']; + $TagData['attached_picture'][0]['picturetypeid'] = '3'; + $TagData['attached_picture'][0]['description'] = 'Cover'; + $TagData['attached_picture'][0]['mime'] = $data['APIC']['mime']; + } + + $tagWriter->tag_data = $TagData; + + if ($tagWriter->WriteTags()) { + if (!empty($tagWriter->warnings)) { + debug_event('vainfo' , 'FWarnings ' . implode("\n", $tagWriter->warnings), 5); + } + } else { + debug_event('vainfo' , 'Failed to write tags! ' . implode("\n", $tagWriter->errors), 5); + } + } + } // write_id3 + + /** + * read_id3 + * This function runs the various steps to gathering the metadata + */ + public function read_id3() + { + // Get the Raw file information + try { + $this->_raw = $this->_getID3->analyze($this->filename); + + return $this->_raw; + } catch (Exception $e) { + debug_event('vainfo', "Unable to read file:" . $e->getMessage(), '1'); + } + } // read_id3 + /** * get_tag_type * @@ -261,6 +333,7 @@ class vainfo public static function clean_tag_info($results, $keys, $filename = null) { $info = array(); + //debug_event('vainfo', 'Clean tag info: ' . print_r($results, true), '5'); $info['file'] = $filename; @@ -279,6 +352,8 @@ class vainfo $info['time'] = $info['time'] ?: intval($tags['time']); $info['channels'] = $info['channels'] ?: $tags['channels']; + // This because video title are almost always bad... + $info['original_name'] = $info['original_name'] ?: stripslashes(trim($tags['original_name'])); $info['title'] = $info['title'] ?: stripslashes(trim($tags['title'])); $info['year'] = $info['year'] ?: intval($tags['year']); @@ -288,39 +363,53 @@ class vainfo $info['totaldisks'] = $info['totaldisks'] ?: intval($tags['totaldisks']); $info['artist'] = $info['artist'] ?: trim($tags['artist']); + $info['albumartist'] = $info['albumartist'] ?: trim($tags['albumartist']); $info['album'] = $info['album'] ?: trim($tags['album']); - // multiple genre support - if ((!$info['genre']) && $tags['genre']) { - if (!is_array($tags['genre'])) { - // not all tag formats will return an array, but we need one - $info['genre'][] = trim($tags['genre']); - } else { - // if we trim the array we lose everything after 1st entry - foreach ($tags['genre'] as $genre) { - $info['genre'][] = trim($genre); - } - } - } + $info['band'] = $info['band'] ?: trim($tags['band']); + $info['composer'] = $info['composer'] ?: trim($tags['composer']); + $info['publisher'] = $info['publisher'] ?: trim($tags['publisher']); + + $info['genre'] = self::clean_array_tag('genre', $info, $tags); $info['mb_trackid'] = $info['mb_trackid'] ?: trim($tags['mb_trackid']); $info['mb_albumid'] = $info['mb_albumid'] ?: trim($tags['mb_albumid']); + $info['mb_albumid_group'] = $info['mb_albumid_group'] ?: trim($tags['mb_albumid_group']); $info['mb_artistid'] = $info['mb_artistid'] ?: trim($tags['mb_artistid']); + $info['mb_albumartistid'] = $info['mb_albumartistid'] ?: trim($tags['mb_albumartistid']); + $info['release_type'] = $info['release_type'] ?: trim($tags['release_type']); $info['language'] = $info['language'] ?: trim($tags['language']); + $info['comment'] = $info['comment'] ?: trim($tags['comment']); $info['lyrics'] = $info['lyrics'] - ?: str_replace( - array("\r\n","\r","\n"), - '
', - strip_tags($tags['lyrics'])); + ?: strip_tags(nl2br($tags['lyrics']), "
"); + $info['replaygain_track_gain'] = $info['replaygain_track_gain'] ?: floatval($tags['replaygain_track_gain']); + $info['replaygain_track_peak'] = $info['replaygain_track_peak'] ?: floatval($tags['replaygain_track_peak']); + $info['replaygain_album_gain'] = $info['replaygain_album_gain'] ?: floatval($tags['replaygain_album_gain']); + $info['replaygain_album_peak'] = $info['replaygain_album_peak'] ?: floatval($tags['replaygain_album_peak']); $info['track'] = $info['track'] ?: intval($tags['track']); $info['resolution_x'] = $info['resolution_x'] ?: intval($tags['resolution_x']); $info['resolution_y'] = $info['resolution_y'] ?: intval($tags['resolution_y']); + $info['display_x'] = $info['display_x'] ?: intval($tags['display_x']); + $info['display_y'] = $info['display_y'] ?: intval($tags['display_y']); + $info['frame_rate'] = $info['frame_rate'] ?: floatval($tags['frame_rate']); + $info['video_bitrate'] = $info['video_bitrate'] ?: intval($tags['video_bitrate']); $info['audio_codec'] = $info['audio_codec'] ?: trim($tags['audio_codec']); $info['video_codec'] = $info['video_codec'] ?: trim($tags['video_codec']); + $info['description'] = $info['description'] ?: trim($tags['description']); + + $info['tvshow'] = $info['tvshow'] ?: trim($tags['tvshow']); + $info['tvshow_year'] = $info['tvshow_year'] ?: trim($tags['tvshow_year']); + $info['tvshow_season'] = $info['tvshow_season'] ?: trim($tags['tvshow_season']); + $info['tvshow_episode'] = $info['tvshow_episode'] ?: trim($tags['tvshow_episode']); + $info['release_date'] = $info['release_date'] ?: trim($tags['release_date']); + + $info['tvshow_art'] = $info['tvshow_art'] ?: trim($tags['tvshow_art']); + $info['tvshow_season_art'] = $info['tvshow_season_art'] ?: trim($tags['tvshow_season_art']); + $info['art'] = $info['art'] ?: trim($tags['art']); } // Some things set the disk number even though there aren't multiple @@ -332,6 +421,25 @@ class vainfo return $info; } + private static function clean_array_tag($field, $info, $tags) + { + $arr = array(); + if ((!$info[$field] || count($info[$field]) == 0) && $tags[$field]) { + if (!is_array($tags[$field])) { + // not all tag formats will return an array, but we need one + $arr[] = trim($tags[$field]); + } else { + foreach ($tags[$field] as $genre) { + $arr[] = trim($genre); + } + } + } else { + $arr = $info[$field]; + } + + return $arr; + } + /** * _get_type * @@ -370,7 +478,6 @@ class vainfo // The tags can come in many different shapes and colors // depending on the encoding time of day and phase of the moon. - if (is_array($this->_raw['tags'])) { foreach ($this->_raw['tags'] as $key => $tag_array) { switch ($key) { @@ -435,6 +542,20 @@ class vainfo return $cleaned; } + private function get_metadata_order_key() + { + if (!in_array('music', $this->gather_types)) { + return 'metadata_order_video'; + } + + return 'metadata_order'; + } + + private function get_metadata_order() + { + return (array) AmpConfig::get($this->get_metadata_order_key()); + } + /** * _get_plugin_tags * @@ -442,7 +563,7 @@ class vainfo */ private function _get_plugin_tags() { - $tag_order = AmpConfig::get('metadata_order'); + $tag_order = $this->get_metadata_order(); if (!is_array($tag_order)) { $tag_order = array($tag_order); } @@ -451,8 +572,11 @@ class vainfo foreach ($tag_order as $tag_source) { if (in_array($tag_source, $plugin_names)) { $plugin = new Plugin($tag_source); - if ($plugin->load($GLOBALS['user'])) { - $this->tags[$tag_source] = $plugin->_plugin->get_metadata(self::clean_tag_info($this->tags, self::get_tag_type($this->tags), $this->filename)); + $installed_version = Plugin::get_plugin_version($plugin->_plugin->name); + if ($installed_version) { + if ($plugin->load($GLOBALS['user'])) { + $this->tags[$tag_source] = $plugin->_plugin->get_metadata($this->gather_types, self::clean_tag_info($this->tags, self::get_tag_type($this->tags, $this->get_metadata_order_key()), $this->filename)); + } } } } @@ -476,18 +600,42 @@ class vainfo $parsed['bitrate'] = $tags['audio']['bitrate']; $parsed['channels'] = intval($tags['audio']['channels']); $parsed['rate'] = intval($tags['audio']['sample_rate']); - $parsed['size'] = $this->_forcedSize ?: intval($tags['filesize']); + $parsed['size'] = $this->_forcedSize ?: $tags['filesize']; $parsed['encoding'] = $tags['encoding']; $parsed['mime'] = $tags['mime_type']; $parsed['time'] = ($this->_forcedSize ? ((($this->_forcedSize - $tags['avdataoffset']) * 8) / $tags['bitrate']) : $tags['playtime_seconds']); - $parsed['video_codec'] = $tags['video']['fourcc']; $parsed['audio_codec'] = $tags['audio']['dataformat']; + $parsed['video_codec'] = $tags['video']['dataformat']; $parsed['resolution_x'] = $tags['video']['resolution_x']; $parsed['resolution_y'] = $tags['video']['resolution_y']; + $parsed['display_x'] = $tags['video']['display_x']; + $parsed['display_y'] = $tags['video']['display_y']; + $parsed['frame_rate'] = $tags['video']['frame_rate']; + $parsed['video_bitrate'] = $tags['video']['bitrate']; + + if (isset($tags['ape'])) { + if (isset($tags['ape']['items'])) { + foreach ($tags['ape']['items'] as $key => $tag) { + switch (strtolower($key)) { + case 'replaygain_track_gain': + case 'replaygain_track_peak': + case 'replaygain_album_gain': + case 'replaygain_album_peak': + $parsed[$key] = floatval($tag['data'][0]); + break; + } + } + } + } return $parsed; } + private function trimAscii($string) + { + return preg_replace('/[\x00-\x1F\x80-\xFF]/', '', trim($string)); + } + /** * _clean_type * This standardizes the type that we are given into a recognized type. @@ -513,6 +661,7 @@ class vainfo default: /* Log the fact that we couldn't figure it out */ debug_event('vainfo','Unable to determine file type from ' . $type . ' on file ' . $this->filename,'5'); + return $type; } } @@ -526,11 +675,29 @@ class vainfo { $parsed = array(); foreach ($tags as $tagname => $data) { - switch ($tagname) { + switch (strtolower($tagname)) { case 'genre': // Pass the array through - $parsed[$tagname] = $data; - break; + $parsed[$tagname] = $this->parseGenres($data); + break; + case 'musicbrainz_artistid': + $parsed['mb_artistid'] = $data[0]; + break; + case 'musicbrainz_albumid': + $parsed['mb_albumid'] = $data[0]; + break; + case 'musicbrainz_albumartistid': + $parsed['mb_albumartistid'] = $data[0]; + break; + case 'musicbrainz_releasegroupid': + $parsed['mb_albumid_group'] = $data[0]; + break; + case 'musicbrainz_trackid': + $parsed['mb_trackid'] = $data[0]; + break; + case 'musicbrainz_albumtype': + $parsed['release_type'] = $data[0]; + break; default: $parsed[$tagname] = $data[0]; break; @@ -550,11 +717,12 @@ class vainfo $parsed = array(); foreach ($tags as $tag => $data) { - if ($tag == 'unsynchedlyrics' || $tag == 'unsynchronised lyric') { + if ($tag == 'unsyncedlyrics' || $tag == 'unsynced lyrics' || $tag == 'unsynchronised lyric') { $tag = 'lyrics'; } $parsed[$tag] = $data[0]; } + return $parsed; } @@ -568,11 +736,11 @@ class vainfo $parsed = array(); foreach ($tags as $tag => $data) { - switch ($tag) { + switch (strtolower($tag)) { case 'genre': // Pass the array through - $parsed[$tag] = $data; - break; + $parsed[$tag] = $this->parseGenres($data); + break; case 'tracknumber': $parsed['track'] = $data[0]; break; @@ -584,6 +752,29 @@ class vainfo case 'date': $parsed['year'] = $data[0]; break; + case 'musicbrainz_artistid': + $parsed['mb_artistid'] = $data[0]; + break; + case 'musicbrainz_albumid': + $parsed['mb_albumid'] = $data[0]; + break; + case 'musicbrainz_albumartistid': + $parsed['mb_albumartistid'] = $data[0]; + break; + case 'musicbrainz_releasegroupid': + $parsed['mb_albumid_group'] = $data[0]; + break; + case 'musicbrainz_trackid': + $parsed['mb_trackid'] = $data[0]; + break; + case 'musicbrainz_albumtype': + $parsed['release_type'] = $data[0]; + break; + case 'unsyncedlyrics': + case 'unsynced lyrics': + case 'lyrics': + $parsed['lyrics'] = $data[0]; + break; default: $parsed[$tag] = $data[0]; break; @@ -621,12 +812,10 @@ class vainfo $parsed = array(); foreach ($tags as $tag => $data) { - switch ($tag) { case 'genre': - // Pass the array through - $parsed['genre'] = $data; - break; + $parsed['genre'] = $this->parseGenres($data); + break; case 'part_of_a_set': $elements = explode('/', $data[0]); $parsed['disk'] = $elements[0]; @@ -638,6 +827,9 @@ class vainfo case 'comments': $parsed['comment'] = $data[0]; break; + case 'unsynchronised_lyric': + $parsed['lyrics'] = $data[0]; + break; default: $parsed[$tag] = $data[0]; break; @@ -657,47 +849,44 @@ class vainfo if (!empty($id3v2['TXXX'])) { // Find the MBIDs for the album and artist + // Use trimAscii to remove noise (see #225 and #438 issues). Is this a GetID3 bug? foreach ($id3v2['TXXX'] as $txxx) { - switch ($txxx['description']) { - case 'MusicBrainz Album Id': - $parsed['mb_albumid'] = $txxx['data']; + switch (strtolower($this->trimAscii($txxx['description']))) { + case 'musicbrainz album id': + $parsed['mb_albumid'] = $this->trimAscii($txxx['data']); break; - case 'MusicBrainz Artist Id': - $parsed['mb_artistid'] = $txxx['data']; + case 'musicbrainz release group id': + $parsed['mb_albumid_group'] = $this->trimAscii($txxx['data']); + break; + case 'musicbrainz artist id': + $parsed['mb_artistid'] = $this->trimAscii($txxx['data']); + break; + case 'musicbrainz album artist id': + $parsed['mb_albumartistid'] = $this->trimAscii($txxx['data']); + break; + case 'musicbrainz album type': + $parsed['release_type'] = $this->trimAscii($txxx['data']); + break; + case 'catalognumber': + $parsed['catalog_number'] = $this->trimAscii($txxx['data']); + break; + case 'replaygain_track_gain': + $parsed['replaygain_track_gain'] = floatval($txxx['data']); + break; + case 'replaygain_track_peak': + $parsed['replaygain_track_peak'] = floatval($txxx['data']); + break; + case 'replaygain_album_gain': + $parsed['replaygain_album_gain'] = floatval($txxx['data']); + break; + case 'replaygain_album_peak': + $parsed['replaygain_album_peak'] = floatval($txxx['data']); break; } } } } - // Find all genre - if (!empty($id3v2['TCON'])) { - // Find the MBID for the track - foreach ($id3v2['TCON'] as $tcid) { - if ($tcid['framenameshort'] == "genre") { - // Removing unwanted UTF-8 charaters - $tcid['data'] = str_replace("\xFF", "", $tcid['data']); - $tcid['data'] = str_replace("\xFE", "", $tcid['data']); - - if (!empty($tcid['data'])) { - // Parsing string with the null character - $genres = explode("\0", $tcid['data']); - $parsed_genres = array(); - foreach ($genres as $g) { - if (strlen($g) > 2) { // Only allow tags with at least 3 characters - $parsed_genres[] = $g; - } - } - - if (count($parsed_genres)) { - $parsed['genre'] = $parsed_genres; - } - } - } - break; - } - } - // Find the rating if (is_array($id3v2['POPM'])) { foreach ($id3v2['POPM'] as $popm) { @@ -745,9 +934,9 @@ class vainfo foreach ($tags as $tag => $data) { switch ($tag) { case 'creation_date': + $parsed['release_date'] = strtotime(str_replace(" ", "", $data[0])); if (strlen($data['0']) > 4) { - // Weird date format, attempt to normalize it - $data[0] = date('Y', strtotime($data[0])); + $data[0] = date('Y', $parsed['release_date']); } $parsed['year'] = $data[0]; break; @@ -757,9 +946,36 @@ class vainfo case 'MusicBrainz Album Id': $parsed['mb_albumid'] = $data[0]; break; + case 'MusicBrainz Album Artist Id': + $parsed['mb_albumartistid'] = $data[0]; + break; + case 'MusicBrainz Release Group Id': + $parsed['mb_albumid_group'] = $data[0]; + break; case 'MusicBrainz Artist Id': $parsed['mb_artistid'] = $data[0]; break; + case 'MusicBrainz Album Type': + $parsed['release_type'] = $data[0]; + break; + case 'track_number': + $parsed['track'] = $data[0]; + break; + case 'disc_number': + $parsed['disk'] = $data[0]; + break; + case 'album_artist': + $parsed['albumartist'] = $data[0]; + break; + case 'tv_episode': + $parsed['tvshow_episode'] = $data[0]; + break; + case 'tv_season': + $parsed['tvshow_season'] = $data[0]; + break; + case 'tv_show_name': + $parsed['tvshow'] = $data[0]; + break; default: $parsed[$tag] = $data[0]; break; @@ -780,66 +996,214 @@ class vainfo $origin = $filename; $results = array(); - // Correctly detect the slash we need to use here - if (strpos($filename, '/') !== false) { - $slash_type = '/'; - $slash_type_preg = $slash_type; - } else { - $slash_type = '\\'; - $slash_type_preg = $slash_type . $slash_type; - } - - // Combine the patterns - $pattern = preg_quote($this->_dir_pattern) . $slash_type_preg . preg_quote($this->_file_pattern); - - // Remove first left directories from filename to match pattern - $cntslash = substr_count($pattern, $slash_type) + 1; - $filepart = explode($slash_type, $filename); - if (count($filepart) > $cntslash) { - $filename = implode($slash_type, array_slice($filepart, count($filepart) - $cntslash)); - } - - // Pull out the pattern codes into an array - preg_match_all('/\%\w/', $pattern, $elements); - - // Mangle the pattern by turning the codes into regex captures - $pattern = preg_replace('/\%[Ty]/', '([0-9]+?)', $pattern); - $pattern = preg_replace('/\%\w/', '(.+?)', $pattern); - $pattern = str_replace('/', '\/', $pattern); - $pattern = str_replace(' ', '\s', $pattern); - $pattern = '/' . $pattern . '\..+$/'; - - // Pull out our actual matches - preg_match($pattern, $filename, $matches); - - if ($matches != null) { - // The first element is the full match text - $matched = array_shift($matches); - debug_event('vainfo', $pattern . ' matched ' . $matched . ' on ' . $filename, 5); - - // Iterate over what we found - foreach ($matches as $key => $value) { - $new_key = translate_pattern_code($elements['0'][$key]); - if ($new_key) { - $results[$new_key] = $value; - } + if (in_array('music', $this->gather_types) || in_array('clip', $this->gather_types)) { + // Correctly detect the slash we need to use here + if (strpos($filename, '/') !== false) { + $slash_type = '/'; + $slash_type_preg = $slash_type; + } else { + $slash_type = '\\'; + $slash_type_preg = $slash_type . $slash_type; } - $results['title'] = $results['title'] ?: basename($filename); - if ($this->islocal) { - $results['size'] = filesize(Core::conv_lc_file($origin)); + // Combine the patterns + $pattern = preg_quote($this->_dir_pattern) . $slash_type_preg . preg_quote($this->_file_pattern); + + // Remove first left directories from filename to match pattern + $cntslash = substr_count($pattern, preg_quote($slash_type)) + 1; + $filepart = explode($slash_type, $filename); + if (count($filepart) > $cntslash) { + $filename = implode($slash_type, array_slice($filepart, count($filepart) - $cntslash)); + } + + // Pull out the pattern codes into an array + preg_match_all('/\%\w/', $pattern, $elements); + + // Mangle the pattern by turning the codes into regex captures + $pattern = preg_replace('/\%[Ty]/', '([0-9]+?)', $pattern); + $pattern = preg_replace('/\%\w/', '(.+?)', $pattern); + $pattern = str_replace('/', '\/', $pattern); + $pattern = str_replace(' ', '\s', $pattern); + $pattern = '/' . $pattern . '\..+$/'; + + // Pull out our actual matches + preg_match($pattern, $filename, $matches); + if ($matches != null) { + // The first element is the full match text + $matched = array_shift($matches); + debug_event('vainfo', $pattern . ' matched ' . $matched . ' on ' . $filename, 5); + + // Iterate over what we found + foreach ($matches as $key => $value) { + $new_key = translate_pattern_code($elements['0'][$key]); + if ($new_key) { + $results[$new_key] = $value; + } + } + + $results['title'] = $results['title'] ?: basename($filename); + if ($this->islocal) { + $results['size'] = Core::get_filesize(Core::conv_lc_file($origin)); + } + } + } + + if (in_array('tvshow', $this->gather_types)) { + $pathinfo = pathinfo($filename); + $filetitle = $pathinfo['filename']; + + $results = array_merge($results, $this->parseEpisodeName($filetitle)); + if (!$results['tvshow']) { + // Try to identify the show information from parent folder + $filetitle = basename($pathinfo['dirname']); + $results = array_merge($results, $this->parseEpisodeName($filetitle)); + + if (!$results['tvshow']) { + if ($results['tvshow_season'] && $results['tvshow_episode']) { + // We have season and episode, we assume parent folder is the tvshow name + $pathinfo = pathinfo($pathinfo['dirname']); + $filetitle = basename($pathinfo['dirname']); + $results['tvshow'] = $this->fixSerieName($filetitle); + } else { + // Or we assume each parent folder contains one missing information + if (preg_match('/[\/\\\\]([^\/\\\\]*)[\/\\\\]Season (\d{1,2})[\/\\\\]((E|Ep|Episode)\s?(\d{1,2})[\/\\\\])?/i', $filename, $matches)) { + if ($matches != null) { + $results['tvshow'] = $this->fixSerieName($matches[1]); + $results['tvshow_season'] = $matches[2]; + if (isset($matches[5])) { + $results['tvshow_episode'] = $matches[5]; + } + } + } + } + } + } + } + + if (in_array('movie', $this->gather_types)) { + $pathinfo = pathinfo($filename); + $filetitle = $pathinfo['filename']; + $results['title'] = $this->fixVideoReleaseName($filetitle); + if (!$results['title']) { + // Try to identify the movie information from parent folder + $filetitle = basename($pathinfo['dirname']); + $results['title'] = $this->fixVideoReleaseName($filetitle); } } return $results; } + private function parseEpisodeName($filetitle) + { + $patterns = array( + '/(.*)s(\d\d)e(\d\d)(\D.*)/i', + '/(.*)s(\d\d)(\D)(.*)/i', + '/(.*)\D(\d{1,2})x(\d\d)(\D)(.*)/i', + '/(.*)\D(\d{1,2})x(\d\d)$/i', + '/(\D*)[\.|\-|_](\d)(\d\d)([\.|\-|_]\D.*)/i', + '/(\D*)(\d)[^0-9](\d\d)(\D.*)/i' + ); + + $results = array(); + for ($i=0;$ifixSerieName($matches[1]); + if (empty($name)) { + continue; + } + + $season = floatval($matches[2]); + if ($season == 0) { + continue; + } + + $episode = floatval($matches[3]); + $leftover = $matches[4]; + + if ($episode == 0) { + // Some malformed string + $leftover = $filetitle; + } + + $results['tvshow'] = $name; + $results['tvshow_season'] = $season; + $results['tvshow_episode'] = $episode; + $results['title'] = $this->fixVideoReleaseName($leftover); + break; + } + } + + return $results; + } + + private function fixSerieName($name) + { + $name = str_replace('_', ' ', $name); + $name = str_replace('.', ' ', $name); + $name = str_replace(' ', ' ', $name); + $name = $this->removeStartingDashesAndSpaces($name); + $name = $this->removeEndingDashesAndSpaces($name); + + return ucwords($name); + } + + private function fixVideoReleaseName($name) + { + $commonabbr = array( + 'divx', 'xvid', 'dvdrip', 'hdtv', 'lol', 'axxo', 'repack', 'xor', + 'pdtv', 'real', 'vtv', 'caph', '2hd', 'proper', 'fqm', 'uncut', + 'topaz', 'tvt', 'notv', 'fpn', 'fov', 'orenji', '0tv', 'omicron', + 'dsr', 'ws', 'sys', 'crimson', 'wat', 'hiqt', 'internal', 'brrip', + 'boheme', 'vost', 'vostfr', 'fastsub', 'addiction' + ); + for ($i=0; $ifixSerieName($name); + } + + private function removeStartingDashesAndSpaces($name) + { + if (empty($name)) { + return $name; + } + + while (strpos($name, ' ') === 0 || strpos($name, '-') === 0) { + $name = preg_replace('/^ /', '', $name); + $name = preg_replace('/^-/', '', $name); + } + + return $name; + } + + private function removeEndingDashesAndSpaces($name) + { + if (empty($name)) { + return $name; + } + + while (strrpos($name, ' ') === strlen($name) - 1 || strrpos($name, '-') === strlen($name) - 1) { + $name = preg_replace('/ $/', '', $name); + $name = preg_replace('/-$/', '', $name); + } + + return $name; + } + /** * set_broken * * This fills all tag types with Unknown (Broken) * - * @return array Return broken title, album, artist + * @return array Return broken title, album, artist */ public function set_broken() { @@ -859,7 +1223,28 @@ class vainfo $broken[$key]['artist'] = 'Unknown (Broken)'; return $broken; + } + // set_broken - } // set_broken + /** + * + * @param array $data + * @return array + * @throws Exception + */ + private function parseGenres($data) + { + // read additional id3v2 delimiters from config + $delimiters = AmpConfig::get('additional_genre_delimiters'); + if (isset($data) && is_array($data) && count($data) === 1 && isset($delimiters)) { + $pattern = '~[\s]?(' . $delimiters . ')[\s]?~'; + $genres = preg_split($pattern, reset($data)); + if ($genres === false) { + throw new Exception('Pattern given in additional_genre_delimiters is not functional. Please ensure is it a valid regex (delimiter ~).'); + } + $data = $genres; + } + return $data; + } } // end class vainfo diff --git a/sources/lib/class/video.class.php b/sources/lib/class/video.class.php index 55f10fe..e7b1593 100644 --- a/sources/lib/class/video.class.php +++ b/sources/lib/class/video.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -20,48 +20,211 @@ * */ -class Video extends database_object implements media +class Video extends database_object implements media, library_item { + /** + * @var int $id + */ public $id; + /** + * @var string $title + */ public $title; + /** + * @var boolean $played + */ + public $played; + /** + * @var boolean $enabled + */ public $enabled; + /** + * @var string $file + */ public $file; + /** + * @var int $size + */ public $size; + /** + * @var string $video_codec + */ public $video_codec; + /** + * @var string $audio_codec + */ public $audio_codec; + /** + * @var int $resolution_x + */ public $resolution_x; + /** + * @var int $resolution_y + */ public $resolution_y; + /** + * @var int $time + */ public $time; + /** + * @var string $mime + */ public $mime; + /** + * @var int $release_date + */ + public $release_date; + /** + * @var int $catalog + */ + public $catalog; + /** + * @var int $bitrate + */ + public $bitrate; + /** + * @var string $mode + */ + public $mode; + /** + * @var int $channels + */ + public $channels; + /** + * @var int $display_x + */ + public $display_x; + /** + * @var int $display_x + */ + public $display_y; + /** + * @var float $frame_rate + */ + public $frame_rate; + /** + * @var int $video_bitrate + */ + public $video_bitrate; + /** + * @var string $type + */ + public $type; + /** + * @var array $tags + */ public $tags; + /** + * @var string $f_title + */ public $f_title; + /** + * @var string $f_full_title + */ + public $f_full_title; + /** + * @var string $f_time + */ + public $f_time; + /** + * @var string $f_time_h + */ + public $f_time_h; + /** + * @var string $link + */ + public $link; + /** + * @var string $f_link + */ public $f_link; + /** + * @var string $f_codec + */ public $f_codec; + /** + * @var string $f_resolution + */ public $f_resolution; + /** + * @var string $f_display + */ + public $f_display; + /** + * @var string $f_bitrate + */ + public $f_bitrate; + /** + * @var string $f_video_bitrate + */ + public $f_video_bitrate; + /** + * @var string $f_frame_rate + */ + public $f_frame_rate; + /** + * @var string $f_tags + */ public $f_tags; + /** + * @var string $f_length + */ public $f_length; + /** + * @var string $f_file + */ + public $f_file; + /** + * @var string $f_release_date + */ + public $f_release_date; /** * Constructor - * This pulls the shoutbox information from the database and returns - * a constructed object, uses user_shout table + * This pulls the information from the database and returns + * a constructed object + * @param int $id */ public function __construct($id) { // Load the data from the database - $info = $this->get_info($id); + $info = $this->get_info($id, 'video'); foreach ($info as $key=>$value) { $this->$key = $value; } + $data = pathinfo($this->file); + $this->type = strtolower($data['extension']); + return true; } // Constructor + /** + * Create a video strongly typed object from its id. + * @param int $video_id + * @return \Video + */ + public static function create_from_id($video_id) + { + $dtypes = self::get_derived_types(); + foreach ($dtypes as $dtype) { + $sql = "SELECT `id` FROM `" . strtolower($dtype) . "` WHERE `id` = ?"; + $db_results = Dba::read($sql, array($video_id)); + if ($results = Dba::fetch_assoc($db_results)) { + if ($results['id']) { + return new $dtype($video_id); + } + } + } + return new Video($video_id); + } + /** * build_cache * Build a cache based on the array of ids passed, saves lots of little queries + * @param int[] $ids */ public static function build_cache($ids=array()) { @@ -82,51 +245,790 @@ class Video extends database_object implements media * format * This formats a video object so that it is human readable */ - public function format() + public function format($details = true) { $this->f_title = scrub_out($this->title); - $this->f_link = scrub_out($this->title); + $this->f_full_title = $this->f_title; + $this->link = AmpConfig::get('web_path') . "/video.php?action=show_video&video_id=" . $this->id; + $this->f_link = "link . "\" title=\"" . scrub_out($this->f_title) . "\"> " . scrub_out($this->f_title) . ""; $this->f_codec = $this->video_codec . ' / ' . $this->audio_codec; - $this->f_resolution = $this->resolution_x . 'x' . $this->resolution_y; - $this->f_tags = ''; + if ($this->resolution_x || $this->resolution_y) { + $this->f_resolution = $this->resolution_x . 'x' . $this->resolution_y; + } + if ($this->display_x || $this->display_y) { + $this->f_display = $this->display_x . 'x' . $this->display_y; + } + + // Format the Bitrate + $this->f_bitrate = intval($this->bitrate/1000) . "-" . strtoupper($this->mode); + $this->f_video_bitrate = (string) intval($this->video_bitrate/1000); + if ($this->frame_rate) { + $this->f_frame_rate = $this->frame_rate . ' fps'; + } + + // Format the Time + $min = floor($this->time/60); + $sec = sprintf("%02d", ($this->time%60)); + $this->f_time = $min . ":" . $sec; + $hour = sprintf("%02d", floor($min/60)); + $min_h = sprintf("%02d", ($min%60)); + $this->f_time_h = $hour . ":" . $min_h . ":" . $sec; + + if ($details) { + // Get the top tags + $this->tags = Tag::get_top_tags('video', $this->id); + $this->f_tags = Tag::get_display($this->tags, true, 'video'); + } + $this->f_length = floor($this->time/60) . ' ' . T_('minutes'); + $this->f_file = $this->f_title . '.' . $this->type; + if ($this->release_date) { + $this->f_release_date = date('Y-m-d', $this->release_date); + } } // format - public function get_stream_types() + /** + * Get item keywords for metadata searches. + * @return array + */ + public function get_keywords() { - return array('native'); + $keywords = array(); + $keywords['title'] = array('important' => true, + 'label' => T_('Title'), + 'value' => $this->f_title); - } // native_stream + return $keywords; + } + + /** + * Get item fullname. + * @return string + */ + public function get_fullname() + { + return $this->f_title; + } + + /** + * Get parent item description. + * @return array|null + */ + public function get_parent() + { + return null; + } + + /** + * Get item childrens. + * @return array + */ + public function get_childrens() + { + return array(); + } + + /** + * Search for item childrens. + * @param string $name + * @return array + */ + public function search_childrens($name) + { + return array(); + } + + /** + * Get all childrens and sub-childrens medias. + * @param string $filter_type + * @return array + */ + public function get_medias($filter_type = null) + { + $medias = array(); + if (!$filter_type || $filter_type == 'video') { + $medias[] = array( + 'object_type' => 'video', + 'object_id' => $this->id + ); + } + return $medias; + } + + /** + * get_catalogs + * + * Get all catalog ids related to this item. + * @return int[] + */ + public function get_catalogs() + { + return array($this->catalog); + } + + /** + * Get item's owner. + * @return int|null + */ + public function get_user_owner() + { + return null; + } + + /** + * Get default art kind for this item. + * @return string + */ + public function get_default_art_kind() + { + return 'preview'; + } + + public function get_description() + { + return ''; + } + + public function display_art($thumb = 2) + { + if (Art::has_db($this->id, 'video')) { + Art::display('video', $this->id, $this->get_fullname(), $thumb, $this->link); + } + } + + /** + * gc + * + * Cleans up the inherited object tables + */ + public static function gc() + { + Movie::gc(); + TVShow_Episode::gc(); + TVShow_Season::gc(); + TVShow::gc(); + Personal_Video::gc(); + Clip::gc(); + } + + /** + * Get stream types. + * @return array + */ + public function get_stream_types($player = null) + { + return Song::get_stream_types_for_type($this->type, $player); + } /** * play_url * This returns a "PLAY" url for the video in question here, this currently feels a little * like a hack, might need to adjust it in the future + * @param int $oid + * @param string $additional_params + * @param string $player + * @param boolean $local + * @return string */ - public static function play_url($oid, $additional_params='',$sid='',$force_http='') + public static function play_url($oid, $additional_params='', $player=null, $local=false) { - $video = new Video($oid); + return Song::generic_play_url('video', $oid, $additional_params, $player, $local); + } - if (!$video->id) { return false; } - - $uid = intval($GLOBALS['user']->id); - $oid = intval($video->id); - - $url = Stream::get_base_url() . "type=video&uid=" . $uid . "&oid=" . $oid; - - return Stream_URL::format($url . $additional_params); - - } // play_url + /** + * Get stream name. + * @return string + */ + public function get_stream_name() + { + return $this->title; + } /** * get_transcode_settings - * - * FIXME: Video transcoding is not implemented + * @param string $target + * @param array $options + * @return array */ - public function get_transcode_settings($target = null) + public function get_transcode_settings($target = null, $player = null, $options=array()) { - return false; + return Song::get_transcode_settings_for_media($this->type, $target, $player, 'video', $options); } + /** + * Get derived video types. + * @return array + */ + private static function get_derived_types() + { + return array('TVShow_Episode', 'Movie', 'Clip', 'Personal_Video'); + } + + /** + * Validate video type. + * @param string $type + * @return string + */ + public static function validate_type($type) + { + $dtypes = self::get_derived_types(); + foreach ($dtypes as $dtype) { + if (strtolower($type) == strtolower($dtype)) + return $type; + } + + return 'Video'; + } + + /** + * type_to_mime + * + * Returns the mime type for the specified file extension/type + * @param string $type + * @return string + */ + public static function type_to_mime($type) + { + // FIXME: This should really be done the other way around. + // Store the mime type in the database, and provide a function + // to make it a human-friendly type. + switch ($type) { + case 'avi': + return 'video/avi'; + case 'ogg': + case 'ogv': + return 'application/ogg'; + case 'wmv': + return 'audio/x-ms-wmv'; + case 'mp4': + case 'm4v': + return 'video/mp4'; + case 'mkv': + return 'video/x-matroska'; + case 'mkv': + return 'video/x-matroska'; + case 'mov': + return 'video/quicktime'; + case 'divx': + return 'video/x-divx'; + case 'webm': + return 'video/webm'; + case 'flv': + return 'video/x-flv'; + case 'ts': + return 'video/mp2t'; + case 'mpg': + case 'mpeg': + case 'm2ts': + default: + return 'video/mpeg'; + } + } + + /** + * Insert new video. + * @param array $data + * @param array $gtypes + * @param array $options + * @return int + */ + public static function insert(array $data, $gtypes = array(), $options = array()) + { + $bitrate = intval($data['bitrate']); + $mode = $data['mode']; + $rezx = intval($data['resolution_x']); + $rezy = intval($data['resolution_y']); + $release_date = intval($data['release_date']); + // No release date, then release date = production year + if (!$release_date && $data['year']) { + $release_date = strtotime($data['year'] . '01-01'); + } + $tags = $data['genre']; + $channels = intval($data['channels']); + $disx = intval($data['display_x']); + $disy = intval($data['display_y']); + $frame_rate = floatval($data['frame_rate']); + $video_bitrate = intval($data['video_bitrate']); + + $sql = "INSERT INTO `video` (`file`,`catalog`,`title`,`video_codec`,`audio_codec`,`resolution_x`,`resolution_y`,`size`,`time`,`mime`,`release_date`,`addition_time`, `bitrate`, `mode`, `channels`, `display_x`, `display_y`, `frame_rate`, `video_bitrate`) " . + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + $params = array($data['file'], $data['catalog'], $data['title'], $data['video_codec'], $data['audio_codec'], $rezx, $rezy, $data['size'], $data['time'], $data['mime'], $release_date, time(), $bitrate, $mode, $channels, $disx, $disy, $frame_rate, $video_bitrate); + Dba::write($sql, $params); + $vid = Dba::insert_id(); + + if (is_array($tags)) { + foreach ($tags as $tag) { + $tag = trim($tag); + if (!empty($tag)) { + Tag::add('video', $vid, $tag, false); + } + } + } + + if ($data['art'] && $options['gather_art']) { + $art = new Art($vid, 'video'); + $art->insert_url($data['art']); + } + + $data['id'] = $vid; + return self::insert_video_type($data, $gtypes, $options); + } + + /** + * Insert video for derived type. + * @param array $data + * @param array $gtypes + * @param array $options + * @return int + */ + private static function insert_video_type(array $data, $gtypes, $options = array()) + { + if (count($gtypes) > 0) { + $gtype = $gtypes[0]; + switch ($gtype) { + case 'tvshow': + return TVShow_Episode::insert($data, $gtypes, $options); + case 'movie': + return Movie::insert($data, $gtypes, $options); + case 'clip': + return Clip::insert($data, $gtypes, $options); + case 'personal_video': + return Personal_Video::insert($data, $gtypes, $options); + default: + // Do nothing, video entry already created and no additional data for now + break; + } + } + + return $data['id']; + } + + /** + * update + * This takes a key'd array of data as input and updates a video entry + * @param array $data + * @return int + */ + public function update(array $data) + { + if (isset($data['release_date'])) { + $f_release_date = $data['release_date']; + $release_date = strtotime($f_release_date); + } else { + $release_date = $this->release_date; + } + $title = isset($data['title']) ? $data['title'] : $this->title; + + $sql = "UPDATE `video` SET `title` = ?, `release_date` = ? WHERE `id` = ?"; + Dba::write($sql, array($title, $release_date, $this->id)); + + if (isset($data['edit_tags'])) { + Tag::update_tag_list($data['edit_tags'], 'video', $this->id, true); + } + + $this->title = $title; + $this->release_date = $release_date; + + return $this->id; + + } // update + + /** + * Get release item art. + * @return array + */ + public function get_release_item_art() + { + return array('object_type' => 'video', + 'object_id' => $this->id + ); + } + + /* + * generate_preview + * Generate video preview image from a video file + * @param int $video_id + * @param boolean $overwrite + */ + public static function generate_preview($video_id, $overwrite = false) + { + if ($overwrite || !Art::has_db($video_id, 'video', 'preview')) { + $artp = new Art($video_id, 'video', 'preview'); + $video = new Video($video_id); + $image = Stream::get_image_preview($video); + $artp->insert($image, 'image/png'); + } + } + + /** + * get_random + * + * This returns a number of random videos. + * @param int $count + * @return int[] + */ + public static function get_random($count = 1) + { + $results = array(); + + if (!$count) { + $count = 1; + } + + $sql = "SELECT DISTINCT(`video`.`id`) FROM `video` "; + $where = "WHERE `video`.`enabled` = '1' "; + if (AmpConfig::get('catalog_disable')) { + $sql .= "LEFT JOIN `catalog` ON `catalog`.`id` = `video`.`catalog` "; + $where .= "AND `catalog`.`enabled` = '1' "; + } + + $sql .= $where; + $sql .= "ORDER BY RAND() LIMIT " . intval($count); + $db_results = Dba::read($sql); + + while ($row = Dba::fetch_assoc($db_results)) { + $results[] = $row['id']; + } + + return $results; + } + + /** + * set_played + * this checks to see if the current object has been played + * if not then it sets it to played. In any case it updates stats. + * @param int $user + * @param string $agent + * @param array $location + * @return boolean + */ + public function set_played($user, $agent, $location) + { + Stats::insert('video', $this->id, $user, $agent, $location); + + if ($this->played) { + return true; + } + + /* If it hasn't been played, set it! */ + Video::update_played(true, $this->id); + + return true; + + } // set_played + + /** + * get_subtitles + * Get existing subtitles list for this video + * @return array + */ + public function get_subtitles() + { + $subtitles = array(); + $pinfo = pathinfo($this->file); + $filter = $pinfo['dirname'] . DIRECTORY_SEPARATOR . $pinfo['filename'] . '*.srt'; + + foreach (glob($filter) as $srt) { + $psrt = explode('.', $srt); + $lang_code = '__'; + $lang_name = T_("Unknown"); + if (count($psrt) >= 2) { + $lang_code = $psrt[count($psrt) - 2]; + if (strlen($lang_code) == 2) { + $lang_name = $this->get_language_name($lang_code); + } + } + $subtitles[] = array( + 'file' => $pinfo['dirname'] . DIRECTORY_SEPARATOR . $srt, + 'lang_code' => $lang_code, + 'lang_name' => $lang_name + ); + } + + return $subtitles; + } + + /** + * Get language name from code. + * @param string $code + * @return string + */ + protected function get_language_name($code) + { + $languageCodes = array( + "aa" => T_("Afar"), + "ab" => T_("Abkhazian"), + "ae" => T_("Avestan"), + "af" => T_("Afrikaans"), + "ak" => T_("Akan"), + "am" => T_("Amharic"), + "an" => T_("Aragonese"), + "ar" => T_("Arabic"), + "as" => T_("Assamese"), + "av" => T_("Avaric"), + "ay" => T_("Aymara"), + "az" => T_("Azerbaijani"), + "ba" => T_("Bashkir"), + "be" => T_("Belarusian"), + "bg" => T_("Bulgarian"), + "bh" => T_("Bihari"), + "bi" => T_("Bislama"), + "bm" => T_("Bambara"), + "bn" => T_("Bengali"), + "bo" => T_("Tibetan"), + "br" => T_("Breton"), + "bs" => T_("Bosnian"), + "ca" => T_("Catalan"), + "ce" => T_("Chechen"), + "ch" => T_("Chamorro"), + "co" => T_("Corsican"), + "cr" => T_("Cree"), + "cs" => T_("Czech"), + "cu" => T_("Church Slavic"), + "cv" => T_("Chuvash"), + "cy" => T_("Welsh"), + "da" => T_("Danish"), + "de" => T_("German"), + "dv" => T_("Divehi"), + "dz" => T_("Dzongkha"), + "ee" => T_("Ewe"), + "el" => T_("Greek"), + "en" => T_("English"), + "eo" => T_("Esperanto"), + "es" => T_("Spanish"), + "et" => T_("Estonian"), + "eu" => T_("Basque"), + "fa" => T_("Persian"), + "ff" => T_("Fulah"), + "fi" => T_("Finnish"), + "fj" => T_("Fijian"), + "fo" => T_("Faroese"), + "fr" => T_("French"), + "fy" => T_("Western Frisian"), + "ga" => T_("Irish"), + "gd" => T_("Scottish Gaelic"), + "gl" => T_("Galician"), + "gn" => T_("Guarani"), + "gu" => T_("Gujarati"), + "gv" => T_("Manx"), + "ha" => T_("Hausa"), + "he" => T_("Hebrew"), + "hi" => T_("Hindi"), + "ho" => T_("Hiri Motu"), + "hr" => T_("Croatian"), + "ht" => T_("Haitian"), + "hu" => T_("Hungarian"), + "hy" => T_("Armenian"), + "hz" => T_("Herero"), + "ia" => T_("Interlingua (International Auxiliary Language Association)"), + "id" => T_("Indonesian"), + "ie" => T_("Interlingue"), + "ig" => T_("Igbo"), + "ii" => T_("Sichuan Yi"), + "ik" => T_("Inupiaq"), + "io" => T_("Ido"), + "is" => T_("Icelandic"), + "it" => T_("Italian"), + "iu" => T_("Inuktitut"), + "ja" => T_("Japanese"), + "jv" => T_("Javanese"), + "ka" => T_("Georgian"), + "kg" => T_("Kongo"), + "ki" => T_("Kikuyu"), + "kj" => T_("Kwanyama"), + "kk" => T_("Kazakh"), + "kl" => T_("Kalaallisut"), + "km" => T_("Khmer"), + "kn" => T_("Kannada"), + "ko" => T_("Korean"), + "kr" => T_("Kanuri"), + "ks" => T_("Kashmiri"), + "ku" => T_("Kurdish"), + "kv" => T_("Komi"), + "kw" => T_("Cornish"), + "ky" => T_("Kirghiz"), + "la" => T_("Latin"), + "lb" => T_("Luxembourgish"), + "lg" => T_("Ganda"), + "li" => T_("Limburgish"), + "ln" => T_("Lingala"), + "lo" => T_("Lao"), + "lt" => T_("Lithuanian"), + "lu" => T_("Luba-Katanga"), + "lv" => T_("Latvian"), + "mg" => T_("Malagasy"), + "mh" => T_("Marshallese"), + "mi" => T_("Maori"), + "mk" => T_("Macedonian"), + "ml" => T_("Malayalam"), + "mn" => T_("Mongolian"), + "mr" => T_("Marathi"), + "ms" => T_("Malay"), + "mt" => T_("Maltese"), + "my" => T_("Burmese"), + "na" => T_("Nauru"), + "nb" => T_("Norwegian Bokmal"), + "nd" => T_("North Ndebele"), + "ne" => T_("Nepali"), + "ng" => T_("Ndonga"), + "nl" => T_("Dutch"), + "nn" => T_("Norwegian Nynorsk"), + "no" => T_("Norwegian"), + "nr" => T_("South Ndebele"), + "nv" => T_("Navajo"), + "ny" => T_("Chichewa"), + "oc" => T_("Occitan"), + "oj" => T_("Ojibwa"), + "om" => T_("Oromo"), + "or" => T_("Oriya"), + "os" => T_("Ossetian"), + "pa" => T_("Panjabi"), + "pi" => T_("Pali"), + "pl" => T_("Polish"), + "ps" => T_("Pashto"), + "pt" => T_("Portuguese"), + "qu" => T_("Quechua"), + "rm" => T_("Raeto-Romance"), + "rn" => T_("Kirundi"), + "ro" => T_("Romanian"), + "ru" => T_("Russian"), + "rw" => T_("Kinyarwanda"), + "sa" => T_("Sanskrit"), + "sc" => T_("Sardinian"), + "sd" => T_("Sindhi"), + "se" => T_("Northern Sami"), + "sg" => T_("Sango"), + "si" => T_("Sinhala"), + "sk" => T_("Slovak"), + "sl" => T_("Slovenian"), + "sm" => T_("Samoan"), + "sn" => T_("Shona"), + "so" => T_("Somali"), + "sq" => T_("Albanian"), + "sr" => T_("Serbian"), + "ss" => T_("Swati"), + "st" => T_("Southern Sotho"), + "su" => T_("Sundanese"), + "sv" => T_("Swedish"), + "sw" => T_("Swahili"), + "ta" => T_("Tamil"), + "te" => T_("Telugu"), + "tg" => T_("Tajik"), + "th" => T_("Thai"), + "ti" => T_("Tigrinya"), + "tk" => T_("Turkmen"), + "tl" => T_("Tagalog"), + "tn" => T_("Tswana"), + "to" => T_("Tonga"), + "tr" => T_("Turkish"), + "ts" => T_("Tsonga"), + "tt" => T_("Tatar"), + "tw" => T_("Twi"), + "ty" => T_("Tahitian"), + "ug" => T_("Uighur"), + "uk" => T_("Ukrainian"), + "ur" => T_("Urdu"), + "uz" => T_("Uzbek"), + "ve" => T_("Venda"), + "vi" => T_("Vietnamese"), + "vo" => T_("Volapuk"), + "wa" => T_("Walloon"), + "wo" => T_("Wolof"), + "xh" => T_("Xhosa"), + "yi" => T_("Yiddish"), + "yo" => T_("Yoruba"), + "za" => T_("Zhuang"), + "zh" => T_("Chinese"), + "zu" => T_("Zulu") + ); + + return $languageCodes[$code]; + } + + /** + * Get subtitle file from language code. + * @param string $lang_code + * @return string + */ + public function get_subtitle_file($lang_code) + { + $subtitle = ''; + if ($lang_code == '__' || $this->get_language_name($lang_code)) { + $pinfo = pathinfo($this->file); + $subtitle = $pinfo['dirname'] . DIRECTORY_SEPARATOR . $pinfo['filename']; + if ($lang_code != '__') { + $subtitle .= '.' . $lang_code; + } + $subtitle .= '.srt'; + } + + return $subtitle; + } + + /** + * Remove the video from disk. + */ + public function remove_from_disk() + { + if (file_exists($this->file)) { + $deleted = unlink($this->file); + } else { + $deleted = true; + } + if ($deleted === true) { + $sql = "DELETE FROM `video` WHERE `id` = ?"; + $deleted = Dba::write($sql, array($this->id)); + if ($deleted) { + Art::gc('video', $this->id); + Userflag::gc('video', $this->id); + Rating::gc('video', $this->id); + Shoutbox::gc('video', $this->id); + } + } else { + debug_event('video', 'Cannot delete ' . $this->file . 'file. Please check permissions.', 1); + } + + return $deleted; + } + + /** + * update_played + * sets the played flag + * @param boolean $new_played + * @param int $song_id + */ + public static function update_played($new_played, $song_id) + { + self::_update_item('played', ($new_played ? 1 : 0),$song_id,'25'); + + } // update_played + + /** + * _update_item + * This is a private function that should only be called from within the video class. + * It takes a field, value video id and level. first and foremost it checks the level + * against $GLOBALS['user'] to make sure they are allowed to update this record + * it then updates it and sets $this->{$field} to the new value + * @param string $field + * @param mixed $value + * @param int $song_id + * @param int $level + * @return boolean + */ + private static function _update_item($field, $value, $song_id, $level) + { + /* Check them Rights! */ + if (!Access::check('interface',$level)) { return false; } + + /* Can't update to blank */ + if (!strlen(trim($value))) { return false; } + + $sql = "UPDATE `video` SET `$field` = ? WHERE `id` = ?"; + Dba::write($sql, array($value, $song_id)); + + return true; + + } // _update_item + } // end Video class diff --git a/sources/lib/class/wanted.class.php b/sources/lib/class/wanted.class.php index d18520d..0fd0127 100644 --- a/sources/lib/class/wanted.class.php +++ b/sources/lib/class/wanted.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -22,27 +22,74 @@ use MusicBrainz\MusicBrainz; use MusicBrainz\Clients\RequestsMbClient; +use MusicBrainz\Filters\ArtistFilter; class Wanted extends database_object { /* Variables from DB */ + + /** + * @var int $id + */ public $id; + /** + * @var string $mbid + */ public $mbid; + /** + * @var int $artist + */ public $artist; + /** + * @var string $artist_mbid + */ public $artist_mbid; + /** + * @var string $name + */ public $name; + /** + * @var string $year + */ public $year; + /** + * @var boolean $accepted + */ public $accepted; + /** + * @var string $release_mbid + */ public $release_mbid; + /** + * @var int $user + */ public $user; - public $f_name_link; + /** + * @var string $link + */ + public $link; + + /** + * @var string $f_link + */ + public $f_link; + /** + * @var string $f_artist_link + */ public $f_artist_link; + /** + * @var string $f_user + */ public $f_user; + /** + * @var array $songs + */ public $songs; /** * Constructor + * @param int $id */ public function __construct($id=0) { @@ -62,6 +109,9 @@ class Wanted extends database_object /** * get_missing_albums * Get list of library's missing albums from MusicBrainz + * @param Artist|null $artist + * @param string $mbid + * @return array */ public static function get_missing_albums($artist, $mbid='') { @@ -83,62 +133,66 @@ class Wanted extends database_object $albums = $artist->get_albums(); foreach ($albums as $id) { $album = new Album($id); - if ($album->mbid) { - $malbum = $mb->lookup('release', $album->mbid, array('release-groups')); - if ($malbum->{'release-group'}) { - if (!in_array($malbum->{'release-group'}->id, $owngroups)) { - $owngroups[] = $malbum->{'release-group'}->id; + if (trim($album->mbid_group)) { + $owngroups[] = $album->mbid_group; + } else { + if (trim($album->mbid)) { + $malbum = $mb->lookup('release', $album->mbid, array('release-groups')); + if ($malbum['release-group']) { + if (!in_array($malbum['release-group']['id'], $owngroups)) { + $owngroups[] = $malbum['release-group']['id']; + } } } } } } else { $wartist['mbid'] = $mbid; - $wartist['name'] = $martist->name; + $wartist['name'] = $martist['name']; parent::add_to_cache('missing_artist', $mbid, $wartist); $wartist = self::get_missing_artist($mbid); } $results = array(); - foreach ($martist->{'release-groups'} as $group) { - if (in_array(strtolower($group->{'primary-type'}), $types)) { + foreach ($martist['release-groups'] as $group) { + if (in_array(strtolower($group['primary-type']), $types)) { $add = true; - for ($i = 0; $i < count($group->{'secondary-types'}) && $add; ++$i) { - $add = in_array(strtolower($group->{'secondary-types'}[$i]), $types); + for ($i = 0; $i < count($group['secondary-types']) && $add; ++$i) { + $add = in_array(strtolower($group['secondary-types'][$i]), $types); } if ($add) { - if (!in_array($group->id, $owngroups)) { - $wantedid = self::get_wanted($group->id); + if (!in_array($group['id'], $owngroups)) { + $wantedid = self::get_wanted($group['id']); $wanted = new Wanted($wantedid); if ($wanted->id) { $wanted->format(); } else { - $wanted->mbid = $group->id; + $wanted->mbid = $group['id']; if ($artist) { $wanted->artist = $artist->id; } else { $wanted->artist_mbid = $mbid; } - $wanted->name = $group->title; - if (!empty($group->{'first-release-date'})) { - if (strlen($group->{'first-release-date'}) == 4) { - $wanted->year = $group->{'first-release-date'}; + $wanted->name = $group['title']; + if (!empty($group['first-release-date'])) { + if (strlen($group['first-release-date']) == 4) { + $wanted->year = $group['first-release-date']; } else { - $wanted->year = date("Y", strtotime($group->{'first-release-date'})); + $wanted->year = date("Y", strtotime($group['first-release-date'])); } } $wanted->accepted = false; - $wanted->f_name_link = "id; + $wanted->link = AmpConfig::get('web_path') . "/albums.php?action=show_missing&mbid=" . $group['id']; if ($artist) { - $wanted->f_name_link .= "&artist=" . $wanted->artist; + $wanted->link .= "&artist=" . $wanted->artist; } else { - $wanted->f_name_link .= "&artist_mbid=" . $mbid; + $wanted->link .= "&artist_mbid=" . $mbid; } - $wanted->f_name_link .= "\" title=\"" . $wanted->name . "\">" . $wanted->name . ""; - $wanted->f_artist_link = $artist ? $artist->f_name_link : $wartist['link']; - $wanted->f_user = $GLOBALS['user']->fullname; + $wanted->f_link = "link . "\" title=\"" . $wanted->name . "\">" . $wanted->name . ""; + $wanted->f_artist_link = $artist ? $artist->f_link : $wartist['link']; + $wanted->f_user = $GLOBALS['user']->f_name; } $results[] = $wanted; } @@ -149,6 +203,11 @@ class Wanted extends database_object return $results; } // get_missing_albums + /** + * Get missing artist data. + * @param string $mbid + * @return array + */ public static function get_missing_artist($mbid) { $wartist = array(); @@ -166,7 +225,7 @@ class Wanted extends database_object return $wartist; } - $wartist['name'] = $martist->name; + $wartist['name'] = $martist['name']; parent::add_to_cache('missing_artist', $mbid, $wartist); } @@ -175,6 +234,28 @@ class Wanted extends database_object return $wartist; } + public static function search_missing_artists($name) + { + $args = array( + 'artist' => $name + ); + $filter = new ArtistFilter($args); + $mb = new MusicBrainz(new RequestsMbClient()); + $res = $mb->search($filter); + $wartists = array(); + foreach ($res as $r) { + $wartists[] = array( + 'mbid' => $r->id, + 'name' => $r->name, + ); + } + return $wartists; + } + + /** + * Get accepted wanted release count. + * @return int + */ public static function get_accepted_wanted_count() { $sql = "SELECT COUNT(`id`) AS `wanted_cnt` FROM `wanted` WHERE `accepted` = 1"; @@ -186,6 +267,11 @@ class Wanted extends database_object return 0; } + /** + * Get wanted release by mbid. + * @param string $mbid + * @return int + */ public static function get_wanted($mbid) { $sql = "SELECT `id` FROM `wanted` WHERE `mbid` = ?"; @@ -194,9 +280,13 @@ class Wanted extends database_object return $row['id']; } - return false; + return 0; } + /** + * Delete wanted release. + * @param string $mbid + */ public static function delete_wanted($mbid) { $sql = "DELETE FROM `wanted` WHERE `mbid` = ?"; @@ -209,17 +299,27 @@ class Wanted extends database_object Dba::write($sql, $params); } + /** + * Delete a wanted release by mbid. + * @param string $mbid + */ public static function delete_wanted_release($mbid) { if (self::get_accepted_wanted_count() > 0) { $mb = new MusicBrainz(new RequestsMbClient()); $malbum = $mb->lookup('release', $mbid, array('release-groups')); - if ($malbum->{'release-group'}) { - self::delete_wanted($malbum->{'release-group'}->id); + if ($malbum['release-group']) { + self::delete_wanted($malbum['release-group']); } } } + /** + * Delete a wanted release by name. + * @param int $artist + * @param string $album_name + * @param int $year + */ public static function delete_wanted_by_name($artist, $album_name, $year) { $sql = "DELETE FROM `wanted` WHERE `artist` = ? AND `name` = ? AND `year` = ?"; @@ -232,12 +332,15 @@ class Wanted extends database_object Dba::write($sql, $params); } + /** + * Accept a wanted request. + */ public function accept() { if ($GLOBALS['user']->has_access('75')) { $sql = "UPDATE `wanted` SET `accepted` = '1' WHERE `mbid` = ?"; Dba::write($sql, array( $this->mbid )); - $this->accepted = 1; + $this->accepted = true; foreach (Plugin::get_plugins('process_wanted') as $plugin_name) { debug_event('wanted', 'Using Wanted Process plugin: ' . $plugin_name, '5'); @@ -249,6 +352,12 @@ class Wanted extends database_object } } + /** + * Check if a release mbid is already marked as wanted + * @param string $mbid + * @param int $userid + * @return boolean + */ public static function has_wanted($mbid, $userid = 0) { if ($userid == 0) { @@ -266,6 +375,14 @@ class Wanted extends database_object } + /** + * Add a new wanted release. + * @param string $mbid + * @param int $artist + * @param string $artist_mbid + * @param string $name + * @param int $year + */ public static function add_wanted($mbid, $artist, $artist_mbid, $name, $year) { $sql = "INSERT INTO `wanted` (`user`, `artist`, `artist_mbid`, `mbid`, `name`, `year`, `date`, `accepted`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; @@ -277,9 +394,14 @@ class Wanted extends database_object $wantedid = Dba::insert_id(); $wanted = new Wanted($wantedid); $wanted->accept(); + + database_object::remove_from_cache('wanted', $wantedid); } } + /** + * Show action buttons. + */ public function show_action_buttons() { if ($this->id) { @@ -296,6 +418,10 @@ class Wanted extends database_object } } + /** + * Load wanted release data. + * @param boolean $track_details + */ public function load_all($track_details = true) { $mb = new MusicBrainz(new RequestsMbClient()); @@ -304,70 +430,51 @@ class Wanted extends database_object try { $group = $mb->lookup('release-group', $this->mbid, array( 'releases' )); // Set fresh data - $this->name = $group->title; - $this->year = date("Y", strtotime($group->{'first-release-date'})); + $this->name = $group['title']; + $this->year = date("Y", strtotime($group['first-release-date'])); // Load from database if already cached $this->songs = Song_preview::get_song_previews($this->mbid); - - if (count($group->releases) > 0) { - $this->release_mbid = $group->releases[0]->id; + if (count($group['releases']) > 0) { + $this->release_mbid = $group['releases'][0]['id']; if ($track_details && count($this->songs) == 0) { // Use the first release as reference for track content $release = $mb->lookup('release', $this->release_mbid, array( 'recordings' )); - foreach ($release->media as $media) { - foreach ($media->tracks as $track) { + foreach ($release['media'] as $media) { + foreach ($media['tracks'] as $track) { $song = array(); - $song['disk'] = $media->position; - $song['track'] = $track->number; - $song['title'] = $track->title; - $song['mbid'] = $track->id; + $song['disk'] = $media['position']; + $song['track'] = $track['number']; + $song['title'] = $track['title']; + $song['mbid'] = $track['id']; if ($this->artist) { $song['artist'] = $this->artist; } $song['artist_mbid'] = $this->artist_mbid; $song['session'] = session_id(); $song['album_mbid'] = $this->mbid; - if (AmpConfig::get('echonest_api_key')) { - $echonest = new EchoNest_Client(new EchoNest_HttpClient_Requests()); - $echonest->authenticate(AmpConfig::get('echonest_api_key')); - $enSong = null; - try { - $enProfile = $echonest->getTrackApi()->profile('musicbrainz:track:' . $track->id); - $enSong = $echonest->getSongApi()->profile($enProfile['song_id'], array( 'id:7digital-US', 'audio_summary', 'tracks')); - } catch (Exception $e) { - debug_event('echonest', 'EchoNest track error on `' . $track->id . '` (' . $track->title . '): ' . $e->getMessage(), '1'); - } - // Wans't able to get the song with MusicBrainz ID, try a search - if ($enSong == null) { - if ($this->artist) { - $artist = new Artist($this->artist); - $artist_name = $artist->name; - } else { - $wartist = Wanted::get_missing_artist($this->artist_mbid); - $artist_name = $wartist['name']; - } - try { - $enSong = $echonest->getSongApi()->search(array( - 'results' => '1', - 'artist' => $artist_name, - 'title' => $track->title, - 'bucket' => array( 'id:7digital-US', 'audio_summary', 'tracks'), - )); + if ($this->artist) { + $artist = new Artist($this->artist); + $artist_name = $artist->name; + } else { + $wartist = Wanted::get_missing_artist($this->artist_mbid); + $artist_name = $wartist['name']; + } - - } catch (Exception $e) { - debug_event('echonest', 'EchoNest song search error: ' . $e->getMessage(), '1'); - } - } - - if ($enSong != null) { - $song['file'] = $enSong[0]['tracks'][0]['preview_url']; - debug_event('echonest', 'EchoNest `' . $track->title . '` preview: ' . $song['file'], '1'); + $song['file'] = null; + foreach (Plugin::get_plugins('get_song_preview') as $plugin_name) { + $plugin = new Plugin($plugin_name); + if ($plugin->load($GLOBALS['user'])) { + $song['file'] = $plugin->_plugin->get_song_preview($track['id'], $artist_name, $track['title']); + if ($song['file'] != null) + break; } } - $this->songs[] = new Song_Preview(Song_preview::insert($song)); + + if ($song != null) { + $this->songs[] = new Song_Preview(Song_preview::insert($song)); + } } } } @@ -382,22 +489,31 @@ class Wanted extends database_object } } + /** + * Format data. + */ public function format() { if ($this->artist) { $artist = new Artist($this->artist); $artist->format(); - $this->f_artist_link = $artist->f_name_link; + $this->f_artist_link = $artist->f_link; } else { $wartist = Wanted::get_missing_artist($this->artist_mbid); $this->f_artist_link = $wartist['link']; } - $this->f_name_link = "mbid . "&artist=" . $this->artist . "&artist_mbid=" . $this->artist_mbid . "\" title=\"" . $this->name . "\">" . $this->name . ""; + $this->link = AmpConfig::get('web_path') . "/albums.php?action=show_missing&mbid=" . $this->mbid . "&artist=" . $this->artist . "&artist_mbid=" . $this->artist_mbid . "\" title=\"" . $this->name; + $this->f_link = "link . "\">" . $this->name . ""; $user = new User($this->user); - $this->f_user = $user->fullname; + $user->format(); + $this->f_user = $user->f_name; } + /** + * Get wanted list sql. + * @return string + */ public static function get_wanted_list_sql() { $sql = "SELECT `id` FROM `wanted` "; @@ -409,6 +525,10 @@ class Wanted extends database_object return $sql; } + /** + * Get wanted list. + * @return int[] + */ public static function get_wanted_list() { $sql = self::get_wanted_list_sql(); diff --git a/sources/lib/class/waveform.class.php b/sources/lib/class/waveform.class.php index 93d3a91..bf0ac7f 100644 --- a/sources/lib/class/waveform.class.php +++ b/sources/lib/class/waveform.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -65,6 +65,11 @@ class Waveform } // Constructor + /** + * Get a song waveform. + * @param int $song_id + * @return binary|string|null + */ public static function get($song_id) { $song = new Song($song_id); @@ -107,6 +112,8 @@ class Waveform fclose($fp); fclose($tfp); + Stream::kill_process($transcoder); + $waveform = self::create_waveform($tmpfile); //$waveform = self::create_waveform("C:\\tmp\\test.wav"); @@ -143,6 +150,8 @@ class Waveform /** * Great function slightly modified as posted by Minux at * http://forums.clantemplates.com/showthread.php?t=133805 + * @param string $input + * @return array */ protected static function html2rgb($input) { @@ -154,8 +163,17 @@ class Waveform ); } + /** + * Create waveform from song file. + * @param string $filename + * @return binary|string|null + */ protected static function create_waveform($filename) { + if (!file_exists($filename)) { + return null; + } + $detail = 5; $width = 400; $height = 32; @@ -194,7 +212,7 @@ class Waveform // start putting together the initial canvas // $data_size = (size_of_file - header_bytes_read) / skipped_bytes + 1 - $data_size = floor((filesize($filename) - 44) / ($ratio + $byte) + 1); + $data_size = floor((Core::get_filesize($filename) - 44) / ($ratio + $byte) + 1); $data_point = 0; // create original image width based on amount of detail @@ -293,6 +311,12 @@ class Waveform return $imgdata; } + /** + * Save waveform to db. + * @param int $song_id + * @param binary|string $waveform + * @return boolean + */ protected static function save_to_db($song_id, $waveform) { $sql = "UPDATE `song_data` SET `waveform` = ? WHERE `song_id` = ?"; diff --git a/sources/lib/class/webdav_auth.class.php b/sources/lib/class/webdav_auth.class.php new file mode 100644 index 0000000..a5342b5 --- /dev/null +++ b/sources/lib/class/webdav_auth.class.php @@ -0,0 +1,38 @@ +catalog_id = $catalog_id; + } + + public function getChildren() + { + $children = array(); + $catalogs = null; + if ($this->catalog_id > 0) { + $catalogs = array(); + $catalogs[] = $this->catalog_id; + } + $artists = Catalog::get_artists($catalogs); + foreach ($artists as $artist) { + $children[] = new WebDAV_Directory($artist); + } + + return $children; + } + + public function getChild($name) + { + debug_event('webdav', 'Catalog getChild for `' . $name . '`', 5); + $matches = Catalog::search_childrens($name, $this->catalog_id); + debug_event('webdav', 'Found ' . count($matches) . ' childs.', 5); + // Always return first match + // Warning: this means that two items with the same name will not be supported for now + if (count($matches) > 0) + return WebDAV_Directory::getChildFromArray($matches[0]); + + throw new DAV\Exception\NotFound('The artist with name: ' . $name . ' could not be found'); + } + + public function childExists($name) + { + $matches = Catalog::search_childrens($name, $this->catalog_id); + return (count($matches) > 0); + } + + public function getName() + { + if ($this->catalog_id > 0) { + $catalog = Catalog::create_from_id($this->catalog_id); + return $catalog->name; + } + + return AmpConfig::get('site_title'); + } +} diff --git a/sources/lib/class/webdav_directory.class.php b/sources/lib/class/webdav_directory.class.php new file mode 100644 index 0000000..0acfab4 --- /dev/null +++ b/sources/lib/class/webdav_directory.class.php @@ -0,0 +1,102 @@ +libitem = $libitem; + $this->libitem->format(); + } + + public function getChildren() + { + debug_event('webdav', 'Directory getChildren', 5); + $children = array(); + $childs = $this->libitem->get_childrens(); + foreach ($childs as $key => $child) { + if (is_string($key)) { + foreach ($child as $schild) { + $children[] = WebDAV_Directory::getChildFromArray($schild); + } + } else { + $children[] = WebDAV_Directory::getChildFromArray($child); + } + } + return $children; + } + + public function getChild($name) + { + // Clean song name + if (strtolower(get_class($this->libitem)) === "album") { + $splitname = explode('-', $name, 3); + $name = trim($splitname[count($splitname) - 1]); + $nameinfo = pathinfo($name); + $name = $nameinfo['filename']; + } + debug_event('webdav', 'Directory getChild: ' . $name, 5); + $matches = $this->libitem->search_childrens($name); + // Always return first match + // Warning: this means that two items with the same name will not be supported for now + if (count($matches) > 0) + return WebDAV_Directory::getChildFromArray($matches[0]); + + throw new DAV\Exception\NotFound('The child with name: ' . $name . ' could not be found');; + } + + public static function getChildFromArray($array) + { + $libitem = new $array['object_type']($array['object_id']); + if (!$libitem->id) { + throw new DAV\Exception\NotFound('The library item `' . $array['object_type'] . '` with id `' . $array['object_id'] . '` could not be found'); + } + + if ($libitem instanceof media) { + return new WebDAV_File($libitem); + } else { + return new WebDAV_Directory($libitem); + } + } + + public function childExists($name) + { + $matches = $this->libitem->search_childrens($name); + return (count($matches) > 0); + } + + public function getName() + { + return $this->libitem->get_fullname(); + } +} diff --git a/sources/lib/class/webdav_file.class.php b/sources/lib/class/webdav_file.class.php new file mode 100644 index 0000000..fff2962 --- /dev/null +++ b/sources/lib/class/webdav_file.class.php @@ -0,0 +1,74 @@ +libitem = $libitem; + $this->libitem->format(); + } + + public function getName() + { + return $this->libitem->f_file; + } + + public function get() + { + debug_event('webdav', 'File get', 5); + // Only media associated to a local catalog is supported + if ($this->libitem->catalog) { + $catalog = Catalog::create_from_id($this->libitem->catalog); + if ($catalog->get_type() === 'local') { + return fopen($this->libitem->file, 'r'); + } else { + debug_event('webdav', 'Catalog associated to the media is not local. This is currently unsupported.', 3); + } + } else { + debug_event('webdav', 'No catalog associated to the media.', 3); + } + + return null; + } + + public function getSize() + { + return $this->libitem->size; + } + + public function getETag() + { + return md5(get_class($this->libitem) . "_" . $this->libitem->id . "_" . $this->libitem->update_time); + } +} diff --git a/sources/lib/class/webplayer.class.php b/sources/lib/class/webplayer.class.php index 04f2c13..10e7be4 100644 --- a/sources/lib/class/webplayer.class.php +++ b/sources/lib/class/webplayer.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -22,6 +22,11 @@ class WebPlayer { + /** + * Check if the playlist is a radio playlist. + * @param \Playlist $playlist + * @return boolean + */ public static function is_playlist_radio($playlist) { $radios = array(); @@ -35,87 +40,66 @@ class WebPlayer return (count($playlist->urls) == 1 && count($radios) > 0 && AmpConfig::get('webplayer_flash')); } + /** + * Check if the playlist is a video playlist. + * @param \Playlist $playlist + * @return boolean + */ public static function is_playlist_video($playlist) { return (count($playlist->urls) > 0 && $playlist->urls[0]->type == "video"); } - public static function browser_info($agent=null) - { - // Declare known browsers to look for - $known = array('msie', 'trident', 'firefox', 'safari', 'webkit', 'opera', 'netscape', 'konqueror', 'gecko'); - - // Clean up agent and build regex that matches phrases for known browsers - // (e.g. "Firefox/2.0" or "MSIE 6.0" (This only matches the major and minor - // version numbers. E.g. "2.0.0.6" is parsed as simply "2.0" - $agent = strtolower($agent ? $agent : $_SERVER['HTTP_USER_AGENT']); - $pattern = '#(?' . join('|', $known) . ')[/ ]+(?[0-9]+(?:\.[0-9]+)?)#'; - - // Find all phrases (or return empty array if none found) - if (!preg_match_all($pattern, $agent, $matches)) return array(); - - // Since some UAs have more than one phrase (e.g Firefox has a Gecko phrase, - // Opera 7,8 have a MSIE phrase), use the last one found (the right-most one - // in the UA). That's usually the most correct. - $i = count($matches['browser'])-1; - return array($matches['browser'][$i] => $matches['version'][$i]); - - } - + /** + * Get types information for an item. + * @param \playable_item $item + * @param string $force_type + * @return array + */ protected static function get_types($item, $force_type='') { $types = array('real' => 'mp3', 'player' => ''); - $browsers = array_keys(self::browser_info()); - $browser = ''; - if (count($browsers) > 0 ) { - $browser = $browsers[0]; - } - - if (!empty($force_type)) { - debug_event("webplayer.class.php", "Forcing type to {".$force_type."}", 5); - $types['real'] = $force_type; - } else { - if ($browser == "msie" || $browser == "trident" || $browser == "webkit" || $browser == "safari") { - $types['real'] = "mp3"; - } else { - $types['real'] = "ogg"; - } - } - - $song = null; + $media = null; $urlinfo = Stream_URL::parse($item->url); - if ($urlinfo['id'] && $urlinfo['type'] == 'song') { - $song = new Song($urlinfo['id']); + if ($urlinfo['id'] && Core::is_media($urlinfo['type'])) { + $media = new $urlinfo['type']($urlinfo['id']); } else if ($urlinfo['id'] && $urlinfo['type'] == 'song_preview') { - $song = new Song_Preview($urlinfo['id']); + $media = new Song_Preview($urlinfo['id']); } else if (isset($urlinfo['demo_id'])) { $democratic = new Democratic($urlinfo['demo_id']); if ($democratic->id) { $song_id = $democratic->get_next_object(); if ($song_id) { - $song = new Song($song_id); + $media = new Song($song_id); } } } - if ($song != null) { - $ftype = $song->type; + if ($media != null) { + $ftype = $media->type; $transcode = false; $transcode_cfg = AmpConfig::get('transcode'); // Check transcode is required - $ftype_transcode = AmpConfig::get('transcode_' . $ftype); - $valid_types = Song::get_stream_types_for_type($ftype); - if ($transcode_cfg == 'always' || !empty($force_type) || $ftype_transcode == 'required' || ($types['real'] != $ftype && !AmpConfig::get('webplayer_flash'))) { + $valid_types = Song::get_stream_types_for_type($ftype, 'webplayer'); + if ($transcode_cfg == 'always' || !empty($force_type) || !in_array('native', $valid_types) || ($types['real'] != $ftype && (!AmpConfig::get('webplayer_flash') || $urlinfo['type'] != 'song'))) { if ($transcode_cfg == 'always' || ($transcode_cfg != 'never' && in_array('transcode', $valid_types))) { - // Transcode only if excepted type available - $transcode_settings = $song->get_transcode_settings($types['real']); - if ($transcode_settings && AmpConfig::get('transcode_player_customize')) { - $transcode = true; - } else { + // Transcode forced from client side + if (!empty($force_type) && AmpConfig::get('transcode_player_customize')) { + debug_event("webplayer.class.php", "Forcing type to {".$force_type."}", 5); + // Transcode only if excepted type available + $transcode_settings = $media->get_transcode_settings($force_type, 'webplayer'); + if ($transcode_settings) { + $types['real'] = $transcode_settings['format']; + $transcode = true; + } + } + + // Transcode is not forced, transcode only if required + if (!$transcode) { if (!in_array('native', $valid_types)) { - $transcode_settings = $song->get_transcode_settings(null); + $transcode_settings = $media->get_transcode_settings(null, 'webplayer'); if ($transcode_settings) { $types['real'] = $transcode_settings['format']; $transcode = true; @@ -128,18 +112,18 @@ class WebPlayer if (!$transcode) { $types['real'] = $ftype; } - if ($types['real'] == "flac" || $types['real'] == "ogg") $types['player'] = "oga"; - else if ($types['real'] == "mp4") $types['player'] = "m4a"; - } else if ($urlinfo['id'] && $urlinfo['type'] == 'video') { - $video = new Video($urlinfo['id']); - $types['real'] = pathinfo($video->file, PATHINFO_EXTENSION); - if ($types['real'] == "ogg") $types['player'] = "ogv"; - else if ($types['real'] == "webm") $types['player'] = "webmv"; - else if ($types['real'] == "mp4") $types['player'] = "m4v"; - } else if ($item->type == 'radio') { + if ($urlinfo['type'] == 'song') { + if ($types['real'] == "ogg") $types['player'] = "oga"; + else if ($types['real'] == "mp4") $types['player'] = "m4a"; + } else if ($urlinfo['type'] == 'video') { + if ($types['real'] == "ogg") $types['player'] = "ogv"; + else if ($types['real'] == "webm") $types['player'] = "webmv"; + else if ($types['real'] == "mp4") $types['player'] = "m4v"; + } + } else if ($item->type == 'live_stream') { $types['real'] = $item->codec; - if ($types['real'] == "flac" || $types['real'] == "ogg") $types['player'] = "oga"; + if ($types['real'] == "ogg") $types['player'] = "oga"; } else { $ext = pathinfo($item->url, PATHINFO_EXTENSION); if (!empty($ext)) $types['real'] = $ext; @@ -151,6 +135,11 @@ class WebPlayer return $types; } + /** + * Get all supplied types for a playlist. + * @param \Playlist $playlist + * @return array + */ public static function get_supplied_types($playlist) { $jptypes = array(); @@ -168,6 +157,12 @@ class WebPlayer return $jptypes; } + /** + * Get add_media javascript. + * @param \Playlist $playlist + * @param string $callback_container + * @return string + */ public static function add_media_js($playlist, $callback_container='') { $addjs = ""; @@ -183,6 +178,33 @@ class WebPlayer return $addjs; } + /** + * Get play_next javascript. + * @param \Playlist $playlist + * @param string $callback_container + * @return string + */ + public static function play_next_js($playlist, $callback_container='') + { + $addjs = ""; + foreach ($playlist->urls as $item) { + if ($item->type == 'broadcast') { + $addjs .= $callback_container . "startBroadcastListening('" . $item->url . "');"; + break; + } else { + $addjs .= $callback_container . "playNext(" . self::get_media_js_param($item) . ");"; + } + } + + return $addjs; + } + + /** + * Get media javascript parameters. + * @param \playable_item $item + * @param string $force_type + * @return string + */ public static function get_media_js_param($item, $force_type='') { $js = array(); @@ -198,30 +220,37 @@ class WebPlayer $types = self::get_types($item, $force_type); - $song = null; + $media = null; $urlinfo = Stream_URL::parse($url); $url = $urlinfo['base_url']; - if ($urlinfo['id'] && $urlinfo['type'] == 'song') { - $song = new Song($urlinfo['id']); + if ($urlinfo['id'] && Core::is_media($urlinfo['type'])) { + $media = new $urlinfo['type']($urlinfo['id']); } else if ($urlinfo['id'] && $urlinfo['type'] == 'song_preview') { - $song = new Song_Preview($urlinfo['id']); + $media = new Song_Preview($urlinfo['id']); } else if (isset($urlinfo['demo_id'])) { $democratic = new Democratic($urlinfo['demo_id']); if ($democratic->id) { $song_id = $democratic->get_next_object(); if ($song_id) { - $song = new Song($song_id); + $media = new Song($song_id); } } } - if ($song != null) { - $js['artist_id'] = $song->artist; - $js['album_id'] = $song->album; - $js['song_id'] = $song->id; + if ($media != null) { + $media->format(); + if ($urlinfo['type'] == 'song') { + $js['artist_id'] = $media->artist; + $js['album_id'] = $media->album; + $js['replaygain_track_gain'] = $media->replaygain_track_gain; + $js['replaygain_track_peak'] = $media->replaygain_track_peak; + $js['replaygain_album_gain'] = $media->replaygain_album_gain; + $js['replaygain_album_peak'] = $media->replaygain_album_peak; + } + $js['media_id'] = $media->id; - if ($song->type != $types['real']) { + if ($media->type != $types['real']) { $url .= '&transcode_to=' . $types['real']; } //$url .= "&content_length=required"; @@ -230,7 +259,7 @@ class WebPlayer $js['filetype'] = $types['player']; $js['url'] = $url; if ($urlinfo['type'] == 'song') { - $js['poster'] = $item->image_url . (!AmpConfig::get('iframes') ? '&thumb=4' : ''); + $js['poster'] = $item->image_url; } debug_event("webplayer.class.php", "Return get_media_js_param {".json_encode($js)."}", 5); diff --git a/sources/lib/class/xml_data.class.php b/sources/lib/class/xml_data.class.php index ec1de77..0d7869e 100644 --- a/sources/lib/class/xml_data.class.php +++ b/sources/lib/class/xml_data.class.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -31,8 +31,8 @@ class XML_Data { // This is added so that we don't pop any webservers - private static $limit = '5000'; - private static $offset = '0'; + private static $limit = 5000; + private static $offset = 0; private static $type = ''; /** @@ -43,7 +43,6 @@ class XML_Data private function __construct() { // Rien a faire - } // constructor /** @@ -58,7 +57,6 @@ class XML_Data { $offset = intval($offset); self::$offset = $offset; - } // set_offset /** @@ -71,11 +69,12 @@ class XML_Data */ public static function set_limit($limit) { - if (!$limit) { return false; } + if (!$limit) { + return false; + } $limit = intval($limit); self::$limit = $limit; - } // set_limit /** @@ -88,10 +87,11 @@ class XML_Data */ public static function set_type($type) { - if (!in_array($type,array('rss','xspf','itunes'))) { return false; } + if (!in_array($type,array('rss','xspf','itunes'))) { + return false; + } self::$type = $type; - } // set_type /** @@ -108,7 +108,6 @@ class XML_Data { $string = self::_header() . "\t" . self::_footer(); return $string; - } // error /** @@ -131,7 +130,6 @@ class XML_Data $final .= self::_footer(); return $final; - } // single_string /** @@ -145,7 +143,6 @@ class XML_Data public static function header() { return self::_header(); - } // header /** @@ -159,7 +156,6 @@ class XML_Data public static function footer() { return self::_footer(); - } // footer /** @@ -173,19 +169,47 @@ class XML_Data $string = ''; if (is_array($tags)) { - + $atags = array(); foreach ($tags as $tag_id => $data) { - $tag = new Tag($tag_id); - $string .= "\tid . - '" count="' . count($data['users']) . - '">name . "]]>\n"; + if (array_key_exists($data['id'], $atags)) { + $atags[$data['id']]['count']++; + } else { + $atags[$data['id']] = array('name' => $data['name'], + 'count' => 1); + } + } + + foreach ($atags as $id => $data) { + $string .= "\t\n"; } } return $string; - } // tags_string + + /** + * playlist_song_tracks_string + * + * This returns the formatted 'playlistTrack' string for an xml document + * + */ + private static function playlist_song_tracks_string($song,$playlist_data) + { + if ($playlist_data == "") { + return ""; + } + $playlist_track = ""; + foreach ($playlist as $playlist_data) { + if ($playlist_data["object_id"] == $song->id) { + return "\t" . $playlist_data["track"] . "\n"; + } + } + return ""; + } // playlist_song_tracks_string + /** * keyed_array * @@ -215,7 +239,6 @@ class XML_Data } else { $string .= "\t<$key$attribute>\n"; } - } // end foreach if (!$callback) { @@ -223,7 +246,6 @@ class XML_Data } return $string; - } // keyed_array /** @@ -259,7 +281,6 @@ class XML_Data $final = self::_header() . $string . self::_footer(); return $final; - } // tags /** @@ -291,22 +312,21 @@ class XML_Data $string .= "id . "\">\n" . "\tf_full_name . "]]>\n" . $tag_string . - "\t" . $artist->albums . "\n" . - "\t" . $artist->songs . "\n" . - "\t" . $rating->get_user_rating() . "\n" . - "\t" . $rating->get_user_rating() . "\n" . - "\t" . $rating->get_average_rating() . "\n" . + "\t" . ($artist->albums ?: 0) . "\n" . + "\t" . ($artist->songs ?: 0) . "\n" . + "\t" . ($rating->get_user_rating() ?: 0) . "\n" . + "\t" . ($rating->get_user_rating() ?: 0) . "\n" . + "\t" . ($rating->get_average_rating() ?: 0) . "\n" . "\t" . $artist->mbid . "\n" . - "\t" . $artist->summary . "\n" . - "\t" . $artist->summary . "\n" . - "\t" . $artist->summary . "\n" . + "\tsummary . "]]>\n" . + "\t" . $artist->yearformed . "\n" . + "\tplaceformed . "]]>\n" . "\n"; } // end foreach artists $final = self::_header() . $string . self::_footer(); return $final; - } // artists /** @@ -333,7 +353,7 @@ class XML_Data $rating = new Rating($album_id,'album'); // Build the Art URL, include session - $art_url = AmpConfig::get('web_path') . '/image.php?id=' . $album->id . '&auth=' . scrub_out($_REQUEST['auth']); + $art_url = AmpConfig::get('web_path') . '/image.php?object_id=' . $album->id . '&object_type=album&auth=' . scrub_out($_REQUEST['auth']); $string .= "id . "\">\n" . "\tname . "]]>\n"; @@ -360,7 +380,6 @@ class XML_Data $final = self::_header() . $string . self::_footer(); return $final; - } // albums /** @@ -392,15 +411,12 @@ class XML_Data "\t$item_total\n" . "\t$playlist->type\n" . "\n"; - - } // end foreach // Build the final and then send her off $final = self::_header() . $string . self::_footer(); return $final; - } // playlists /** @@ -409,7 +425,7 @@ class XML_Data * This returns an xml document from an array of song ids. * (Spiffy isn't it!) */ - public static function songs($songs) + public static function songs($songs,$playlist_data='') { if (count($songs) > self::$limit OR self::$offset > 0) { $songs = array_slice($songs, self::$offset, self::$limit); @@ -424,8 +440,10 @@ class XML_Data $song = new Song($song_id); // If the song id is invalid/null - if (!$song->id) { continue; } - + if (!$song->id) { + continue; + } + $playlist_track_string = self::playlist_song_tracks_string($song, $playlist_data); $tag_string = self::tags_string(Tag::get_top_tags('song', $song_id)); $rating = new Rating($song_id, 'song'); $art_url = Art::url($song->album, 'album', $_REQUEST['auth']); @@ -441,26 +459,25 @@ class XML_Data $tag_string . "\tfile . "]]>\n" . "\t" . $song->track . "\n" . + $playlist_track_string . "\t\n" . "\t" . $song->year . "\n" . "\t" . $song->bitrate . "\n". "\t" . $song->mode . "\n". "\t" . $song->mime . "\n" . - "\tid) . "]]>\n" . + "\tid, '', 'api') . "]]>\n" . "\t" . $song->size . "\n". "\t" . $song->mbid . "\n". "\t" . $song->album_mbid . "\n". "\t" . $song->artist_mbid . "\n". "\t\n" . - "\t" . $rating->get_user_rating() . "\n" . - "\t" . $rating->get_user_rating() . "\n" . - "\t" . $rating->get_average_rating() . "\n" . + "\t" . ($rating->get_user_rating() ?: 0) . "\n" . + "\t" . ($rating->get_user_rating() ?: 0) . "\n" . + "\t" . ($rating->get_average_rating() ?: 0) . "\n" . "\n"; - } // end foreach return self::_header() . $string . self::_footer(); - } // songs /** @@ -488,15 +505,13 @@ class XML_Data "\t" . $video->f_resolution . "\n" . "\t" . $video->size . "\n" . self::tags_string($video->tags) . - "\tid) . "]]>\n" . + "\tid, '', 'api') . "]]>\n" . "\n"; - } // end foreach $final = self::_header() . $string . self::_footer(); return $final; - } // videos /** @@ -510,7 +525,9 @@ class XML_Data */ public static function democratic($object_ids=array()) { - if (!is_array($object_ids)) { $object_ids = array(); } + if (!is_array($object_ids)) { + $object_ids = array(); + } $democratic = Democratic::get_current_playlist(); @@ -540,7 +557,7 @@ class XML_Data "\t" . $song->track . "\n" . "\t\n" . "\t" . $song->mime . "\n" . - "\tid) . "]]>\n" . + "\tid, '', 'api') . "]]>\n" . "\t" . $song->size . "\n" . "\t\n" . "\t" . $rating->get_user_rating() . "\n" . @@ -548,15 +565,94 @@ class XML_Data "\t" . $rating->get_average_rating() . "\n" . "\t" . $democratic->get_vote($row_id) . "\n" . "\n"; - } // end foreach $final = self::_header() . $string . self::_footer(); return $final; - } // democratic + /** + * user + * + * This handles creating an xml document for an user + * + * @param User $user User + * @return string return xml + */ + public static function user(User $user) + { + $user->format(); + + $string = "id . "\">\n" . + "\tusername . "]]>\n" . + "\t" . $user->create_date . "\n" . + "\t" . $user->last_seen . "\n" . + "\twebsite . "]]>\n" . + "\tstate . "]]>\n" . + "\tcity . "]]>\n"; + if ($user->fullname_public) { + $string .= "\tfullname . "]]>\n"; + } + $string .= "\n"; + + $final = self::_header() . $string . self::_footer(); + + return $final; + } // user + + /** + * users + * + * This handles creating an xml document for an user list + * + * @param int[] $users User identifier list + * @return string return xml + */ + public static function users($users) + { + $string = "\n"; + foreach ($users as $user_id) { + $user = new User($user_id); + $string .= "\tusername . "]]>\n"; + } + $string .= "\n"; + + $final = self::_header() . $string . self::_footer(); + + return $final; + } // users + + /** + * shouts + * + * This handles creating an xml document for a shout list + * + * @param int[] $shouts Shout identifier list + * @return string return xml + */ + public static function shouts($shouts) + { + $string = "\n"; + foreach ($shouts as $shout_id) { + $shout = new Shoutbox($shout_id); + $shout->format(); + $user = new User($shout->user); + $string .= "\t\n" . + "\t\t" . $shout->date . "\n" . + "\t\ttext . "]]>\n"; + if ($user->id) { + $string .= "\t\tusername ."]]>"; + } + $string .= "\tn"; + } + $string .= "\n"; + + $final = self::_header() . $string . self::_footer(); + + return $final; + } // shouts + /** * rss_feed * @@ -572,8 +668,10 @@ class XML_Data */ public static function rss_feed($data,$title,$description,$date) { - $string = "\t$title\n\t" . AmpConfig::get('web_path') . "\n\t" . - "" . date("r",$date) . "\n"; + $string = "\t$title\n\t" . AmpConfig::get('web_path') . "\n\t"; + if ($date != null) { + $string .= "" . date("r",$date) . "\n"; + } // Pass it to the keyed array xml function foreach ($data as $item) { @@ -584,7 +682,6 @@ class XML_Data $final = self::_header() . $string . self::_footer(); return $final; - } // rss_feed /** @@ -633,7 +730,6 @@ class XML_Data } // end switch return $header; - } // _header /** @@ -662,7 +758,68 @@ class XML_Data return $footer; - } // _footer + public static function podcast(library_item $libitem) + { + $xml = new SimpleXMLElement(''); + $xml->addAttribute("version", "2.0"); + $xml->addAttribute("xmlns:xmlns:atom", "http://www.w3.org/2005/Atom"); + $xml->addAttribute("xmlns:xmlns:itunes", "http://www.itunes.com/dtds/podcast-1.0.dtd"); + $xchannel = $xml->addChild("channel"); + $xchannel->addChild("title", $libitem->get_fullname() . " Podcast"); + $xlink = $xchannel->addChild("atom:link"); + $xlink->addAttribute("type", "text/html"); + $xlink->addAttribute("href", $libitem->link); + if (Art::has_db($libitem->id, get_class($libitem))) { + $ximg = $xchannel->addChild("xmlns:itunes:image"); + $ximg->addAttribute("href", Art::url($libitem->id, get_class($libitem))); + } + $summary = $libitem->get_description(); + if (!empty($summary)) { + $xchannel->addChild("xmlns:itunes:summary", $summary); + } + $xchannel->addChild("xmlns:itunes:category", "Music"); + $owner = $libitem->get_user_owner(); + if ($owner) { + $user_owner = new User($owner); + $user_owner->format(); + $xowner = $xitem->addChild("xmlns:itunes:owner"); + $xowner->addChild("xmlns:itunes:name", $user_owner->f_name); + } + + $medias = $libitem->get_medias(); + foreach ($medias as $media_info) { + $media = new $media_info['object_type']($media_info['object_id']); + $media->format(); + $xitem = $xchannel->addChild("item"); + $xitem->addChild("title", $media->get_fullname()); + if ($media->f_artist) { + $xitem->addChild("xmlns:itunes:author", $media->f_artist); + } + $xmlink = $xitem->addChild("link"); + $xmlink->addAttribute("href", $media->link); + $xitem->addChild("guid", $media->link); + if ($media->addition_time) { + $xitem->addChild("pubDate", date("r", $media->addition_time)); + } + $description = $media->get_description(); + if (!empty($description)) { + $xitem->addChild("description", $description); + } + $xitem->addChild("xmlns:itunes:duration", $media->f_time); + $xencl = $xitem->addChild("enclosure"); + $xencl->addAttribute("type", $media->mime); + $xencl->addAttribute("length", $media->size); + $surl = $media_info['object_type']::play_url($media_info['object_id']); + $xencl->addAttribute("url", $surl); + } + + $xmlstr = $xml->asXml(); + // Format xml output + $dom = new DOMDocument(); + $dom->loadXML($xmlstr); + $dom->formatOutput = true; + return $dom->saveXML($dom->documentElement); + } } // XML_Data diff --git a/sources/lib/debug.lib.php b/sources/lib/debug.lib.php index 2f39a93..740fa59 100644 --- a/sources/lib/debug.lib.php +++ b/sources/lib/debug.lib.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -96,9 +96,10 @@ function check_config_values($conf) if (!$conf['database_username']) { return false; } - if (!$conf['database_password']) { + /* Don't check for password to support mysql socket auth + * if (!$conf['database_password']) { return false; - } + }*/ if (!$conf['session_length']) { return false; } @@ -206,6 +207,52 @@ function check_override_exec_time() return true; } +/** + * check_upload_size + * This checks to see if max upload size is not too small + */ +function check_upload_size() +{ + $upload_max = return_bytes(ini_get('upload_max_filesize')); + $post_max = return_bytes(ini_get('post_max_size')); + $mini = 20971520; // 20M + + return (($upload_max >= $mini || $upload_max <= 0) && ($post_max >= $mini || $post_max <= 0)); +} + +function check_php_int_size() +{ + return (PHP_INT_SIZE > 4); +} + +function check_php_zlib() +{ + return function_exists('gzcompress'); +} + +function check_php_simplexml() +{ + return function_exists('simplexml_load_string'); +} + +function return_bytes($val) +{ + $val = trim($val); + $last = strtolower($val[strlen($val)-1]); + switch ($last) { + // The 'G' modifier is available since PHP 5.1.0 + case 'g': + $val *= 1024; + case 'm': + $val *= 1024; + case 'k': + $val *= 1024; + break; + } + + return $val; +} + /** * check_config_writable * This checks whether we can write the config file @@ -217,6 +264,12 @@ function check_config_writable() || (!file_exists(AmpConfig::get('prefix') . '/config/ampache.cfg.php') && is_writeable(AmpConfig::get('prefix') . '/config/'))); } +function check_htaccess_channel_writable() +{ + return ((file_exists(AmpConfig::get('prefix') . '/channel/.htaccess') && is_writable(AmpConfig::get('prefix') . '/channel/.htaccess')) + || (!file_exists(AmpConfig::get('prefix') . '/channel/.htaccess') && is_writeable(AmpConfig::get('prefix') . '/channel/'))); +} + function check_htaccess_rest_writable() { return ((file_exists(AmpConfig::get('prefix') . '/rest/.htaccess') && is_writable(AmpConfig::get('prefix') . '/rest/.htaccess')) @@ -239,7 +292,24 @@ function debug_result($status = false, $value = null, $comment = '') $class = $status ? 'success' : 'danger'; if (!$value) { - $value = $status ? 'OK' : 'ERROR'; + $value = $status ? T_('OK') : T_('ERROR'); + } + + return ''; +} + +/** + * debug_wresult + * + * Convenience function to format the output. + */ +function debug_wresult($status = false, $value = null, $comment = '') +{ + $class = $status ? 'success' : 'warning'; + + if (!$value) { + $value = $status ? T_('OK') : T_('WARNING'); } return ' - +
+
-
-
-
-
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
-
 
 
-
- -
-
- - -
-
-
-
-
- +

+
+
+
+

+
+
"> +
+ +
 
 
+
+ +
+
+ + +
+
+
+
+
+ +
 
 
+
+ +
+
+ + +
+
+
+
+
+ +
 
 
+
+ +
+
+ + +
+
+
+
+
+ + +
 
 
+
+ +
+
+ + +
+
+
+
+
+
 
 
+ +
+ +
+ [] +
+
+
+
+
-
 
 
- +
- - +
-
-
-
-
-
 
 
- - - -
- -
- [] -
- -
" - enctype="multipart/form-data" -> -
diff --git a/sources/templates/show_install_lang.inc.php b/sources/templates/show_install_lang.inc.php index e28fedf..7a00d1e 100644 --- a/sources/templates/show_install_lang.inc.php +++ b/sources/templates/show_install_lang.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_ip_history.inc.php b/sources/templates/show_ip_history.inc.php index 8745736..e5e1760 100644 --- a/sources/templates/show_ip_history.inc.php +++ b/sources/templates/show_ip_history.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_label.inc.php b/sources/templates/show_label.inc.php new file mode 100644 index 0000000..1a3d716 --- /dev/null +++ b/sources/templates/show_label.inc.php @@ -0,0 +1,109 @@ + +set_type($object_type); + +UI::show_box_top($label->f_name, 'info-box'); +if ($label->website) + echo "website) . "\">" . scrub_out($label->website) . "
"; +?> +
+ +
+
+ id, $label->f_name, 2); ?> +
+ address); ?> +
+
+
+ summary)); ?> +
+
+
+ +
+

:

+
    + + +
  • + + +
  • + + + email) { ?> +
  • + + +
  • + + can_edit()) { ?> +
  • + + + + + + +
  • + + +
  • + + + +
  • + +
+
+ +
+
+
    +
  • +
  • +
+
+
+
+show_objects($object_ids, true); + $browse->store(); +?> +
+id, 'songs')); +?> +
+ +
+
+
diff --git a/sources/templates/show_label_row.inc.php b/sources/templates/show_label_row.inc.php new file mode 100644 index 0000000..3444109 --- /dev/null +++ b/sources/templates/show_label_row.inc.php @@ -0,0 +1,54 @@ + +f_name); +?> + + id, $name, 1, AmpConfig::get('web_path') . '/labels.php?action=show&label=' . $libitem->id); + ?> + + +f_link; ?> +category; ?> +artists; ?> + + + + + + + + +can_edit()) { ?> + + + + + + + + + + diff --git a/sources/templates/show_labels.inc.php b/sources/templates/show_labels.inc.php new file mode 100644 index 0000000..69d2c5d --- /dev/null +++ b/sources/templates/show_labels.inc.php @@ -0,0 +1,76 @@ + + +
+
    +
  • +
+
+ +get_show_header()) require AmpConfig::get('prefix') . '/templates/list_header.inc.php'; ?> + + + + + + + + + + + + + + format(); + ?> + + + + + + + + + + + + + + + + + + + + + +
id . '&type=label&sort=name', T_('Label'),'label_sort_name'); ?>id . '&type=label&sort=category', T_('Category'),'label_sort_category'); ?>
id . '&type=label&sort=name', T_('Label'),'label_sort_name'); ?>id . '&type=label&sort=category', T_('Category'),'label_sort_category'); ?>
+ + +get_show_header()) require AmpConfig::get('prefix') . '/templates/list_header.inc.php'; ?> diff --git a/sources/templates/show_license_row.inc.php b/sources/templates/show_license_row.inc.php new file mode 100644 index 0000000..ad146b3 --- /dev/null +++ b/sources/templates/show_license_row.inc.php @@ -0,0 +1,34 @@ + + + f_link; ?> + description; ?> + + + + + + + + + diff --git a/sources/templates/show_live_stream.inc.php b/sources/templates/show_live_stream.inc.php index c1062fe..a662d08 100644 --- a/sources/templates/show_live_stream.inc.php +++ b/sources/templates/show_live_stream.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -20,12 +20,14 @@ * */ ?> +
  • - +
+ diff --git a/sources/templates/show_live_stream_row.inc.php b/sources/templates/show_live_stream_row.inc.php index 364dfbb..aba6929 100644 --- a/sources/templates/show_live_stream_row.inc.php +++ b/sources/templates/show_live_stream_row.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -24,25 +24,20 @@  
- id, 'play', T_('Play live stream'),'play_live_stream_' . $radio->id); ?> + id, 'play', T_('Play live stream'),'play_live_stream_' . $libitem->id); ?>
-f_name_link; ?> - - - id,'add', T_('Add to temporary playlist'),'add_radio_' . $radio->id); ?> - - -f_url_link; ?> -codec; ?> +f_link; ?> +f_url_link; ?> +codec; ?> - + - id,'delete', T_('Delete'),'delete_live_stream_' . $radio->id); ?> + id,'delete', T_('Delete'),'delete_live_stream_' . $libitem->id); ?> diff --git a/sources/templates/show_live_streams.inc.php b/sources/templates/show_live_streams.inc.php index 26f0c21..ccf797a 100644 --- a/sources/templates/show_live_streams.inc.php +++ b/sources/templates/show_live_streams.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -28,7 +28,6 @@ $web_path = AmpConfig::get('web_path'); id . '&sort=name', T_('Name'),'live_stream_sort_name'); ?> - id . '&sort=codec', T_('Codec'),'live_stream_codec'); ?> @@ -37,10 +36,10 @@ $web_path = AmpConfig::get('web_path'); format(); + $libitem = new Live_Stream($radio_id); + $libitem->format(); ?> - + @@ -54,7 +53,6 @@ $web_path = AmpConfig::get('web_path'); id . '&sort=name', T_('Name'),'live_stream_sort_name'); ?> - id . '&sort=codec', T_('Codec'),'live_stream_codec_bottom'); ?> diff --git a/sources/templates/show_localplay_add_instance.inc.php b/sources/templates/show_localplay_add_instance.inc.php index 8f0bf6a..0665460 100644 --- a/sources/templates/show_localplay_add_instance.inc.php +++ b/sources/templates/show_localplay_add_instance.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_localplay_control.inc.php b/sources/templates/show_localplay_control.inc.php index 8477728..d084663 100644 --- a/sources/templates/show_localplay_control.inc.php +++ b/sources/templates/show_localplay_control.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_localplay_controllers.inc.php b/sources/templates/show_localplay_controllers.inc.php index 4f94417..5d8ec17 100644 --- a/sources/templates/show_localplay_controllers.inc.php +++ b/sources/templates/show_localplay_controllers.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_localplay_edit_instance.inc.php b/sources/templates/show_localplay_edit_instance.inc.php index cca208a..5e7c016 100644 --- a/sources/templates/show_localplay_edit_instance.inc.php +++ b/sources/templates/show_localplay_edit_instance.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_localplay_instances.inc.php b/sources/templates/show_localplay_instances.inc.php index 47af03a..a555516 100644 --- a/sources/templates/show_localplay_instances.inc.php +++ b/sources/templates/show_localplay_instances.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_localplay_playlist.inc.php b/sources/templates/show_localplay_playlist.inc.php index 0f741da..6c9122e 100644 --- a/sources/templates/show_localplay_playlist.inc.php +++ b/sources/templates/show_localplay_playlist.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_localplay_status.inc.php b/sources/templates/show_localplay_status.inc.php index 77ad9a3..e2a8027 100644 --- a/sources/templates/show_localplay_status.inc.php +++ b/sources/templates/show_localplay_status.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_login_form.inc.php b/sources/templates/show_login_form.inc.php index 4a87c9c..8b75713 100644 --- a/sources/templates/show_login_form.inc.php +++ b/sources/templates/show_login_form.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -30,89 +30,67 @@ if (AmpConfig::get('session_length') >= AmpConfig::get('remember_length')) { $htmllang = str_replace("_","-",AmpConfig::get('lang')); is_rtl(AmpConfig::get('lang')) ? $dir = 'rtl' : $dir = 'ltr'; +$_SESSION['login'] = true; +define('TABLE_RENDERED', 1); + ?> - - - - - - <?php echo scrub_out(AmpConfig::get('site_title')); ?> - - - + + + +
+ +
+

+
+
+ + +
+
+ + +
+
/> +
+ + + +
+ + + + + + + + +
+
+ +
+
+ - - - -"> -
- -
-

-
- -
- - -
-
- - -
-
/> -
- - - - -
- - - - - - - - -
- -
- -
-
- - + UI::show_footer(); + ?> diff --git a/sources/templates/show_lostpassword_form.inc.php b/sources/templates/show_lostpassword_form.inc.php index 99af682..60b11cf 100644 --- a/sources/templates/show_lostpassword_form.inc.php +++ b/sources/templates/show_lostpassword_form.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -28,52 +28,50 @@ if (AmpConfig::get('session_length') >= AmpConfig::get('remember_length')) { } $htmllang = str_replace("_","-",AmpConfig::get('lang')); is_rtl(AmpConfig::get('lang')) ? $dir = 'rtl' : $dir = 'ltr'; - ?> + - - - - - - -<?php echo scrub_out(AmpConfig::get('site_title')); ?> - - - - -
- -
-

-
- -
- -
-
-
-
- - + + + + + + + + + <?php echo scrub_out(AmpConfig::get('site_title')); ?> + + + +
+ +
+

+
+
+ + + +
+ +
+
+
+ + diff --git a/sources/templates/show_lyrics.inc.php b/sources/templates/show_lyrics.inc.php index f0a3c98..00b7291 100644 --- a/sources/templates/show_lyrics.inc.php +++ b/sources/templates/show_lyrics.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -28,21 +28,16 @@ $title = scrub_out($song->title); $album = scrub_out($song->f_album_full); $artist = scrub_out($song->f_artist_full); ?> -
- name != T_('Unknown (Orphaned)')) { - $aa_url = $web_path . "/image.php?id=" . $song->album . "&sid=" . session_id(); - echo ""; - echo "\"".$song-f_album_full."\" alt=\"".$song->f_album_full."\" height=\"128\" width=\"128\" />"; - echo "\n"; - } - ?> -
+album, $song->f_album_full, 2); +} +?>
- +
diff --git a/sources/templates/show_mail_users.inc.php b/sources/templates/show_mail_users.inc.php index 379d1f3..b2349bf 100644 --- a/sources/templates/show_mail_users.inc.php +++ b/sources/templates/show_mail_users.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_manage_catalogs.inc.php b/sources/templates/show_manage_catalogs.inc.php index 89d7644..9748869 100644 --- a/sources/templates/show_manage_catalogs.inc.php +++ b/sources/templates/show_manage_catalogs.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -22,17 +22,30 @@ ?>
-
    -
  • -
  • -
  • -
  • -
  • -
  • -
  • +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    -
    -
    + diff --git a/sources/templates/show_manage_democratic.inc.php b/sources/templates/show_manage_democratic.inc.php index be15eb2..1acb24a 100644 --- a/sources/templates/show_manage_democratic.inc.php +++ b/sources/templates/show_manage_democratic.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -40,7 +40,7 @@ UI::show_box_top(T_('Manage Democratic Playlists')); ?> ?> - + diff --git a/sources/templates/show_manage_license.inc.php b/sources/templates/show_manage_license.inc.php new file mode 100644 index 0000000..b285d75 --- /dev/null +++ b/sources/templates/show_manage_license.inc.php @@ -0,0 +1,61 @@ + +
    +
      +
    • + +
    • +
    +
    +
    /data/myNewMusic'); ?>
    name); ?>f_name_link; ?>f_link; ?> f_cooldown; ?> f_level; ?> f_primary; ?>
    + + + + + + + + + format(); + + require AmpConfig::get('prefix') . '/templates/show_license_row.inc.php'; + ?> + + + + + + + + + + + + + +
    diff --git a/sources/templates/show_manage_shoutbox.inc.php b/sources/templates/show_manage_shoutbox.inc.php index c1f4a47..23eb2ec 100644 --- a/sources/templates/show_manage_shoutbox.inc.php +++ b/sources/templates/show_manage_shoutbox.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -23,36 +23,43 @@ $web_path = AmpConfig::get('web_path'); ?> - - - - - - - - - format(); - $object = Shoutbox::get_object($shout->object_type,$shout->object_id); + + + + + + + + + + + + format(); + + $object = Shoutbox::get_object($libitem->object_type, $libitem->object_id); $object->format(); - $client = new User($shout->user); + $client = new User($libitem->user); $client->format(); - require AmpConfig::get('prefix') . '/templates/show_shout_row.inc.php'; - ?> - - - - - - - - - - - - - + require AmpConfig::get('prefix') . '/templates/show_shout_row.inc.php'; + ?> + + + + + + + + + + + + + + + +
    diff --git a/sources/templates/show_missing_album.inc.php b/sources/templates/show_missing_album.inc.php index 0b14fac..90e2c69 100644 --- a/sources/templates/show_missing_album.inc.php +++ b/sources/templates/show_missing_album.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -27,7 +27,7 @@ $title = scrub_out($walbum->name) . ' (' . $walbum->year . ')'; $title .= ' - ' . $walbum->f_artist_link; ?> -
    +
    mbid, 'album'); @@ -50,24 +50,6 @@ if (count($images) > 0 && !empty($images[0]['url'])) {

    :

      - - -
    • - mbid,'play_preview', T_('Play'),'directplay_full_' . $walbum->mbid); ?> - mbid, T_('Play'),'directplay_full_text_' . $walbum->mbid); ?> -
    • - - -
    • - mbid . '&append=true','play_add_preview', T_('Play last'),'addplay_album_' . $walbum->mbid); ?> - mbid . '&append=true', T_('Play last'),'addplay_album_text_' . $walbum->mbid); ?> -
    • - -
    • - mbid,'add', T_('Add to temporary playlist'),'play_full_' . $walbum->mbid); ?> - mbid, T_('Add to temporary playlist'), 'play_full_text_' . $walbum->mbid); ?> -
    • -
    • :
      diff --git a/sources/templates/show_missing_albums.inc.php b/sources/templates/show_missing_albums.inc.php index f458099..b5d07d0 100644 --- a/sources/templates/show_missing_albums.inc.php +++ b/sources/templates/show_missing_albums.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -33,15 +33,19 @@ - + - + - + diff --git a/sources/templates/show_missing_artist.inc.php b/sources/templates/show_missing_artist.inc.php index 8b2d092..f446359 100644 --- a/sources/templates/show_missing_artist.inc.php +++ b/sources/templates/show_missing_artist.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -27,7 +27,7 @@ UI::show_box_top($wartist['name'], 'info-box'); ?>
      @@ -37,7 +37,7 @@ if (AmpConfig::get('lastfm_api_key')) {
      diff --git a/sources/templates/show_missing_artists.inc.php b/sources/templates/show_missing_artists.inc.php new file mode 100644 index 0000000..8c40689 --- /dev/null +++ b/sources/templates/show_missing_artists.inc.php @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + +
      + +
      + diff --git a/sources/templates/show_newest.inc.php b/sources/templates/show_newest.inc.php index ef57588..b42dd10 100644 --- a/sources/templates/show_newest.inc.php +++ b/sources/templates/show_newest.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_now_playing.inc.php b/sources/templates/show_now_playing.inc.php index 56757c6..43b35c2 100644 --- a/sources/templates/show_now_playing.inc.php +++ b/sources/templates/show_now_playing.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -43,7 +43,13 @@ foreach ($results as $item) { if (!$np_user->fullname) { $np_user->fullname = "Ampache User"; } ?>
      - +
      -
      - - f_tags; ?> -
      + f_tags)) { ?> +
      + + f_tags; ?> +
      +
      - - <?php echo scrub_out($media->f_album_full); ?> - + album, $media->get_fullname(), 1, AmpConfig::get('web_path') . '/albums.php?action=show&album=' . $media->album); ?>
      @@ -85,19 +85,23 @@ $(document).ready(function(){ -
      - -
      - -
      - id,'song'); ?> + +
      + +
      + +
      + id,'song'); ?> +
      -
      -
      - -
      - id,'song'); ?> + + +
      + +
      + id,'song'); ?> +
      +
      -
      diff --git a/sources/templates/show_now_playing_similar.inc.php b/sources/templates/show_now_playing_similar.inc.php index 6fe3e2d..7d087df 100644 --- a/sources/templates/show_now_playing_similar.inc.php +++ b/sources/templates/show_now_playing_similar.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -37,7 +37,7 @@ } else { $artist = new Artist($a['id']); $artist->format(); - echo $artist->f_name_link; + echo $artist->f_link; } ?>
      diff --git a/sources/templates/show_now_playing_video_row.inc.php b/sources/templates/show_now_playing_video_row.inc.php new file mode 100644 index 0000000..5baa222 --- /dev/null +++ b/sources/templates/show_now_playing_video_row.inc.php @@ -0,0 +1,72 @@ +id); +$media->format(); +?> + + +
      +
      + + f_link; ?> +
      +
      + + +
      +
      + get_release_item_art(); + Art::display($release_art['object_type'], $release_art['object_id'], $media->get_fullname(), 6, $media->link); + ?> +
      +
      + + +
      + +
      + +
      + id, 'video'); ?> +
      +
      +
      + +
      + id,'video'); ?> +
      +
      + +
      diff --git a/sources/templates/show_object_rating.inc.php b/sources/templates/show_object_rating.inc.php index fde9c3f..6ee7f61 100644 --- a/sources/templates/show_object_rating.inc.php +++ b/sources/templates/show_object_rating.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_object_row.inc.php b/sources/templates/show_object_row.inc.php index d9edfd7..93b1e62 100644 --- a/sources/templates/show_object_row.inc.php +++ b/sources/templates/show_object_row.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_object_userflag.inc.php b/sources/templates/show_object_userflag.inc.php index 4f35730..34d205d 100644 --- a/sources/templates/show_object_userflag.inc.php +++ b/sources/templates/show_object_userflag.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_objects.inc.php b/sources/templates/show_objects.inc.php index 3518e25..0b982d5 100644 --- a/sources/templates/show_objects.inc.php +++ b/sources/templates/show_objects.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_partial_clip.inc.php b/sources/templates/show_partial_clip.inc.php new file mode 100644 index 0000000..5655253 --- /dev/null +++ b/sources/templates/show_partial_clip.inc.php @@ -0,0 +1,24 @@ +f_artist; +$videoprops[gettext_noop('Song')] = $video->f_song; diff --git a/sources/templates/show_partial_clip_row.inc.php b/sources/templates/show_partial_clip_row.inc.php new file mode 100644 index 0000000..1063b93 --- /dev/null +++ b/sources/templates/show_partial_clip_row.inc.php @@ -0,0 +1,23 @@ + +f_artist; ?> diff --git a/sources/templates/show_partial_clips.inc.php b/sources/templates/show_partial_clips.inc.php new file mode 100644 index 0000000..1480699 --- /dev/null +++ b/sources/templates/show_partial_clips.inc.php @@ -0,0 +1,23 @@ + +id . '&type=clip&sort=artist', T_('Artist'),'sort_video_artist'); ?> diff --git a/sources/templates/show_partial_edit_clip_row.inc.php b/sources/templates/show_partial_edit_clip_row.inc.php new file mode 100644 index 0000000..f1caac8 --- /dev/null +++ b/sources/templates/show_partial_edit_clip_row.inc.php @@ -0,0 +1,28 @@ + + + + + artist); ?> + + diff --git a/sources/templates/show_partial_edit_movie_row.inc.php b/sources/templates/show_partial_edit_movie_row.inc.php new file mode 100644 index 0000000..a9188dd --- /dev/null +++ b/sources/templates/show_partial_edit_movie_row.inc.php @@ -0,0 +1,34 @@ + + + + + + + + + + + + + diff --git a/sources/templates/show_partial_edit_personal_video_row.inc.php b/sources/templates/show_partial_edit_personal_video_row.inc.php new file mode 100644 index 0000000..13dce1c --- /dev/null +++ b/sources/templates/show_partial_edit_personal_video_row.inc.php @@ -0,0 +1,30 @@ + + + + + + + + + diff --git a/sources/templates/show_partial_edit_tvshow_episode_row.inc.php b/sources/templates/show_partial_edit_tvshow_episode_row.inc.php new file mode 100644 index 0000000..801fc4c --- /dev/null +++ b/sources/templates/show_partial_edit_tvshow_episode_row.inc.php @@ -0,0 +1,40 @@ + + + + + season); ?> + + + + + + + + + + + + + + diff --git a/sources/templates/show_partial_movie.inc.php b/sources/templates/show_partial_movie.inc.php new file mode 100644 index 0000000..c4448df --- /dev/null +++ b/sources/templates/show_partial_movie.inc.php @@ -0,0 +1,22 @@ +year; diff --git a/sources/templates/show_partial_movie_row.inc.php b/sources/templates/show_partial_movie_row.inc.php new file mode 100644 index 0000000..3e84698 --- /dev/null +++ b/sources/templates/show_partial_movie_row.inc.php @@ -0,0 +1,23 @@ + +year; ?> diff --git a/sources/templates/show_partial_movies.inc.php b/sources/templates/show_partial_movies.inc.php new file mode 100644 index 0000000..b10ca8a --- /dev/null +++ b/sources/templates/show_partial_movies.inc.php @@ -0,0 +1,24 @@ + +id . '&type=movie&sort=year', T_('Year'),'sort_video_year'); ?> diff --git a/sources/templates/show_partial_personal_video.inc.php b/sources/templates/show_partial_personal_video.inc.php new file mode 100644 index 0000000..404614c --- /dev/null +++ b/sources/templates/show_partial_personal_video.inc.php @@ -0,0 +1,24 @@ +f_location; +$videoprops[gettext_noop('Summary')] = scrub_out($video->summary); diff --git a/sources/templates/show_partial_personal_video_row.inc.php b/sources/templates/show_partial_personal_video_row.inc.php new file mode 100644 index 0000000..cd181c9 --- /dev/null +++ b/sources/templates/show_partial_personal_video_row.inc.php @@ -0,0 +1,23 @@ + +f_location; ?> diff --git a/sources/templates/show_partial_personal_videos.inc.php b/sources/templates/show_partial_personal_videos.inc.php new file mode 100644 index 0000000..c7604a5 --- /dev/null +++ b/sources/templates/show_partial_personal_videos.inc.php @@ -0,0 +1,23 @@ + +id . '&type=personal_video&sort=location', T_('Location'),'sort_video_location'); ?> diff --git a/sources/templates/show_partial_tvshow_episode.inc.php b/sources/templates/show_partial_tvshow_episode.inc.php new file mode 100644 index 0000000..c42e491 --- /dev/null +++ b/sources/templates/show_partial_tvshow_episode.inc.php @@ -0,0 +1,26 @@ +f_tvshow_link; +$videoprops[gettext_noop('Season')] = $video->f_season_link; +$videoprops[gettext_noop('Episode')] = $video->episode_number; +$videoprops[gettext_noop('Summary')] = $video->summary; diff --git a/sources/templates/show_partial_tvshow_episode_row.inc.php b/sources/templates/show_partial_tvshow_episode_row.inc.php new file mode 100644 index 0000000..4aad400 --- /dev/null +++ b/sources/templates/show_partial_tvshow_episode_row.inc.php @@ -0,0 +1,25 @@ + +episode_number; ?> +f_season_link; ?> +f_tvshow_link; ?> diff --git a/sources/templates/show_partial_tvshow_episodes.inc.php b/sources/templates/show_partial_tvshow_episodes.inc.php new file mode 100644 index 0000000..6e50464 --- /dev/null +++ b/sources/templates/show_partial_tvshow_episodes.inc.php @@ -0,0 +1,25 @@ + +id . '&type=tvshow_episode&sort=episode', T_('Episode'),'sort_video_episode'); ?> +id . '&type=tvshow_episode&sort=season', T_('Season'),'sort_video_season'); ?> +id . '&type=tvshow_episode&sort=tvshow', T_('TV Show'),'sort_video_tvshow'); ?> diff --git a/sources/templates/show_playlist.inc.php b/sources/templates/show_playlist.inc.php index a5f4a0c..b65f0de 100644 --- a/sources/templates/show_playlist.inc.php +++ b/sources/templates/show_playlist.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -33,8 +33,21 @@ $title = ob_get_contents(); ob_end_clean(); UI::show_box_top('
      ' . $title . '
      ', 'info-box'); ?> + + +
      + id,'playlist'); ?> +
      + + +
      + id,'playlist'); ?> +
      + +
        + has_access('50')) { ?>
      • @@ -42,9 +55,18 @@ UI::show_box_top('
      • -
      • - + +    +
      • +
      • + +    +
      • + + +
      • +    @@ -52,14 +74,14 @@ UI::show_box_top('
        ' . $title . '
      • - id,'play', T_('Play all'),'directplay_full_' . $playlist->id); ?> - id, T_('Play all'),'directplay_full_text_' . $playlist->id); ?> + id,'play', T_('Play all'),'directplay_full_' . $playlist->id); ?> + id, T_('Play all'),'directplay_full_text_' . $playlist->id); ?>
      • - id . '&append=true','play_add', T_('Play all last'),'addplay_playlist_' . $playlist->id); ?> - id . '&append=true', T_('Play all last'),'addplay_playlist_text_' . $playlist->id); ?> + id . '&append=true','play_add', T_('Play all last'),'addplay_playlist_' . $playlist->id); ?> + id . '&append=true', T_('Play all last'),'addplay_playlist_text_' . $playlist->id); ?>
      • @@ -70,7 +92,7 @@ UI::show_box_top('
        ' . $title . 'id,'random', T_('Random all to temporary playlist'),'play_playlist_random'); ?> id, T_('Random all to temporary playlist'),'play_playlist_random_text'); ?>
      • - has_access('50')) { ?> + has_access('50') && AmpConfig::get('channel')) { ?>
      • @@ -80,7 +102,7 @@ UI::show_box_top('
        ' . $title . ' has_access()) { ?>
      • - +    @@ -94,8 +116,8 @@ UI::show_box_top('
        ' . $title . 'set_type('playlist_song'); $browse->add_supplemental_object('playlist', $playlist->id); - $browse->set_static_content(false); - $browse->show_objects($object_ids); + $browse->set_static_content(true); + $browse->show_objects($object_ids, true); $browse->store(); ?>
        diff --git a/sources/templates/show_playlist_row.inc.php b/sources/templates/show_playlist_row.inc.php index feee6eb..c7817c9 100644 --- a/sources/templates/show_playlist_row.inc.php +++ b/sources/templates/show_playlist_row.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -24,39 +24,54 @@  
        - id,'play', T_('Play'),'play_playlist_' . $playlist->id); ?> + id,'play', T_('Play'),'play_playlist_' . $libitem->id); ?> - id . '&append=true','play_add', T_('Play last'),'addplay_playlist_' . $playlist->id); ?> + id . '&append=true','play_add', T_('Play last'),'addplay_playlist_' . $libitem->id); ?> + + + id . '&playnext=true', 'play_next', T_('Play next'), 'nextplay_playlist_' . $libitem->id); ?>
        -f_name_link; ?> +f_link; ?> - id,'add', T_('Add to temporary playlist'),'add_playlist_' . $playlist->id); ?> - id,'random', T_('Random to temporary playlist'),'random_playlist_' . $playlist->id); ?> - - - + id,'add', T_('Add to temporary playlist'),'add_playlist_' . $libitem->id); ?> + + id,'random', T_('Random to temporary playlist'),'random_playlist_' . $libitem->id); ?> + + + + -f_type; ?> - -f_user); ?> +f_type; ?> +get_song_count(); ?> +f_user); ?> + + + id, 'playlist'); ?> + + + id, 'playlist'); ?> + + - - + + - - + + + id, false); ?> + - has_access()) { ?> - + has_access()) { ?> + - id, 'delete', T_('Delete'), 'delete_playlist_'.$playlist->id, '', '', T_('Do you really want to delete the playlist?')); ?> + id, 'delete', T_('Delete'), 'delete_playlist_'.$libitem->id, '', '', T_('Do you really want to delete the playlist?')); ?> diff --git a/sources/templates/show_playlist_song_row.inc.php b/sources/templates/show_playlist_song_row.inc.php index fc84d39..6649ff2 100644 --- a/sources/templates/show_playlist_song_row.inc.php +++ b/sources/templates/show_playlist_song_row.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -24,45 +24,53 @@ '.$playlist_track.''; ?>
        - id, 'play', T_('Play'),'play_playlist_song_' . $song->id); ?> + id, 'play', T_('Play'),'play_playlist_song_' . $libitem->id); ?> - id . '&append=true','play_add', T_('Play last'),'addplay_song_' . $song->id); ?> + id . '&append=true','play_add', T_('Play last'),'addplay_song_' . $libitem->id); ?>
        -f_link; ?> +f_link; ?> - id,'add', T_('Add to temporary playlist'),'playlist_add_' . $song->id); ?> - - - + id,'add', T_('Add to temporary playlist'),'playlist_add_' . $libitem->id); ?> + + + + + -f_artist_link; ?> -f_album_link; ?> -f_tags; ?> -f_time; ?> - -id,'song'); ?> - - -id,'song'); ?> +f_artist_link; ?> +f_album_link; ?> +f_tags; ?> +f_time; ?> + + + id,'song'); ?> + + + id,'song'); ?> + - + - - + + + id, false); ?> + - has_access()) { ?> + has_access()) { ?> id . '&track_id=' . $object['track_id'],'delete', T_('Delete'),'track_del_' . $object['track_id']); ?> + + diff --git a/sources/templates/show_playlist_songs.inc.php b/sources/templates/show_playlist_songs.inc.php index 9f457b0..c9a4507 100644 --- a/sources/templates/show_playlist_songs.inc.php +++ b/sources/templates/show_playlist_songs.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -34,14 +34,16 @@ $web_path = AmpConfig::get('web_path'); - - - - + + + + + + @@ -53,8 +55,8 @@ $web_path = AmpConfig::get('web_path'); if (!is_array($object)) { $object = (array) $object; } - $song = new Song($object['object_id']); - $song->format(); + $libitem = new Song($object['object_id']); + $libitem->format(); $playlist_track = $object['track']; ?> @@ -71,16 +73,19 @@ $web_path = AmpConfig::get('web_path'); - - - - - - + + + + + + + +
      • + get_show_header()) require AmpConfig::get('prefix') . '/templates/list_header.inc.php'; ?> diff --git a/sources/templates/show_playlist_title.inc.php b/sources/templates/show_playlist_title.inc.php index aad0de6..0a461ba 100644 --- a/sources/templates/show_playlist_title.inc.php +++ b/sources/templates/show_playlist_title.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_playlists.inc.php b/sources/templates/show_playlists.inc.php index cf0a7d3..91ee23e 100644 --- a/sources/templates/show_playlists.inc.php +++ b/sources/templates/show_playlists.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -30,17 +30,24 @@ id . '&type=playlist&sort=user', T_('Owner'),'playlist_sort_owner'); ?> + + + + + + + + format(); - $count = $playlist->get_song_count(); + $libitem = new Playlist($playlist_id); + $libitem->format(); ?> - + @@ -58,6 +65,14 @@ id . '&type=playlist&sort=user', T_('Owner'),'playlist_sort_owner_bottom'); ?> + + + + + + + + diff --git a/sources/templates/show_playlists_dialog.inc.php b/sources/templates/show_playlists_dialog.inc.php index eabf95a..9cbff83 100644 --- a/sources/templates/show_playlists_dialog.inc.php +++ b/sources/templates/show_playlists_dialog.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -23,7 +23,7 @@
        • - +
        • @@ -35,7 +35,7 @@ $playlist->format(); ?>
        • - + f_name; ?>
        • diff --git a/sources/templates/show_playtype_switch.inc.php b/sources/templates/show_playtype_switch.inc.php index 4ce9f08..0e82e7a 100644 --- a/sources/templates/show_playtype_switch.inc.php +++ b/sources/templates/show_playtype_switch.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_plugins.inc.php b/sources/templates/show_plugins.inc.php index 4d95330..cda3ecf 100644 --- a/sources/templates/show_plugins.inc.php +++ b/sources/templates/show_plugins.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -38,7 +38,7 @@ $web_path = AmpConfig::get('web_path'); foreach ($plugins as $plugin_name) { $plugin = new Plugin($plugin_name); $installed_version = Plugin::get_plugin_version($plugin->_plugin->name); - if (! $installed_version) { + if (!$installed_version) { $action = "" . T_('Activate') . ""; } else { diff --git a/sources/templates/show_popular.inc.php b/sources/templates/show_popular.inc.php index 344a861..9b5a0ad 100644 --- a/sources/templates/show_popular.inc.php +++ b/sources/templates/show_popular.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -22,25 +22,41 @@ UI::show_box_top(T_('Information')); -$sql = Stats::get_top_sql('song'); +$thresh_value = AmpConfig::get('stats_threshold'); + +$sql = Stats::get_top_sql('song', $thresh_value); $browse = new Browse(); +// We limit threshold for all items otherwise the counter will not be the same that the top_sql query. +// Example: Item '1234' => 3 counts during period with 'get_top_sql'. Without threshold, 'show_objects' would return the total which could be 24 during all time) +$browse->set_threshold($thresh_value); $browse->set_type('song', $sql); $browse->set_simple_browse(true); $browse->show_objects(); $browse->store(); -$sql = Stats::get_top_sql('album'); +$sql = Stats::get_top_sql('album', $thresh_value); $browse = new Browse(); +$browse->set_threshold($thresh_value); $browse->set_type('album', $sql); $browse->set_simple_browse(true); $browse->show_objects(); $browse->store(); -$sql = Stats::get_top_sql('artist'); +$sql = Stats::get_top_sql('artist', $thresh_value); $browse = new Browse(); +$browse->set_threshold($thresh_value); $browse->set_type('artist', $sql); $browse->set_simple_browse(true); $browse->show_objects(); $browse->store(); +if (AmpConfig::get('allow_video')) { + $sql = Stats::get_top_sql('video'); + $browse = new Browse(); + $browse->set_type('video', $sql); + $browse->set_simple_browse(true); + $browse->show_objects(null); + $browse->store(); +} + UI::show_box_bottom(); diff --git a/sources/templates/show_preference_admin.inc.php b/sources/templates/show_preference_admin.inc.php index fafe8b8..a378f01 100644 --- a/sources/templates/show_preference_admin.inc.php +++ b/sources/templates/show_preference_admin.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_preference_box.inc.php b/sources/templates/show_preference_box.inc.php index 9f6fc7e..0f8fca9 100644 --- a/sources/templates/show_preference_box.inc.php +++ b/sources/templates/show_preference_box.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -23,7 +23,7 @@ /* I'm cheating a little here, check to see if we want to show the * Apply to All button on this page */ -if ((Access::check('interface','100') OR !AmpConfig::get('use_auth')) AND $_REQUEST['action'] == 'admin') { +if (Access::check('interface','100') && $_REQUEST['action'] == 'admin') { $is_admin = true; } ?> diff --git a/sources/templates/show_preferences.inc.php b/sources/templates/show_preferences.inc.php index d8c1446..29ce46e 100644 --- a/sources/templates/show_preferences.inc.php +++ b/sources/templates/show_preferences.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_pvmsg.inc.php b/sources/templates/show_pvmsg.inc.php new file mode 100644 index 0000000..80a21d8 --- /dev/null +++ b/sources/templates/show_pvmsg.inc.php @@ -0,0 +1,45 @@ +f_subject, 'info-box'); +?> +
          + f_from_user_link . ' at ' . $pvmsg->f_creation_date; ?> +
          +
          +

          :

          + + +
          + +
          +
          + f_message); ?> +
          + + diff --git a/sources/templates/show_pvmsg_row.inc.php b/sources/templates/show_pvmsg_row.inc.php new file mode 100644 index 0000000..028cf2c --- /dev/null +++ b/sources/templates/show_pvmsg_row.inc.php @@ -0,0 +1,36 @@ + + +f_link; ?> +f_from_user_link; ?> +f_to_user_link; ?> +f_creation_date; ?> + + + + + + + + diff --git a/sources/templates/show_pvmsgs.inc.php b/sources/templates/show_pvmsgs.inc.php new file mode 100644 index 0000000..b29a03a --- /dev/null +++ b/sources/templates/show_pvmsgs.inc.php @@ -0,0 +1,85 @@ + + +
          +
            +
          • +
          • +
          • +
          • +
          +
          +get_show_header()) require AmpConfig::get('prefix') . '/templates/list_header.inc.php'; ?> + + + + + + + + + + + + + format(); + ?> + "> + + + + + + + + + + + + + + + + + + + +
          id . '&type=pvmsg&sort=subject', T_('Subject'),'pvmsg_sort_subject'); ?>id . '&type=pvmsg&sort=from_user', T_('Sender'),'pvmsg_sort_from_user'); ?>id . '&type=pvmsg&sort=to_user', T_('Recipient'),'pvmsg_sort_to_user'); ?>id . '&type=pvmsg&sort=creation_date', T_('Date'),'pvmsg_sort_creation_date'); ?>
          id . '&type=pvmsg&sort=subject', T_('Subject'),'pvmsg_sort_subject'); ?>id . '&type=pvmsg&sort=from_user', T_('Sender'),'pvmsg_sort_from_user'); ?>id . '&type=pvmsg&sort=to_user', T_('Recipient'),'pvmsg_sort_to_user'); ?>id . '&type=pvmsg&sort=creation_date', T_('Date'),'pvmsg_sort_creation_date'); ?>
          + + +get_show_header()) require AmpConfig::get('prefix') . '/templates/list_header.inc.php'; ?> diff --git a/sources/templates/show_random.inc.php b/sources/templates/show_random.inc.php index f7ca1ca..b88e371 100644 --- a/sources/templates/show_random.inc.php +++ b/sources/templates/show_random.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -111,7 +111,7 @@ $browse->save_objects($object_ids); $browse->show_objects(); $browse->store(); - echo Ajax::observe('window','load',Ajax::action('?action=refresh_rightbar','playlist_refresh_load')); + echo Ajax::observe('window', 'load',Ajax::action('?action=refresh_rightbar','playlist_refresh_load')); } ?>
      diff --git a/sources/templates/show_random_albums.inc.php b/sources/templates/show_random_albums.inc.php index 3a556e0..d09ff82 100644 --- a/sources/templates/show_random_albums.inc.php +++ b/sources/templates/show_random_albums.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -35,7 +35,7 @@ if ($albums) {
      - get_http_album_query_ids('album_id'),'play', T_('Play'),'play_album_' . $album->id); ?> + get_http_album_query_ids('object_id'),'play', T_('Play'),'play_album_' . $album->id); ?> - get_http_album_query_ids('album_id') . '&append=true','play_add', T_('Play last'),'addplay_album_' . $album->id); ?> + get_http_album_query_ids('object_id') . '&append=true','play_add', T_('Play last'),'addplay_album_' . $album->id); ?> get_http_album_query_ids('id'),'add', T_('Add to temporary playlist'),'play_full_' . $album->id); ?>
      id . "_album\">"; show_rating($album->id, 'album'); echo "
      "; diff --git a/sources/templates/show_random_videos.inc.php b/sources/templates/show_random_videos.inc.php new file mode 100644 index 0000000..a1fc4f8 --- /dev/null +++ b/sources/templates/show_random_videos.inc.php @@ -0,0 +1,61 @@ + + +format(); + ?> +
      +
      + get_release_item_art(); + Art::display($release_art['object_type'], $release_art['object_id'], $video->get_fullname(), 6, $video->link); + } else { ?> + get_fullname(); ?> + +
      +
      + + id,'play', T_('Play'),'play_album_' . $video->id); ?> + + id . '&append=true','play_add', T_('Play last'),'addplay_video_' . $video->id); ?> + + +
      + id . "_video\">"; + show_rating($video->id, 'video'); + echo "
      "; + } + ?> +
      + + + + diff --git a/sources/templates/show_recent.inc.php b/sources/templates/show_recent.inc.php index 0e815c2..ebb4666 100644 --- a/sources/templates/show_recent.inc.php +++ b/sources/templates/show_recent.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_recently_played.inc.php b/sources/templates/show_recently_played.inc.php index 5e6b27b..193b783 100644 --- a/sources/templates/show_recently_played.inc.php +++ b/sources/templates/show_recently_played.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -33,7 +33,9 @@ UI::show_box_top(T_('Recently Played') . $link, 'box box_recently_played'); + + @@ -56,37 +58,38 @@ foreach ($data as $row) { if ($is_allowed || $has_allowed_agent) { $agent = $row['agent']; + if (!empty($row['geo_name'])) { + $agent .= ' - ' . $row['geo_name']; + } } if ($is_allowed || $has_allowed_time) { $interval = intval(time() - $row['date']); if ($interval < 60) { - $unit = ngettext('second ago', 'seconds ago', $interval); - } else if ($interval < 3600) { + $time_string = sprintf(ngettext('%d second ago', '%d seconds ago', $interval), $interval); + } elseif ($interval < 3600) { $interval = floor($interval / 60); - $unit = ngettext('minute ago', 'minutes ago', $interval); - } else if ($interval < 86400) { + $time_string = sprintf(ngettext('%d minute ago', '%d minutes ago', $interval), $interval); + } elseif ($interval < 86400) { $interval = floor($interval / 3600); - $unit = ngettext('hour ago', 'hours ago', $interval); - } else if ($interval < 604800) { + $time_string = sprintf(ngettext('%d hour ago', '%d hours ago', $interval), $interval); + } elseif ($interval < 604800) { $interval = floor($interval / 86400); - $unit = ngettext('day ago', 'days ago', $interval); - } else if ($interval < 2592000) { + $time_string = sprintf(ngettext('%d day ago', '%d days ago', $interval), $interval); + } elseif ($interval < 2592000) { $interval = floor($interval / 604800); - $unit = ngettext('week ago', 'weeks ago', $interval); - } else if ($interval < 31556926) { + $time_string = sprintf(ngettext('%d week ago', '%d weeks ago', $interval), $interval); + } elseif ($interval < 31556926) { $interval = floor($interval / 2592000); - $unit = ngettext('month ago', 'months ago', $interval); - } else if ($interval < 631138519) { + $time_string = sprintf(ngettext('%d month ago', '%d months ago', $interval), $interval); + } elseif ($interval < 631138519) { $interval = floor($interval / 31556926); - $unit = ngettext('year ago', 'years ago', $interval); + $time_string = sprintf(ngettext('%d year ago', '%d years ago', $interval), $interval); } else { $interval = floor($interval / 315569260); - $unit = ngettext('decade ago', 'decades ago', $interval); + $time_string = sprintf(ngettext('%d decade ago', '%d decades ago', $interval), $interval); } - - $time_string = sprintf('%d ' . (T_ngettext($unit, $unit, $interval)), $interval); } $song->format(); ?> @@ -95,9 +98,12 @@ foreach ($data as $row) {  
      - id,'play', T_('Play'),'play_song_' . $nb . '_' . $song->id); ?> + id,'play', T_('Play'),'play_song_' . $nb . '_' . $song->id); ?> - id . '&append=true','play_add', T_('Play last'),'addplay_song_' . $nb . '_' . $song->id); ?> + id . '&append=true','play_add', T_('Play last'),'addplay_song_' . $nb . '_' . $song->id); ?> + + + id . '&playnext=true', 'play_next', T_('Play next'), 'nextplay_song_' . $nb . '_' . $song->id); ?>
      @@ -119,11 +125,13 @@ foreach ($data as $row) {
      + + + + @@ -158,7 +168,7 @@ foreach ($data as $row) {
      diff --git a/sources/templates/show_recommended_artists.inc.php b/sources/templates/show_recommended_artists.inc.php index 47192ff..cc0a86c 100644 --- a/sources/templates/show_recommended_artists.inc.php +++ b/sources/templates/show_recommended_artists.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -51,10 +51,10 @@ $thcount = 8; /* Foreach through every artist that has been passed to us */ foreach ($object_ids as $artist_id) { - $artist = new Artist($artist_id); - $artist->format(); + $libitem = new Artist($artist_id); + $libitem->format(); ?> - + diff --git a/sources/templates/show_registration_confirmation.inc.php b/sources/templates/show_registration_confirmation.inc.php index 2dbaf86..17a7073 100644 --- a/sources/templates/show_registration_confirmation.inc.php +++ b/sources/templates/show_registration_confirmation.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -22,38 +22,43 @@ $htmllang = str_replace("_","-",AmpConfig::get('lang')); $web_path = AmpConfig::get('web_path'); + +$_SESSION['login'] = true; ?> - - -<?php echo AmpConfig::get('site_title'); ?> - <?php echo T_('Registration'); ?> - - - - - - - - -
      - + + + + <?php echo AmpConfig::get('site_title'); ?> - <?php echo T_('Registration'); ?> + + + + + + +
      + + + + +
      + + +

      + +

      +
      +
      -

      - -
      -
      -

      Ampache
      -For the love of Music.

      -
      - - diff --git a/sources/templates/show_rules.inc.php b/sources/templates/show_rules.inc.php index 4a423d9..3af05e1 100644 --- a/sources/templates/show_rules.inc.php +++ b/sources/templates/show_rules.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -59,7 +59,7 @@ $logic_operator = strtolower($logic_operator); if ($playlist) { $out = $playlist->to_js(); } else { - $mysearch = new Search($_REQUEST['type']); + $mysearch = new Search(null, $_REQUEST['type']); $mysearch->parse_rules(Search::clean_request($_REQUEST)); $out = $mysearch->to_js(); } diff --git a/sources/templates/show_run_add_catalog.inc.php b/sources/templates/show_run_add_catalog.inc.php index a7311dc..01d80c8 100644 --- a/sources/templates/show_run_add_catalog.inc.php +++ b/sources/templates/show_run_add_catalog.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_search.inc.php b/sources/templates/show_search.inc.php index 95e0ae6..811f1ff 100644 --- a/sources/templates/show_search.inc.php +++ b/sources/templates/show_search.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -19,42 +19,53 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ - -UI::show_box_top(T_('Search Ampache') . "...", 'box box_advanced_search'); ?> - + + +
      +set_type('playlist_song'); + $browse->add_supplemental_object('search', $playlist->id); + $browse->set_static_content(false); + $browse->show_objects($object_ids); + $browse->store(); +?> +
      diff --git a/sources/templates/show_search_bar.inc.php b/sources/templates/show_search_bar.inc.php index bafa559..acce4c1 100644 --- a/sources/templates/show_search_bar.inc.php +++ b/sources/templates/show_search_bar.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -22,7 +22,7 @@ ?>
      - + @@ -33,6 +33,12 @@ + + + + + + diff --git a/sources/templates/show_search_descriptor.inc.php b/sources/templates/show_search_descriptor.inc.php index 879680a..857e47d 100644 --- a/sources/templates/show_search_descriptor.inc.php +++ b/sources/templates/show_search_descriptor.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_search_form.inc.php b/sources/templates/show_search_form.inc.php new file mode 100644 index 0000000..8e0d7af --- /dev/null +++ b/sources/templates/show_search_form.inc.php @@ -0,0 +1,60 @@ + + + + + + + + + + +
       
      + + + + + +
      + +
      + + + +
      +    + +    + + +
      +
      + diff --git a/sources/templates/show_search_options.inc.php b/sources/templates/show_search_options.inc.php index a86d8e0..eb15a72 100644 --- a/sources/templates/show_search_options.inc.php +++ b/sources/templates/show_search_options.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -27,9 +27,9 @@ id,'add', T_('Add Search Results'),'add_search_results'); ?>
    • - +
    • - +
    • diff --git a/sources/templates/show_smartplaylist_row.inc.php b/sources/templates/show_search_row.inc.php similarity index 51% rename from sources/templates/show_smartplaylist_row.inc.php rename to sources/templates/show_search_row.inc.php index 95302ec..f938f67 100644 --- a/sources/templates/show_smartplaylist_row.inc.php +++ b/sources/templates/show_search_row.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -24,34 +24,36 @@  
      - id,'play', T_('Play'),'play_playlist_' . $playlist->id); ?> + id,'play', T_('Play'),'play_playlist_' . $libitem->id); ?> - id . '&append=true','play_add', T_('Play last'),'addplay_playlist_' . $playlist->id); ?> + id . '&append=true','play_add', T_('Play last'),'addplay_playlist_' . $libitem->id); ?>
      -f_name_link; ?> +f_link; ?> - id,'add', T_('Add to temporary playlist'),'add_playlist_' . $playlist->id); ?> - + id,'add', T_('Add to temporary playlist'),'add_playlist_' . $libitem->id); ?> + -f_type; ?> -f_user); ?> +f_type; ?> +random ? T_('Yes') : T_('No')); ?> +limit > 0) ? $libitem->limit : T_('None')); ?> +f_user); ?> - - + + - has_access()) { ?> - + has_access()) { ?> + - id,'delete', T_('Delete'),'delete_playlist_' . $playlist->id); ?> + id,'delete', T_('Delete'),'delete_playlist_' . $libitem->id); ?> diff --git a/sources/templates/show_smartplaylist_title.inc.php b/sources/templates/show_search_title.inc.php similarity index 95% rename from sources/templates/show_smartplaylist_title.inc.php rename to sources/templates/show_search_title.inc.php index 65b0ad1..e197a05 100644 --- a/sources/templates/show_smartplaylist_title.inc.php +++ b/sources/templates/show_search_title.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_smartplaylists.inc.php b/sources/templates/show_searches.inc.php similarity index 79% rename from sources/templates/show_smartplaylists.inc.php rename to sources/templates/show_searches.inc.php index 1d020b7..3e60bc6 100644 --- a/sources/templates/show_smartplaylists.inc.php +++ b/sources/templates/show_searches.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -21,6 +21,15 @@ */ ?> +
      +
        + +
      • + +
      • + +
      +
      get_show_header()) require AmpConfig::get('prefix') . '/templates/list_header.inc.php' ?> @@ -29,6 +38,8 @@ + + @@ -36,11 +47,11 @@ format(); + $libitem = new Search($playlist_id, 'song'); + $libitem->format(); ?> - - + + @@ -55,6 +66,8 @@ + + diff --git a/sources/templates/show_share.inc.php b/sources/templates/show_share.inc.php index a6837d4..33e969a 100644 --- a/sources/templates/show_share.inc.php +++ b/sources/templates/show_share.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -22,23 +22,24 @@ $embed = $_REQUEST['embed']; -if (empty($embed)) { - UI::show_box_top(T_('Shared on') . ' ' . AmpConfig::get('site_title'), 'box box_share'); - echo T_('by') . ' ' . $share->f_user . '
      '; - echo "" . $share->public_url . "
      "; - echo "

      "; +$is_share = true; +$playlist = $share->create_fake_playlist(); +require AmpConfig::get('prefix') . '/templates/show_web_player.inc.php'; + +if (empty($embed)) { + echo "" .T_('Shared by') . ' ' . $share->f_user . "
      "; if ($share->allow_download) { echo "id . "&secret=" . $share->secret . "\">" . UI::get_icon('download', T_('Download')) . " "; echo "id . "&secret=" . $share->secret . "\">" . T_('Download') . ""; } } -$is_share = true; -$iframed = true; -$playlist = $share->create_fake_playlist(); -require AmpConfig::get('prefix') . '/templates/show_web_player.inc.php'; - if (!empty($embed)) { UI::show_box_bottom(); +} else { +?> + + + + + + + + + + + + + + + + diff --git a/sources/templates/show_shared_object_row.inc.php b/sources/templates/show_shared_object_row.inc.php deleted file mode 100644 index be73c5f..0000000 --- a/sources/templates/show_shared_object_row.inc.php +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - diff --git a/sources/templates/show_shared_objects.inc.php b/sources/templates/show_shared_objects.inc.php index 6c6eb82..cc5c5dc 100644 --- a/sources/templates/show_shared_objects.inc.php +++ b/sources/templates/show_shared_objects.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -32,7 +32,7 @@ - + @@ -40,11 +40,11 @@ format(); + $libitem = new Share($share_id); + $libitem->format(); ?> - - + + diff --git a/sources/templates/show_shares.inc.php b/sources/templates/show_shares.inc.php index b2e4123..0c4a6b1 100644 --- a/sources/templates/show_shares.inc.php +++ b/sources/templates/show_shares.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -21,5 +21,13 @@ */ ?> +
      +
        +
      • + +
      • +
      +
      + diff --git a/sources/templates/show_shout_row.inc.php b/sources/templates/show_shout_row.inc.php index 67efdbd..97d6066 100644 --- a/sources/templates/show_shout_row.inc.php +++ b/sources/templates/show_shout_row.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -20,17 +20,17 @@ * */ ?> - + - - - + + + diff --git a/sources/templates/show_shoutbox.inc.php b/sources/templates/show_shoutbox.inc.php index b84d80f..80338fd 100644 --- a/sources/templates/show_shoutbox.inc.php +++ b/sources/templates/show_shoutbox.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -25,8 +25,9 @@ format(); ?> -
      +
      get_display(true, true); ?>
      diff --git a/sources/templates/show_smartplaylist.inc.php b/sources/templates/show_smartplaylist.inc.php deleted file mode 100644 index 0589369..0000000 --- a/sources/templates/show_smartplaylist.inc.php +++ /dev/null @@ -1,60 +0,0 @@ - -id . '">' . $title . '
      ' , 'box box_smartplaylist'); -?> -
      -
        - -
      • - - -
      • - -
      • - id,'add', T_('Add All'),'play_playlist'); ?> - -
      • - has_access()) { ?> -
      • - - - - -
      • - -
      -
      - - - -
      - -
      - - - diff --git a/sources/templates/show_song.inc.php b/sources/templates/show_song.inc.php index b33600f..01fd49c 100644 --- a/sources/templates/show_song.inc.php +++ b/sources/templates/show_song.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -24,24 +24,26 @@ $icon = $song->enabled ? 'disable' : 'enable'; $button_flip_state_id = 'button_flip_state_' . $song->id; ?> title . ' ' . T_('Details'), 'box box_song_details'); ?> -
      +
      - - -
      -
      -
      id,'song'); ?> -
      -
      - + + + +
      +
      +
      id,'song'); ?> +
      +
      + - - -
      -
      -
      id,'song'); ?> -
      -
      + + +
      +
      +
      id,'song'); ?> +
      +
      + @@ -56,42 +58,76 @@ $button_flip_state_id = 'button_flip_state_' . $song->id;
      - id, 'play', T_('Play'),'play_song_' . $song->id); ?> + id, 'play', T_('Play'),'play_song_' . $song->id); ?> - id . '&append=true','play_add', T_('Play last'),'addplay_song_' . $song->id); ?> + id . '&append=true','play_add', T_('Play last'),'addplay_song_' . $song->id); ?> + + + id . '&playnext=true','play_next', T_('Play next'),'nextplay_song_' . $song->id); ?> show_custom_play_actions(); ?> id,'add', T_('Add to temporary playlist'),'add_song_' . $song->id); ?> - - - - - - - + + + + + + + + id, false); ?> + - - + + - + + + + + + user_upload == $GLOBALS['user']->id && AmpConfig::get('upload_allow_edit'))) { ?> + + + + + user_upload == $GLOBALS['user']->id && AmpConfig::get('upload_allow_edit'))) { ?> id,$icon, T_(ucfirst($icon)),'flip_song_' . $song->id); ?> + + + + +
      title); $songprops[gettext_noop('Artist')] = $song->f_artist_link; + if (!empty($song->f_albumartist_link)) { + $songprops[gettext_noop('Album Artist')] = $song->f_albumartist_link; + } $songprops[gettext_noop('Album')] = $song->f_album_link . ($song->year ? " (" . scrub_out($song->year). ")" : ""); - $songprops[gettext_noop('Genre')] = $song->f_genre_link; + $songprops[gettext_noop('Composer')] = scrub_out($song->composer); + $songprops[gettext_noop('Genre')] = $song->f_tags; + $songprops[gettext_noop('Year')] = $song->year; + $songprops[gettext_noop('Links')] = "f_artist) . "%22+%22" . rawurlencode($song->f_title) . "%22\" target=\"_blank\">" . UI::get_icon('google', T_('Search on Google ...')) . ""; + $songprops[gettext_noop('Links')] .= " f_artist) . "%22+%22" . rawurlencode($song->f_title) . "%22&type=track\" target=\"_blank\">" . UI::get_icon('lastfm', T_('Search on Last.fm ...')) . ""; $songprops[gettext_noop('Length')] = scrub_out($song->f_time); $songprops[gettext_noop('Comment')] = scrub_out($song->comment); - $songprops[gettext_noop('Label')] = scrub_out($song->label); + $songprops[gettext_noop('Label')] = AmpConfig::get('label') ? "label) . "\">" . scrub_out($song->label) . "" : scrub_out($song->label); $songprops[gettext_noop('Song Language')]= scrub_out($song->language); $songprops[gettext_noop('Catalog Number')] = scrub_out($song->catalog_number); $songprops[gettext_noop('Bitrate')] = scrub_out($song->f_bitrate); + $songprops[gettext_noop('Channels')] = scrub_out($song->channels); + if ($song->replaygain_track_gain != 0) { + $songprops[gettext_noop('ReplayGain Track Gain')] = scrub_out($song->replaygain_track_gain); + } + if ($song->replaygain_album_gain != 0) { + $songprops[gettext_noop('ReplayGain Album Gain')] = scrub_out($song->replaygain_album_gain); + } if (Access::check('interface','75')) { $songprops[gettext_noop('Filename')] = scrub_out($song->file) . " " . $song->f_size; } @@ -107,12 +143,28 @@ $button_flip_state_id = 'button_flip_state_' . $song->id; $songprops[gettext_noop('Lyrics')] = $song->f_lyrics; } - foreach ($songprops as $key => $value) { - if (trim($value)) { - $rowparity = UI::flip_class(); - echo "
      " . T_($key) . "
      " . $value . "
      "; - } + if (AmpConfig::get('licensing')) { + if ($song->license) { + $license = new License($song->license); + $license->format(); + $songprops[gettext_noop('Licensing')] = $license->f_link; } + } + + $owner_id = $song->get_user_owner(); + if (AmpConfig::get('sociable') && $owner_id > 0) { + $owner = new User($owner_id); + $owner->format(); + $songprops[gettext_noop('Uploaded by')] = $owner->f_link; + } + + + foreach ($songprops as $key => $value) { + if (trim($value)) { + $rowparity = UI::flip_class(); + echo "
      " . T_($key) . "
      " . $value . "
      "; + } + } ?>
      diff --git a/sources/templates/show_song_preview_row.inc.php b/sources/templates/show_song_preview_row.inc.php index 6377d54..da2c5df 100644 --- a/sources/templates/show_song_preview_row.inc.php +++ b/sources/templates/show_song_preview_row.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -22,28 +22,30 @@ ?>
      - + - - - - + + + + diff --git a/sources/templates/show_song_previews.inc.php b/sources/templates/show_song_previews.inc.php index ae500ed..e8bc2b2 100644 --- a/sources/templates/show_song_previews.inc.php +++ b/sources/templates/show_song_previews.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -38,9 +38,9 @@ - + diff --git a/sources/templates/show_song_row.inc.php b/sources/templates/show_song_row.inc.php index 5576c45..9bfafd4 100644 --- a/sources/templates/show_song_row.inc.php +++ b/sources/templates/show_song_row.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -21,63 +21,81 @@ */ ?> - + - - - - - - + + + + + + - - + + + + + + + - + diff --git a/sources/templates/show_songs.inc.php b/sources/templates/show_songs.inc.php index dd00abb..41df917 100644 --- a/sources/templates/show_songs.inc.php +++ b/sources/templates/show_songs.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -24,41 +24,46 @@ $web_path = AmpConfig::get('web_path'); $thcount = 8; ?> get_show_header()) require AmpConfig::get('prefix') . '/templates/list_header.inc.php'; ?> -
      id . '&type=smartplaylist&sort=name', T_('Playlist Name'),'playlist_sort_name'); ?> id . '&type=smartplaylist&sort=user', T_('Owner'),'playlist_sort_owner'); ?>
      id . '&type=playlist&sort=name', T_('Playlist Name'),'playlist_sort_name_bottom'); ?> id . '&type=playlist&sort=user', T_('Owner'),'playlist_sort_owner_bottom'); ?>
      f_object_link; ?>object_type; ?>f_user; ?>f_creation_date; ?>f_lastvisit_date; ?>counter; ?>max_counter; ?>f_allow_stream; ?>f_allow_download; ?>expire_days; ?>public_url; ?> +
      + show_action_buttons(); + ?> +
      +
      f_object_link; ?>object_type; ?>f_user; ?>f_creation_date; ?>f_lastvisit_date; ?>counter; ?>max_counter; ?>f_allow_stream; ?>f_allow_download; ?>expire_days; ?>public_url; ?> -
      - show_action_buttons(); - ?> -
      -
      id . '&type=share&sort=max_counter', T_('Max Counter'),'sort_share_max_counter'); ?> id . '&type=share&sort=allow_stream', T_('Allow Stream'),'sort_share_allow_stream'); ?> id . '&type=share&sort=allow_download', T_('Allow Download'),'sort_share_allow_download'); ?>id . '&type=share&sort=expire', T_('Expire Days'),'sort_share_expire'); ?>id . '&type=share&sort=expire', T_('Expiry Days'),'sort_share_expire'); ?>
      f_link; ?> f_link; ?>sticky; ?>text); ?>date; ?>sticky; ?>text); ?>date; ?> - + - +
      - file)) { ?> - id,'play_preview', T_('Play'),'play_song_' . $song->id); ?> + file) { ?> + id,'play_preview', T_('Play'),'play_song_' . $libitem->id); ?> - id . '&append=true','play_add_preview', T_('Play last'),'addplay_song_' . $song->id); ?> + id . '&append=true','play_add_preview', T_('Play last'),'addplay_song_' . $libitem->id); ?> title; ?>title; ?> - file)) { ?> - id,'add', T_('Add to temporary playlist'),'add_' . $song->id); ?> - - - + file) { ?> + id,'add', T_('Add to temporary playlist'),'add_' . $libitem->id); ?> + + + + + f_artist_link; ?>f_album_link; ?>track; ?>disk; ?>f_artist_link; ?>f_album_link; ?>track; ?>disk; ?>
      - '.$song->f_track.''; } ?> + '.$libitem->f_track.''; } ?>
      - id, 'play', T_('Play'), 'play_song_' . $song->id); ?> + id, 'play', T_('Play'), 'play_song_' . $libitem->id); ?> - id . '&append=true', 'play_add', T_('Play last'), 'addplay_song_' . $song->id); ?> + id . '&append=true', 'play_add', T_('Play last'), 'addplay_song_' . $libitem->id); ?> + + + id . '&playnext=true', 'play_next', T_('Play next'), 'nextplay_song_' . $libitem->id); ?>
      f_link; ?>f_link; ?> - id,'add', T_('Add to temporary playlist'),'add_' . $song->id); ?> - - - + id,'add', T_('Add to temporary playlist'),'add_' . $libitem->id); ?> + + + + + - show_custom_play_actions(); ?> + show_custom_play_actions(); ?> f_artist_link; ?>f_album_link; ?>f_tags; ?>f_time; ?>id,'song'); ?>f_artist_link; ?>f_album_link; ?>f_tags; ?>f_time; ?>object_cnt; ?> id,'song'); ?>id,'song'); ?>id,'song'); ?> - - - - - - - - + + + + + + + id, false); ?> + - - - + + + user_upload == $GLOBALS['user']->id && AmpConfig::get('upload_allow_edit'))) { ?> + - enabled ? 'disable' : 'enable'; ?> - + + user_upload == $GLOBALS['user']->id && AmpConfig::get('upload_allow_edit'))) { ?> + enabled ? 'disable' : 'enable'; ?> + id; ?> - id,$icon, T_(ucfirst($icon)),'flip_song_' . $song->id); ?> + id,$icon, T_(ucfirst($icon)),'flip_song_' . $libitem->id); ?> + + + + +
      +
      - + - - + + - - - - - - - - - - - + + + + + + + + + + + + + + + + format(); + $libitem = new Song($song_id, $limit_threshold); + $libitem->format(); ?> - + @@ -72,24 +77,30 @@ $thcount = 8; - + - - + + - - - - - - - - - - - + + + + + + + + + + + + + + + +
      id . '&sort=title', T_('Song Title'), 'sort_song_title'.$browse->id); ?>id . '&sort=title' . $argument_param, T_('Song Title'), 'sort_song_title'.$browse->id); ?> id . '&sort=artist', T_('Artist'), 'sort_song_artist'.$browse->id); ?>id . '&sort=album', T_('Album'), 'sort_song_album'.$browse->id); ?>id . '&sort=artist' . $argument_param, T_('Artist'), 'sort_song_artist'.$browse->id); ?>id . '&sort=album' . $argument_param, T_('Album'), 'sort_song_album'.$browse->id); ?> id . '&sort=time', T_('Time'), 'sort_song_time'.$browse->id); ?>id . '&sort=time' . $argument_param, T_('Time'), 'sort_song_time'.$browse->id); ?>
      id . '&sort=title', T_('Song Title'), 'sort_song_title'.$browse->id); ?>id . '&sort=title' . $argument_param, T_('Song Title'), 'sort_song_title'.$browse->id); ?> id . '&sort=artist', T_('Artist'), 'sort_song_artist'.$browse->id); ?>id . '&sort=album', T_('Album'), 'sort_song_album'.$browse->id); ?>id . '&sort=artist' . $argument_param, T_('Artist'), 'sort_song_artist'.$browse->id); ?>id . '&sort=album' . $argument_param, T_('Album'), 'sort_song_album'.$browse->id); ?> id . '&sort=time', T_('Time'), 'sort_song_time'.$browse->id); ?>id . '&sort=time' . $argument_param, T_('Time'), 'sort_song_time'.$browse->id); ?>
      - + + get_show_header()) require AmpConfig::get('prefix') . '/templates/list_header.inc.php'; ?> diff --git a/sources/templates/show_stats.inc.php b/sources/templates/show_stats.inc.php index b03d6e6..7c5c1e7 100644 --- a/sources/templates/show_stats.inc.php +++ b/sources/templates/show_stats.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -26,67 +26,80 @@ $catalogs = Catalog::get_catalogs(); - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + +

      -- - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + format(); $stats = Catalog::get_stats($catalog_id); ?> - - - - - - - - - - + + + + + + + + + + - +
      name; ?>f_path); ?>f_update); ?>f_add); ?>f_clean); ?>
      name; ?>f_path); ?>f_update); ?>f_add); ?>f_clean); ?>
      + + diff --git a/sources/templates/show_stats_highest.inc.php b/sources/templates/show_stats_highest.inc.php index 0fbd11f..8b9adaf 100644 --- a/sources/templates/show_stats_highest.inc.php +++ b/sources/templates/show_stats_highest.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -40,3 +40,12 @@ $browse->set_type('artist', $sql); $browse->set_simple_browse(true); $browse->show_objects(); $browse->store(); + +if (AmpConfig::get('allow_video')) { + $sql = Rating::get_highest_sql('video'); + $browse = new Browse(); + $browse->set_type('video', $sql); + $browse->set_simple_browse(true); + $browse->show_objects(); + $browse->store(); +} diff --git a/sources/templates/show_stats_newest.inc.php b/sources/templates/show_stats_newest.inc.php index 9d1e166..5b424af 100644 --- a/sources/templates/show_stats_newest.inc.php +++ b/sources/templates/show_stats_newest.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -33,3 +33,12 @@ $browse->set_type('artist', $sql); $browse->set_simple_browse(true); $browse->show_objects(); $browse->store(); + +if (AmpConfig::get('allow_video')) { + $sql = Stats::get_newest_sql('video'); + $browse = new Browse(); + $browse->set_type('video', $sql); + $browse->set_simple_browse(true); + $browse->show_objects(); + $browse->store(); +} diff --git a/sources/templates/show_stats_popular.inc.php b/sources/templates/show_stats_popular.inc.php index cb0072a..4b700b6 100644 --- a/sources/templates/show_stats_popular.inc.php +++ b/sources/templates/show_stats_popular.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -29,10 +29,18 @@ require AmpConfig::get('prefix') . '/templates/show_objects.inc.php'; UI::show_box_bottom(); $objects = Stats::get_top('artist'); -$headers = array('f_name_link' => T_('Most Popular Artists')); +$headers = array('f_link' => T_('Most Popular Artists')); UI::show_box_top('','info-box box_popular_artists'); require AmpConfig::get('prefix') . '/templates/show_objects.inc.php'; UI::show_box_bottom(); +if (AmpConfig::get('allow_video')) { + $objects = Stats::get_top('video'); + $headers = array('f_link' => T_('Most Popular Videos')); + UI::show_box_top('','info-box box_popular_videos'); + require AmpConfig::get('prefix') . '/templates/show_objects.inc.php'; + UI::show_box_bottom(); +} + ?>
    diff --git a/sources/templates/show_stats_recent.inc.php b/sources/templates/show_stats_recent.inc.php index 46f8651..ba16843 100644 --- a/sources/templates/show_stats_recent.inc.php +++ b/sources/templates/show_stats_recent.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -40,3 +40,12 @@ $browse->set_type('song', $sql); $browse->set_simple_browse(true); $browse->show_objects(); $browse->store(); + +if (AmpConfig::get('allow_video')) { + $sql = Stats::get_recent_sql('video', $user_id); + $browse = new Browse(); + $browse->set_type('video', $sql); + $browse->set_simple_browse(true); + $browse->show_objects(); + $browse->store(); +} diff --git a/sources/templates/show_stats_share.inc.php b/sources/templates/show_stats_share.inc.php index c2cdb0e..3cb0180 100644 --- a/sources/templates/show_stats_share.inc.php +++ b/sources/templates/show_stats_share.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_stats_upload.inc.php b/sources/templates/show_stats_upload.inc.php new file mode 100644 index 0000000..8cff5f1 --- /dev/null +++ b/sources/templates/show_stats_upload.inc.php @@ -0,0 +1,44 @@ +id); +$browse = new Browse(); +$browse->set_type('song', $sql); +$browse->set_simple_browse(true); +$browse->show_objects(); +$browse->store(); + +$sql = Catalog::get_uploads_sql('album', $GLOBALS['user']->id); +$browse = new Browse(); +$browse->set_type('album', $sql); +$browse->set_simple_browse(true); +$browse->show_objects(); +$browse->store(); + +if (!AmpConfig::get('upload_user_artist')) { + $sql = Catalog::get_uploads_sql('artist', $GLOBALS['user']->id); + $browse = new Browse(); + $browse->set_type('artist', $sql); + $browse->set_simple_browse(true); + $browse->show_objects(); + $browse->store(); +} diff --git a/sources/templates/show_stats_userflag.inc.php b/sources/templates/show_stats_userflag.inc.php index af96c1a..84852c7 100644 --- a/sources/templates/show_stats_userflag.inc.php +++ b/sources/templates/show_stats_userflag.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -40,3 +40,19 @@ $browse->set_type('artist', $sql); $browse->set_simple_browse(true); $browse->show_objects(); $browse->store(); + +if (AmpConfig::get('allow_video')) { + $sql = Userflag::get_latest_sql('video'); + $browse = new Browse(); + $browse->set_type('video', $sql); + $browse->set_simple_browse(true); + $browse->show_objects(); + $browse->store(); +} + +$sql = Userflag::get_latest_sql('playlist'); +$browse = new Browse(); +$browse->set_type('playlist', $sql); +$browse->set_simple_browse(true); +$browse->show_objects(); +$browse->store(); diff --git a/sources/templates/show_stats_wanted.inc.php b/sources/templates/show_stats_wanted.inc.php index 1740b78..dac9a56 100644 --- a/sources/templates/show_stats_wanted.inc.php +++ b/sources/templates/show_stats_wanted.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_tag_row.inc.php b/sources/templates/show_tag_row.inc.php new file mode 100644 index 0000000..f70ea1f --- /dev/null +++ b/sources/templates/show_tag_row.inc.php @@ -0,0 +1,23 @@ +name; diff --git a/sources/templates/show_tagcloud.inc.php b/sources/templates/show_tagcloud.inc.php index 918a139..f276a10 100644 --- a/sources/templates/show_tagcloud.inc.php +++ b/sources/templates/show_tagcloud.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -20,8 +20,31 @@ * */ +$tag_types = array( + 'song' => T_('Song'), + 'album' => T_('Album'), + 'artist' => T_('Artist'), + 'video' => T_('Video'), +); ?> + +
    + : + + +
    +
    @@ -32,7 +55,7 @@
    • - +
    • @@ -44,6 +67,18 @@
    + +


    + + + diff --git a/sources/templates/show_test.inc.php b/sources/templates/show_test.inc.php index 6c0308d..fa68af6 100644 --- a/sources/templates/show_test.inc.php +++ b/sources/templates/show_test.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -23,6 +23,7 @@ + Ampache -- Debug Page diff --git a/sources/templates/show_test_config.inc.php b/sources/templates/show_test_config.inc.php index 498efa3..d375efb 100644 --- a/sources/templates/show_test_config.inc.php +++ b/sources/templates/show_test_config.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -23,6 +23,7 @@ + Ampache -- Config Debug Page diff --git a/sources/templates/show_test_table.inc.php b/sources/templates/show_test_table.inc.php index 8e10fcb..0d43eed 100644 --- a/sources/templates/show_test_table.inc.php +++ b/sources/templates/show_test_table.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -95,10 +95,28 @@ - + - + + + + + + + + + + + + + + + + + + + @@ -107,25 +125,43 @@ - + - + - + - + - + + + + + + + + + + = 20M). This is not strictly necessary, but may result in a better experience.'); ?> + + + + + + + + + 2GB). This is not strictly necessary, but may result in a better experience. This generally requires 64-bit operating system.'); ?> +set_type($object_type); + +UI::show_box_top($tvshow->f_name, 'info-box'); +?> +
    + id, $tvshow->f_name, 6); + ?> + summary) { ?> +
    + summary; ?> +
    + +
    + + +
    + id, 'tvshow'); ?> +
    + + +
    + id,'tvshow'); ?> +
    + + +
    +

    :

    +
      + +
    • + id,'play', T_('Play all'),'directplay_full_' . $tvshow->id); ?> + id, T_('Play all'),'directplay_full_text_' . $tvshow->id); ?> +
    • + + +
    • + id . '&append=true','play_add', T_('Play all last'),'addplay_tvshow_' . $tvshow->id); ?> + id . '&append=true', T_('Play all last'),'addplay_tvshow_text_' . $tvshow->id); ?> +
    • + + +
    • + + + + + + +
    • + + +
    • + + + +
    • + +
    +
    + +
    +
    +
      +
    • + +
    • +
    +
    +
    +
    +show_objects($object_ids, true); + $browse->store(); +?> +
    +
    +
    diff --git a/sources/templates/show_tvshow_row.inc.php b/sources/templates/show_tvshow_row.inc.php new file mode 100644 index 0000000..802854c --- /dev/null +++ b/sources/templates/show_tvshow_row.inc.php @@ -0,0 +1,64 @@ + + +   +
    + + id,'play', T_('Play'),'play_tvshow_' . $libitem->id); ?> + + id . '&append=true','play_add', T_('Play last'),'addplay_tvshow_' . $libitem->id); ?> + + +
    + + + + id, $libitem->f_name, 6, $libitem->link); ?> + + +f_link; ?> +episodes; ?> +seasons; ?> +f_tags; ?> + + + id,'tvshow'); ?> + + + id,'tvshow'); ?> + + + + + + + + + + + + + + diff --git a/sources/templates/show_tvshow_season.inc.php b/sources/templates/show_tvshow_season.inc.php new file mode 100644 index 0000000..ef8b27e --- /dev/null +++ b/sources/templates/show_tvshow_season.inc.php @@ -0,0 +1,101 @@ + +set_type($object_type); + +UI::show_box_top($season->f_name . ' - ' . $season->f_tvshow_link, 'info-box'); +?> +
    + id, $season->f_name, 6); + ?> +
    + + +
    + id, 'tvshow_season'); ?> +
    + + +
    + id,'tvshow_season'); ?> +
    + + +
    +

    :

    +
      + +
    • + id,'play', T_('Play all'),'directplay_full_' . $season->id); ?> + id, T_('Play all'),'directplay_full_text_' . $season->id); ?> +
    • + + +
    • + id . '&append=true','play_add', T_('Play all last'),'addplay_season_' . $season->id); ?> + id . '&append=true', T_('Play all last'),'addplay_season_text_' . $season->id); ?> +
    • + + +
    • + + + + + + +
    • + + +
    • + + + +
    • + +
    +
    + +
    +
    +
      +
    • + +
    • +
    +
    +
    +
    +show_objects($object_ids, true); + $browse->store(); +?> +
    +
    +
    diff --git a/sources/templates/show_tvshow_season_row.inc.php b/sources/templates/show_tvshow_season_row.inc.php new file mode 100644 index 0000000..5b66459 --- /dev/null +++ b/sources/templates/show_tvshow_season_row.inc.php @@ -0,0 +1,63 @@ + + +   +
    + + id,'play', T_('Play'),'play_season_' . $libitem->id); ?> + + id . '&append=true','play_add', T_('Play last'),'addplay_season_' . $libitem->id); ?> + + +
    + + + + id, $libitem->f_name, 6, $libitem->link); ?> + + +f_link; ?> +f_tvshow_link; ?> +episodes; ?> + + + id,'tvshow_season'); ?> + + + id,'tvshow_season'); ?> + + + + + + + + + + + + + + diff --git a/sources/templates/show_tvshow_seasons.inc.php b/sources/templates/show_tvshow_seasons.inc.php new file mode 100644 index 0000000..4e3419e --- /dev/null +++ b/sources/templates/show_tvshow_seasons.inc.php @@ -0,0 +1,89 @@ + +get_show_header()) require AmpConfig::get('prefix') . '/templates/list_header.inc.php'; ?> + + + + + + + + + + + + + + + + + + + + + + + format(); + ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    id . '&sort=season', T_('Season'),'season_sort_season'); ?>id . '&sort=tvshow', T_('TV Show'),'season_sort_tvshow'); ?>
    id . '&sort=season', T_('Season'),'season_sort_name_bottom'); ?>id . '&sort=tvshow', T_('TV Show'),'season_sort_artist_bottom'); ?>
    + +get_show_header()) require AmpConfig::get('prefix') . '/templates/list_header.inc.php'; ?> diff --git a/sources/templates/show_tvshows.inc.php b/sources/templates/show_tvshows.inc.php new file mode 100644 index 0000000..c72481d --- /dev/null +++ b/sources/templates/show_tvshows.inc.php @@ -0,0 +1,95 @@ + +get_show_header()) require AmpConfig::get('prefix') . '/templates/list_header.inc.php'; ?> + + + + + + + + + + + + + + + + + + + + + + + + format(); + ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    id . '&type=tvshow&sort=name', T_('TV Show'),'tvshow_sort_name'); ?>
    id . '&type=tvshow&sort=name', T_('TV Show'),'tvshow_sort_name'); ?>
    + +get_show_header()) require AmpConfig::get('prefix') . '/templates/list_header.inc.php'; ?> diff --git a/sources/templates/show_update_items.inc.php b/sources/templates/show_update_items.inc.php index d2fa992..cc87fbf 100644 --- a/sources/templates/show_update_items.inc.php +++ b/sources/templates/show_update_items.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_uploads.inc.php b/sources/templates/show_uploads.inc.php new file mode 100644 index 0000000..0b39e7a --- /dev/null +++ b/sources/templates/show_uploads.inc.php @@ -0,0 +1,25 @@ + + + + diff --git a/sources/templates/show_user.inc.php b/sources/templates/show_user.inc.php index 0045cac..a5394c0 100644 --- a/sources/templates/show_user.inc.php +++ b/sources/templates/show_user.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -24,25 +24,65 @@ $last_seen = $client->last_seen ? date("m\/d\/y - H:i",$client->last_seen) : T_( $create_date = $client->create_date ? date("m\/d\/y - H:i",$client->create_date) : T_('Unknown'); $client->format(); ?> -fullname); ?> +f_name); ?> +
    f_avatar) { - echo '
    ' . $client->f_avatar . '
    '; + echo $client->f_avatar . "

    "; } ?> -
    +get_display_follow(); + + $plugins = Plugin::get_plugins('display_user_field'); +?> +
      +load($client)) { +?> +
    • _plugin->display_user_field(); ?>
    • + +
    + +
    +
    -
    -
    fullname; ?>
    +
    +
    + f_name; ?> + + + + + + + + + id == $GLOBALS['user']->id) { ?> + + +
    -
    +
    +
    -
    f_useage; ?>
    +
    + f_useage; ?> + + + +
    +
    @@ -54,27 +94,95 @@ if ($client->f_avatar) {

    - - - - - -
    - id)); - $object_ids = $tmp_playlist->get_items(); - foreach ($object_ids as $object_data) { - $type = array_shift($object_data); - $object = new $type(array_shift($object_data)); - $object->format(); - echo $object->f_link; ?> -
    + +
    +
    +
      +
    • + +
    • -

    - -id); - Song::build_cache(array_keys($data)); - $user_id = $client->id; - require AmpConfig::get('prefix') . '/templates/show_recently_played.inc.php'; -?> +
  • + +
  • +
  • + +
+
+
+
+ id)); + $object_ids = $tmp_playlist->get_items(); + if (count($object_ids) > 0) { + UI::show_box_top(T_('Active Playlist')); + ?> + + + + +
+ format(); + echo $object->f_link; ?> +
+ +

+ + + id); + Song::build_cache(array_keys($data)); + $user_id = $client->id; + require AmpConfig::get('prefix') . '/templates/show_recently_played.inc.php'; + ?> +
+ +
+ id); + $browse = new Browse(); + $browse->set_type('artist', $sql); + $browse->set_simple_browse(true); + $browse->show_objects(); + $browse->store(); + ?> +
+ +
+ id); + $browse = new Browse(); + $browse->set_type('playlist'); + $browse->set_simple_browse(false); + $browse->show_objects($playlist_ids); + $browse->store(); + ?> +
+ +
+ get_following(); + $browse = new Browse(); + $browse->set_type('user'); + $browse->set_simple_browse(false); + $browse->show_objects($following_ids); + $browse->store(); + ?> +
+
+ get_followers(); + $browse = new Browse(); + $browse->set_type('user'); + $browse->set_simple_browse(false); + $browse->show_objects($follower_ids); + $browse->store(); + ?> +
+ +
+
diff --git a/sources/templates/show_user_activate.inc.php b/sources/templates/show_user_activate.inc.php index 6dc4125..498ef98 100644 --- a/sources/templates/show_user_activate.inc.php +++ b/sources/templates/show_user_activate.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -25,23 +25,25 @@ $web_path = AmpConfig::get('web_path'); ?> - - -<?php echo AmpConfig::get('site_title'); ?> - <?php echo T_('Registration'); ?> - - - - - - - - - - -
+ + + + <?php echo AmpConfig::get('site_title'); ?> - <?php echo T_('Registration'); ?> + + + + + + +
+ + + + +

-
-
-

Ampache
-For the love of Music.

-
- - +
+
+ diff --git a/sources/templates/show_user_preferences.inc.php b/sources/templates/show_user_preferences.inc.php index c91e5ae..e4cb976 100644 --- a/sources/templates/show_user_preferences.inc.php +++ b/sources/templates/show_user_preferences.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_user_registration.inc.php b/sources/templates/show_user_registration.inc.php index 20e598f..d28cf77 100644 --- a/sources/templates/show_user_registration.inc.php +++ b/sources/templates/show_user_registration.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -22,99 +22,131 @@ $htmllang = str_replace("_","-",AmpConfig::get('lang')); $web_path = AmpConfig::get('web_path'); + +$display_fields = (array) AmpConfig::get('registration_display_fields'); +$mandatory_fields = (array) AmpConfig::get('registration_mandatory_fields'); + +$_SESSION['login'] = true; ?> + - - -<?php echo AmpConfig::get('site_title'); ?> - <?php echo T_('Registration'); ?> - - - - - - - + + + + <?php echo AmpConfig::get('site_title'); ?> - <?php echo T_('Registration'); ?> + + + + + + + + + -
- - + + +
+
+ +

+
+
+ +
-$action = scrub_in($_REQUEST['action']); -$fullname = scrub_in($_REQUEST['fullname']); -$username = scrub_in($_REQUEST['username']); -$email = scrub_in($_REQUEST['email']); -$website = scrub_in($_REQUEST['website']); -?> -
- - -

-
-
- -
+
+ + +
+
+ +

+
+ + + + +
+ +
+ + + +
+ -
- - -
-
- -

-
- - - - -
-
- - - -
+
+ + + +
+ +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ -
- - - -
-
- - - -
+
+ + + +
-
- - - -
+
+ + +
-
- - -
+
+
+ +
-
- -
+ - - - - - -
- - ' /> -
- +
+ + ' /> +
+ diff --git a/sources/templates/show_user_row.inc.php b/sources/templates/show_user_row.inc.php index de50683..b941555 100644 --- a/sources/templates/show_user_row.inc.php +++ b/sources/templates/show_user_row.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -21,42 +21,55 @@ */ ?> - + f_avatar_mini) { - echo $client->f_avatar_mini; +if ($libitem->f_avatar_mini) { + echo $libitem->f_avatar_mini; } ?> - fullname; ?> (username; ?>) + username; ?> + fullname_public || Access::check('interface', 100)) { + echo "(" . $libitem->fullname . ")"; + } ?> - - - f_useage; ?> + + + + f_useage; ?> - - - ip_history; ?> - - + + + ip_history; ?> + + + + + + get_display_follow(); ?> - - + + + + + + disabled == '1') { - echo "id\">" . UI::get_icon('enable', T_('Enable')) . ""; + if ($libitem->disabled == '1') { + echo "id\">" . UI::get_icon('enable', T_('Enable')) . ""; } else { - echo "id\">" . UI::get_icon('disable', T_('Disable')) .""; + echo "id\">" . UI::get_icon('disable', T_('Disable')) .""; } ?> - + + - is_logged_in()) AND ($client->is_online())) { + is_logged_in()) AND ($libitem->is_online())) { echo "   "; - } elseif ($client->disabled == 1) { + } elseif ($libitem->disabled == 1) { echo "   "; } else { echo "   "; diff --git a/sources/templates/show_userflag.inc.php b/sources/templates/show_userflag.inc.php index 3f24100..58e56a2 100644 --- a/sources/templates/show_userflag.inc.php +++ b/sources/templates/show_userflag.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_users.inc.php b/sources/templates/show_users.inc.php index da9145a..65381b3 100644 --- a/sources/templates/show_users.inc.php +++ b/sources/templates/show_users.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -29,35 +29,42 @@ $web_path = AmpConfig::get('web_path'); + - + - + + - id . '&type=user&sort=fullname', T_('Fullname'),'users_sort_fullname'); ?>( ) + id . '&type=user&sort=fullname', T_('Fullname'),'users_sort_fullname'); ?> () id . '&type=user&sort=last_seen', T_('Last Seen'),'users_sort_lastseen'); ?> id . '&type=user&sort=create_date', T_('Registration Date'),'users_sort_createdate'); ?> + - + - - + + + + + + format(); - $last_seen = $client->last_seen ? date("m\/d\/Y - H:i",$client->last_seen) : T_('Never'); - $create_date = $client->create_date ? date("m\/d\/Y - H:i",$client->create_date) : T_('Unknown'); + $libitem = new User($user_id); + $libitem->format(); + $last_seen = $libitem->last_seen ? date("m\/d\/Y - H:i",$libitem->last_seen) : T_('Never'); + $create_date = $libitem->create_date ? date("m\/d\/Y - H:i",$libitem->create_date) : T_('Unknown'); ?> - + @@ -67,11 +74,16 @@ foreach ($object_ids as $user_id) { id . '&type=user&sort=fullname', T_('Fullname'),'users_sort_fullname1'); ?>( ) id . '&type=user&sort=last_seen', T_('Last Seen'),'users_sort_lastseen1'); ?> id . '&type=user&sort=create_date', T_('Registration Date'),'users_sort_createdate1'); ?> + - + - - + + + + + + diff --git a/sources/templates/show_verify_catalog.inc.php b/sources/templates/show_verify_catalog.inc.php index 3bc7315..411f5fb 100644 --- a/sources/templates/show_verify_catalog.inc.php +++ b/sources/templates/show_verify_catalog.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -23,8 +23,6 @@ UI::show_box_top(T_('Verify Catalog'), 'box box_verify_catalog'); /* HINT: Catalog Name */ printf(T_('Updating the %s catalog'), "[ $this->name ]"); -echo "
\n"; -printf(ngettext('%d item found checking tag information', '%d items found checking tag information', $number), $number); echo "
\n\n"; echo T_('Verified') . ': ' . $catalog_verify_found . '
'; echo T_('Reading') . ': ' . $catalog_verify_directory . ''; diff --git a/sources/templates/show_video.inc.php b/sources/templates/show_video.inc.php new file mode 100644 index 0000000..a608d6c --- /dev/null +++ b/sources/templates/show_video.inc.php @@ -0,0 +1,142 @@ + +f_title . ' ' . T_('Details'), 'box box_video_details'); ?> +
+id, $video->f_title, 8, null, false, 'preview'); +} +if (!$gart) { + $gart = Art::display('video', $video->id, $video->f_title, 7); +} +?> + +
+: + +
+ +
+
+ + + +
+
+
id,'video'); ?> +
+
+ + + + +
+
+
id,'video'); ?> +
+
+ + + +
+
+ + id, 'play', T_('Play'),'play_video_' . $video->id); ?> + + id . '&append=true','play_add', T_('Play last'),'addplay_video_' . $video->id); ?> + + + + + + + + id, false); ?> + + + + + + + + + + + + + + + + + + + +
+f_title); + $videoprops[gettext_noop('Length')] = scrub_out($video->f_time); +if (!strtolower(get_class($video)) != 'video') { + require AmpConfig::get('prefix') . '/templates/show_partial_' . strtolower(get_class($video)) . '.inc.php'; +} + $videoprops[gettext_noop('Release Date')] = scrub_out($video->f_release_date); + $videoprops[gettext_noop('Codec')] = scrub_out($video->f_codec); + $videoprops[gettext_noop('Resolution')] = scrub_out($video->f_resolution); + $videoprops[gettext_noop('Display')] = scrub_out($video->f_display); + $videoprops[gettext_noop('Audio Bitrate')] = scrub_out($video->f_bitrate); + $videoprops[gettext_noop('Video Bitrate')] = scrub_out($video->f_video_bitrate); + $videoprops[gettext_noop('Frame Rate')] = scrub_out($video->f_frame_rate); + $videoprops[gettext_noop('Channels')] = scrub_out($video->channels); + if (Access::check('interface','75')) { + $videoprops[gettext_noop('Filename')] = scrub_out($video->file) . " " . $video->f_size; + } + if ($video->update_time) { + $videoprops[gettext_noop('Last Updated')] = date("d/m/Y H:i",$video->update_time); + } + $videoprops[gettext_noop('Added')] = date("d/m/Y H:i",$video->addition_time); + if (AmpConfig::get('show_played_times')) { + $videoprops[gettext_noop('# Played')] = scrub_out($video->object_cnt); + } + + foreach ($videoprops as $key => $value) { + if (trim($value)) { + $rowparity = UI::flip_class(); + echo "
" . T_($key) . "
" . $value . "
"; + } + } +?> +
+ diff --git a/sources/templates/show_video_row.inc.php b/sources/templates/show_video_row.inc.php index 838de25..ad10a5f 100644 --- a/sources/templates/show_video_row.inc.php +++ b/sources/templates/show_video_row.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -19,30 +19,84 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ +if (!isset($video_type)) { + $libitem = Video::create_from_id($libitem->id); + $libitem->format(); + $video_type = strtolower(get_class($libitem)); +} ?>  
- id,'play', T_('Play'),'play_video_' . $video->id); ?> + id,'play', T_('Play'),'play_video_' . $libitem->id); ?> - id . '&append=true','play_add', T_('Play last'),'addplay_video_' . $video->id); ?> + id . '&append=true','play_add', T_('Play last'),'addplay_video_' . $libitem->id); ?> + + + id . '&playnext=true', 'play_next', T_('Play next'), 'nextplay_video_' . $libitem->id); ?>
-f_title; ?> - - - id,'add', T_('Add to temporary playlist'),'add_video_' . $video->id); ?> - + + + get_default_art_kind() == 'preview') { + $art_showed = Art::display('video', $libitem->id, $libitem->f_title, 9, $libitem->link, false, 'preview'); + } + if (!$art_showed) { + Art::display('video', $libitem->id, $libitem->f_title, 6, $libitem->link); + } + ?> -f_codec; ?> -f_resolution; ?> -f_length; ?> -f_tags; ?> + +f_link; ?> + +f_release_date; ?> +f_codec; ?> +f_resolution; ?> +f_length; ?> + +object_cnt; ?> + +f_tags; ?> + + + id, 'video'); ?> + + + id, 'video'); ?> + + + + + + + + + id, false); ?> + + - + + + + + + + + + + + diff --git a/sources/templates/show_videos.inc.php b/sources/templates/show_videos.inc.php index f39aa15..b9357b4 100644 --- a/sources/templates/show_videos.inc.php +++ b/sources/templates/show_videos.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -27,12 +27,31 @@ if ($browse->get_show_header()) require AmpConfig::get('prefix') . '/templates/l + + + id . '&type=video&sort=title', T_('Title'),'sort_video_title'); ?> - + + id . '&type=video&sort=release_date', T_('Release Date'),'sort_video_release_date'); ?> id . '&type=video&sort=codec', T_('Codec'),'sort_video_codec'); ?> id . '&type=video&sort=resolution', T_('Resolution'),'sort_video_rez'); ?> id . '&type=video&sort=length', T_('Time'),'sort_video_length'); ?> + + + + + + + + + + + @@ -40,31 +59,54 @@ if ($browse->get_show_header()) require AmpConfig::get('prefix') . '/templates/l format(); + if (isset($video_type)) { + $libitem = new $video_type($video_id); + } else { + $libitem = new Video($video_id); + } + $libitem->format(); ?> - + - + + + + id . '&type=video&sort=title', T_('Title'),'sort_video_title'); ?> - + + id . '&type=video&sort=release_date', T_('Release Date'),'sort_video_release_date'); ?> id . '&type=video&sort=codec', T_('Codec'),'sort_video_codec'); ?> id . '&type=video&sort=resolution', T_('Resolution'),'sort_video_rez'); ?> id . '&type=video&sort=length', T_('Time'),'sort_video_length'); ?> + + + + + + + + + + + - + get_show_header()) require AmpConfig::get('prefix') . '/templates/list_header.inc.php'; ?> diff --git a/sources/templates/show_wanted.inc.php b/sources/templates/show_wanted.inc.php index f23b436..f767c09 100644 --- a/sources/templates/show_wanted.inc.php +++ b/sources/templates/show_wanted.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/templates/show_wanted_album_row.inc.php b/sources/templates/show_wanted_album_row.inc.php index a1fee76..4e0e326 100644 --- a/sources/templates/show_wanted_album_row.inc.php +++ b/sources/templates/show_wanted_album_row.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -21,14 +21,14 @@ */ ?> -f_name_link; ?> -f_artist_link; ?> -year; ?> -f_user; ?> +f_link; ?> +f_artist_link; ?> +year; ?> +f_user; ?> -
+
show_action_buttons(); + $libitem->show_action_buttons(); ?>
diff --git a/sources/templates/show_wanted_albums.inc.php b/sources/templates/show_wanted_albums.inc.php index 9a1f674..5b1ba2b 100644 --- a/sources/templates/show_wanted_albums.inc.php +++ b/sources/templates/show_wanted_albums.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -33,10 +33,10 @@ format(); + $libitem = new Wanted($wanted_id); + $libitem->format(); ?> - + diff --git a/sources/templates/show_web_player.inc.php b/sources/templates/show_web_player.inc.php index ab9140e..cb9ef1e 100644 --- a/sources/templates/show_web_player.inc.php +++ b/sources/templates/show_web_player.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2013 Ampache.org + * Copyright 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -28,52 +28,17 @@ header('Expires: ' . gmdate(DATE_RFC1123, time()-1)); + <?php echo AmpConfig::get('site_title'); ?> + + + + - - -'home', 'title' => T_('Home'), 'icon'=>'home', 'access'=>5); -$sidebar_items[] = array('id'=>'localplay', 'title' => T_('Localplay'), 'icon'=>'volumeup', 'access'=>5); -$sidebar_items[] = array('id'=>'preferences', 'title' => T_('Preferences'), 'icon'=>'edit', 'access'=>5); -$sidebar_items[] = array('id'=>'modules','title' => T_('Modules'),'icon'=>'plugin','access'=>100); -$sidebar_items[] = array('id'=>'admin', 'title' => T_('Admin'), 'icon'=>'admin', 'access'=>100); - -$web_path = AmpConfig::get('web_path'); ?> - + + diff --git a/sources/templates/sidebar.light.inc.php b/sources/templates/sidebar.light.inc.php new file mode 100644 index 0000000..762f68d --- /dev/null +++ b/sources/templates/sidebar.light.inc.php @@ -0,0 +1,33 @@ + + + diff --git a/sources/templates/sidebar_admin.inc.php b/sources/templates/sidebar_admin.inc.php index 4fe7df7..9f81f44 100644 --- a/sources/templates/sidebar_admin.inc.php +++ b/sources/templates/sidebar_admin.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -21,26 +21,28 @@ */ ?>
    -
  • +
  • <?php echo T_('Expand/Collapse'); ?>

  • -
  • +
  • <?php echo T_('Expand/Collapse'); ?>

  • -
  • +
  • <?php echo T_('Expand/Collapse'); ?>

  • -
  • + + +
  • <?php echo T_('Expand/Collapse'); ?>

    • @@ -48,10 +50,13 @@
    • + +
    • +
  • -
  • +
  • <?php echo T_('Expand/Collapse'); ?>

        -
      • +
      • -
          -
        • -
        • -
        • -
        • -
        • -
        • -
        • - -
        • +
            +
          • +
          • +
          • + +
          • + +
          • +
          • +
          • + +
          • + + +
          • + + +
          • + + +
          • -
          • -
          + +
        • +
            +
          • +
          • +
          • +
          • +
          • +
          +
        • + +
        • -

          -
            -
          • +

            <?php echo T_('Expand/Collapse'); ?>

            +
              + +
            • + -
            • +
            • current_instance(); - $class = $current_instance ? '' : ' class="active_instance"'; + // Little bit of work to be done here + $localplay = new Localplay(AmpConfig::get('localplay_controller')); + $current_instance = $localplay->current_instance(); + $class = $current_instance ? '' : ' class="active_instance"'; ?> -
            • +
            • -
            • +
            +
          • -

            +

            <?php echo T_('Expand/Collapse'); ?>

            • @@ -75,34 +99,43 @@
          • -

            +

            <?php echo T_('Expand/Collapse'); ?>

              -
            • -
            • - - -
            • +
            • +
            • + + + +
            • + + +
            • + + +
            • + + +
            • + + +
            • + + +
            • + - -
            • - - -
            • - - -
            • - -
          • -

            +

            <?php echo T_('Expand/Collapse'); ?>

          diff --git a/sources/templates/sidebar_localplay.inc.php b/sources/templates/sidebar_localplay.inc.php index 52cffb2..9225397 100644 --- a/sources/templates/sidebar_localplay.inc.php +++ b/sources/templates/sidebar_localplay.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -35,7 +35,7 @@ if ($server_allow && $controller && $access_check) { $class = $current_instance ? '' : ' class="active_instance"'; ?> -
        • +
        • <?php echo T_('Expand/Collapse'); ?>

          • @@ -45,7 +45,7 @@ if ($server_allow && $controller && $access_check) {
        • -
        • +
        • <?php echo T_('Expand/Collapse'); ?>

          • >
          • -
          • +
          • <?php echo T_('Expand/Collapse'); ?>

          • diff --git a/sources/templates/sidebar_modules.inc.php b/sources/templates/sidebar_modules.inc.php index 1b91bb4..cdf5985 100644 --- a/sources/templates/sidebar_modules.inc.php +++ b/sources/templates/sidebar_modules.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -21,14 +21,14 @@ */ ?>
              -
            • +
            • <?php echo T_('Expand/Collapse'); ?>

            • -
            • +
            • <?php echo T_('Expand/Collapse'); ?>

              • diff --git a/sources/templates/sidebar_preferences.inc.php b/sources/templates/sidebar_preferences.inc.php index 2c1a2a1..b2bc5b6 100644 --- a/sources/templates/sidebar_preferences.inc.php +++ b/sources/templates/sidebar_preferences.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -27,7 +27,7 @@ $catagories = Preference::get_catagories(); ?>
                  -
                • +
                • <?php echo T_('Expand/Collapse'); ?>

                    + diff --git a/sources/templates/subnavbar.inc.php b/sources/templates/subnavbar.inc.php index 01446be..c2a9306 100644 --- a/sources/templates/subnavbar.inc.php +++ b/sources/templates/subnavbar.inc.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/test.php b/sources/test.php index b06ccef..58a137d 100644 --- a/sources/test.php +++ b/sources/test.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/themes/classic/images/ajax-loader.gif b/sources/themes/classic/images/ajax-loader.gif deleted file mode 100644 index 62ece0b..0000000 Binary files a/sources/themes/classic/images/ajax-loader.gif and /dev/null differ diff --git a/sources/themes/classic/images/ampache.png b/sources/themes/classic/images/ampache.png deleted file mode 100644 index a106239..0000000 Binary files a/sources/themes/classic/images/ampache.png and /dev/null differ diff --git a/sources/themes/classic/images/bg_login.jpg b/sources/themes/classic/images/bg_login.jpg deleted file mode 100644 index ee10fe6..0000000 Binary files a/sources/themes/classic/images/bg_login.jpg and /dev/null differ diff --git a/sources/themes/classic/images/blank-pixel.gif b/sources/themes/classic/images/blank-pixel.gif deleted file mode 100644 index 17d4390..0000000 Binary files a/sources/themes/classic/images/blank-pixel.gif and /dev/null differ diff --git a/sources/themes/classic/images/blankalbum.gif b/sources/themes/classic/images/blankalbum.gif deleted file mode 100644 index a1d25b4..0000000 Binary files a/sources/themes/classic/images/blankalbum.gif and /dev/null differ diff --git a/sources/themes/classic/images/blankalbum.jpg b/sources/themes/classic/images/blankalbum.jpg deleted file mode 100644 index 33e89a0..0000000 Binary files a/sources/themes/classic/images/blankalbum.jpg and /dev/null differ diff --git a/sources/themes/classic/images/bottom.gif b/sources/themes/classic/images/bottom.gif deleted file mode 100644 index e154598..0000000 Binary files a/sources/themes/classic/images/bottom.gif and /dev/null differ diff --git a/sources/themes/classic/images/bottomleft.gif b/sources/themes/classic/images/bottomleft.gif deleted file mode 100644 index 940eeb3..0000000 Binary files a/sources/themes/classic/images/bottomleft.gif and /dev/null differ diff --git a/sources/themes/classic/images/bottomright.gif b/sources/themes/classic/images/bottomright.gif deleted file mode 100644 index 4e9caf4..0000000 Binary files a/sources/themes/classic/images/bottomright.gif and /dev/null differ diff --git a/sources/themes/classic/images/left.gif b/sources/themes/classic/images/left.gif deleted file mode 100644 index 0f9fb2d..0000000 Binary files a/sources/themes/classic/images/left.gif and /dev/null differ diff --git a/sources/themes/classic/images/right.gif b/sources/themes/classic/images/right.gif deleted file mode 100644 index c984eaa..0000000 Binary files a/sources/themes/classic/images/right.gif and /dev/null differ diff --git a/sources/themes/classic/images/rightbar_top.jpg b/sources/themes/classic/images/rightbar_top.jpg deleted file mode 100644 index 196fa41..0000000 Binary files a/sources/themes/classic/images/rightbar_top.jpg and /dev/null differ diff --git a/sources/themes/classic/images/sidebar_top.jpg b/sources/themes/classic/images/sidebar_top.jpg deleted file mode 100644 index 51ff93a..0000000 Binary files a/sources/themes/classic/images/sidebar_top.jpg and /dev/null differ diff --git a/sources/themes/classic/images/top.gif b/sources/themes/classic/images/top.gif deleted file mode 100644 index dbdde2c..0000000 Binary files a/sources/themes/classic/images/top.gif and /dev/null differ diff --git a/sources/themes/classic/images/topleft.gif b/sources/themes/classic/images/topleft.gif deleted file mode 100644 index 3a931ff..0000000 Binary files a/sources/themes/classic/images/topleft.gif and /dev/null differ diff --git a/sources/themes/classic/images/topright.gif b/sources/themes/classic/images/topright.gif deleted file mode 100644 index ea13e18..0000000 Binary files a/sources/themes/classic/images/topright.gif and /dev/null differ diff --git a/sources/themes/classic/preview.png b/sources/themes/classic/preview.png deleted file mode 100644 index 8ca9a46..0000000 Binary files a/sources/themes/classic/preview.png and /dev/null differ diff --git a/sources/themes/classic/templates/default.css b/sources/themes/classic/templates/default.css deleted file mode 100644 index 91df85b..0000000 --- a/sources/themes/classic/templates/default.css +++ /dev/null @@ -1,959 +0,0 @@ -/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ -/** - * - * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License v2 - * as published by the Free Software Foundation. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - */ - -/************************************************/ -/* Unify default browsers style rules */ -/************************************************/ -h1, h2, h3, h4, h5, h6, pre, code { font-size: 1em; line-height: 1em; } /* avoid browser default inconsistent font-sizes */ -ol, ul { list-style: none; } -table { border-collapse: separate; border-spacing: 0; } -caption, th, td { text-align: left; font-weight: normal; } -* { margin: 0; padding: 0; } /* White space reset */ -a img, :link img, :visited img { border: 0; } /* no blue linked image borders */ - - -/************************************************/ -/* General style rules */ -/************************************************/ -body { - background:#d3d3d3; - font-family:Arial, Helvetica, Sans-Serif; -} -p { - color: #000; - margin:1em 0; -} -a, a:visited, a:active{ - color: #000; - font-family: Verdana, Helvetica, sans-serif; -} -td { - padding: 0 8px; - color: #000; - font:12px Verdana, Helvetica, sans-serif; -} -th { - padding-right: 10px; - font-family: Verdana, Helvetica, sans-serif; - font-size: 13px; - font-weight: bold; -} -input, select, textarea{ - font: 12px Verdana, Helvetica, sans-serif; - background: #ddd; - color: #000; -} -input { - font-weight: bold; -} - -/***************************************************/ -/* IE6 behaviors */ -/* - Whatever:hover: :hover support on any element */ -/***************************************************/ -body { behavior:url("modules/whatever_hover/csshover3.htc"); } - -/************************************************/ -/* Float Clearer */ -/************************************************/ -/* float clearing for IE6 */ -* html .clearfix{ height: 1%; overflow: visible; } -/* float clearing for IE7 */ -/**+html .clearfix{ min-height: 1%; }*/ -/* float clearing for everyone else */ -.clearfix:after{ clear: both; content: "."; display: block; height: 0; visibility: hidden; } - -/************************************************/ -/* XSPF Player */ -/************************************************/ -.xspf_player { - right: 20px; - position: absolute; -} - -/************************************************/ -/* Main Container */ -/************************************************/ -#maincontainer{} - -/************************************************/ -/* Header */ -/************************************************/ -#header { - height: 85px; - padding: 3px 0 0 10px; -} -#headerbox { - float: right; - font-size: 10px; -} -#headerlogo, #headerlogo a{ - float: left; -} -.box_headerbox {display:table;} -.box_headerbox #loginInfo {font-weight:bold;display:block;text-align:right;margin-bottom:.3em;} - -#play_type_switch { - float:left; - margin-top:2px; -} - -/************************************************/ -/* Content block */ -/************************************************/ -#content { - margin: 20px 135px 20px 135px; -} - -/************************************************/ -/* Footer */ -/************************************************/ -#footer { - clear:both; - text-align:center; - font-size:10px; - padding:3px; -} - -/************************************************/ -/* Buttons */ -/************************************************/ -.button, input[type=button], input[type=submit] { - border: 1px solid #f0f0f0; - border-color: #f0f0f0 #a0a0a0 #a0a0a0 #f0f0f0; - background: #d0d0d0; - padding: 0 1px; - font-size: 11px; - font-weight:bold; - text-decoration:none; - cursor: pointer; -} -a.button{padding:1px 3px;} - -/************************************************/ -/* Sidebar */ -/************************************************/ -#sidebar{ - position:absolute; - top:100px; - left:0; - width:120px; - padding-top:28px; - background:#c0c0c0 url(../images/sidebar_top.jpg) no-repeat; - font-size:0.9em; -} - -#sidebar select { width: 95%; } -#sidebar input { vertical-align:middle; background:#fff; } -#sidebar ul { list-style:none; } -#sidebar a { text-decoration:none; } - - -/* For sidebar tabs */ -/********************/ -#sidebar-tabs{ - border-top:1px solid #8b8b8b; -} - -#sidebar-tabs li.sb1 { - float: left; - padding:2px; - background: #c0c0c0 ; -} -#sidebar-tabs li.active { - background: #fff url(../images/top.gif) repeat-x 0 0; - margin-top:8px; -} -#sidebar-tabs li.active img{ - margin-top:4px;position:relative;z-index:2; -} - -/* Tabs content */ -/****************/ -#sidebar-page { - position:absolute; - left:0; - top:52px; - background: #fff url(../images/bottom.gif) 0 100% repeat-x; - padding-bottom:0.5em; - border-right:2px solid #c0c0c0; - font-size: 0.8em; - width:118px; -} -#sidebar-page ul.sb2 { - padding:2px 4px; -} -#sidebar-page ul.sb2 li{ - font-weight:bold; - margin:1em auto; - padding-bottom: 0.5em; - border-bottom: 1px solid #666; -} -#sidebar-page ul.sb2 h4{ padding-bottom: 0.5em; } - -#sidebar-page ul.sb3, #sidebar-page div.sb3 { - font-size:0.9em; - margin-left:2px; - font-weight:normal; - color:#5b5b5b; -} -#sidebar-page div.sb3 input[type=radio] { - margin-left:2px; -} - -#sidebar-page ul.sb3 li{ - margin:0; - padding:0; - border:none; - font-weight:normal; -} -* html #sidebar-page ul.sb3 li{display:inline;} /* fix ie6 */ - -#sidebar-page .sb3 a{ - padding:0.1em; - border-bottom:1px dotted #c0c0c0; - color:#5b5b5b; -} - - -#sidebar-page a{ - display:block; -} -#sidebar-page a:hover{ - background:#99ccff; - color:#fff; -} - -/* SIDEBAR : Home */ -/******************/ - -/* SIDEBAR : Browse */ -/********************/ -.alphabet { - background:transparent; /* fix ie bug */ - font-size:0.95em; - font-weight:normal; - margin: 0.3em auto; - color:#5b5b5b; -} -.alphabet span.link { - cursor: pointer; - margin: 0; - padding:0 5px; - font-family: monospace, Courier, Georgia; -} -.alphabet span.active{ background:#5b5b5b; color:#fff; } -.alphabet span.link:hover { - background: #99ccff; - color:#fff; -} - -#multi_alpha_filter { - width:40px; - margin-bottom:4px; -} - -/* SIDEBAR : Localplay */ -/***********************/ -.active_instance { - background:#99ccff; -} - -/* SIDEBAR : Preferences */ -/*************************/ - -/* SIDEBAR : Admin */ -/*******************/ -#sb_admin_catalogs li.sb_admin_catalogs_ctrls img {margin:0;} -#sb_admin_catalogs li.sb_admin_catalogs_ctrls a{ - display:inline; - padding:0; - border:none; -} - - -/************************************************/ -/* XSPF Player */ -/************************************************/ -#xspf_player { - width:400px; - float: left; - background:#fff; - font-family: Verdana,Helvetica,sans-serif; -} - -/************************************************/ -/* Rightbar */ -/************************************************/ -#rightbar { - width:120px; - background:#fff; - padding-top:28px; - background:#c0c0c0 url(../images/rightbar_top.jpg) no-repeat; - float:right; - font-family: Verdana,Helvetica,sans-serif; - clear:both; -} -#rightbar ul { list-style:none; } -#rightbar a { text-decoration:none; } - - -/* Rightbar Menu */ -#rightbar #rb_action { - border-top:1px solid #8b8b8b; - padding:4px; -} -#rightbar #rb_action li { display:inline; } - -#rightbar li#rb_add, #rightbar li#pl_add { position:relative; z-index:10;} -#rightbar li#rb_add:hover, #rightbar li#pl_add:hover { background:#99ccff; } -/* Rightbar AddItems SubMenu */ -#rightbar li:hover .submenu { display:block; } -#rightbar .submenu { - display:none; - position:absolute; - right:0px; - top:18px; - background:#fff; - border:2px solid #c0c0c0; - width:120px; - font-size:0.7em; - padding:0.3em; -} -* html #rightbar .submenu {right:100px;} /* IE6 fix */ - -#rightbar .submenu a { - display:block; - padding:0.1em; - border-bottom:1px dotted #c0c0c0; - color:#5b5b5b; - text-decoration:none; - text-align:right; -} - -#rightbar .submenu a:hover, -#rightbar #rb_current_playlist a:hover { background:#99ccff; color:#fff; } - -/* Rightbar playlist */ -#rightbar #rb_current_playlist { - background: #fff url(../images/bottom.gif) 0 100% repeat-x; - padding-bottom:0.5em; - border-left:2px solid #c0c0c0; - border-right:2px solid #c0c0c0; -} -#rightbar #rb_current_playlist li { position:relative; font-size:0.6em;line-height:14px; color:#5b5b5b; padding-right:20px;} -#rightbar #rb_current_playlist li a { display:block; padding:0.2em;} -#rightbar .delitem { position:absolute;right:0;top:0; } - -/* Rightbar Localplay Controls */ -#rightbar #localplay-control { - padding-left: 5px; -} -#localplay-control span { - cursor: pointer; -} - - -/************************************************/ -/* Styles for the star ratings */ -/************************************************/ -.star-rating { - position:relative; -} -.dynamic-star-rating{ - width:95px; -} -.star-rating ul, -.star-rating a:hover, -.star-rating .current-rating{ - background: url(../../../images/ratings/star_rating.gif) left -1000px repeat-x; -} -.star-rating ul{ - position:relative; - width:80px; - height:15px; - overflow:hidden; - list-style:none; - margin:0; - padding:0; - background-position: left top; -} -.star-rating li{ - display: inline; -} -.star-rating a, .star-rating span, -.star-rating .current-rating{ - position:absolute; - top:0; - left:0; - text-indent:-1000em; - height:15px; - line-height:15px; - outline:none; - overflow:hidden; - border:none; -} -.star-rating .star1 { width:20%; z-index:6; } -.star-rating .star2 { width:40%; z-index:5; } -.star-rating .star3 { width:60%; z-index:4; } -.star-rating .star4 { width:80%; z-index:3; } -.star-rating .star5 { width:100%; z-index:2;} -.star-rating .current-rating { z-index:1; background-position: left bottom; } - -.star-rating a.star0 { - left:0px; - width:16px; - background: url(../../../images/ratings/x_off.gif) left top; -} - -/* hovering effect only for dynamic star rating */ -.dynamic-star-rating a:hover{ - background-position: left center; -} -.dynamic-star-rating a:hover.star0 { - background: url(../../../images/ratings/x.gif) left top; -} -.dynamic-star-rating ul { - left:16px; -} - -/************************************************/ -/* Styles for user flags */ -/************************************************/ -.userflag -{ - position: relative; - width:16px; - height:16px; -} - -.userflag a { - position:absolute; - display: inline; -} - -.userflag a.userflag_true -{ - width:16px; - height: 16px; - background: url(../../../images/icon_flag.png) left top; -} - -.userflag a:hover.userflag_true -{ - background: url(../../../images/icon_flag_off.png) left top; -} - -.userflag a.userflag_false -{ - width:16px; - height:16px; - background: url(../../../images/icon_flag_off.png) left top; -} - -.userflag a:hover.userflag_false -{ - background: url(../../../images/icon_flag.png) left top; -} - -/************************************************/ -/* Box Related Styles */ -/************************************************/ - -.box-title { - border-bottom: solid 1px #000; - font-size: 1.05em; - font-weight: bold; - margin: 0 10px; -} -.box-list { - padding-right: 10px; -} - -/* Enclosing Boxes Styles */ - -.box, .info-box { - background: #f7f7f7 url(../images/left.gif) top left repeat-y; - float:left; - clear:left; - height:1%; /* IE6 : Holly Hack comes to rescue once again */ -} -.box-inside { - background: url(../images/right.gif) top right repeat-y; -} -.box-content { - padding:8px 15px; -} - -.box-top { - background: url(../images/top.gif) 0 0 repeat-x; - position:relative; -} -.box-left-top { - background: url(../images/topleft.gif) no-repeat; - height:16px; - width:16px; - position:relative;left:0;top:0; -} -.box-right-top { - background: url(../images/topright.gif) no-repeat; - height:16px; - width:20px; - position:absolute;right:0;top:0; -} -* html .box-right-top {right: expression(-this.parentNode.offsetWidth%2+"px");} /* Fixes an IE6 rounding error */ -.box-bottom { - background: url(../images/bottom.gif) 0 0 repeat-x; - position:relative;clear:both; -} -.box-left-bottom { - background: url(../images/bottomleft.gif) no-repeat; - height:18px; - width:16px; - position:relative;left:0;top:0; -} -.box-right-bottom { - background: url(../images/bottomright.gif) no-repeat; - height:18px; - width:20px; - position:absolute;right:0;top:0; -} -* html .box-right-bottom {right: expression(-this.parentNode.offsetWidth%2+"px");} /* Fixes an IE6 rounding error */ - - -/* Specific to Info Boxes */ -.info-box { float:left;margin-right:10px; } -.album_art { float:left; margin-right:10px; } -#information_actions { margin-left:5px; font-size:0.7em; float:left; } -#information_actions h3 { font-size:1.2em; margin:0.2em; } - -.item_right_info { - float: right; - max-width: 60%; -} - -.external_links { - text-align: right; -} - -.external_links a { - margin: 0px 5px 0px 0px; - opacity: 0.3; -} - -.external_links a:hover { - opacity: 1; -} - -#artist_summary { - margin-right: 150px; -} - -/* Specific boxes */ -.box_newest_albums {} -.box_newest_artists {clear:none;} -.box_newest_genres {clear:none;} -.box_popular_album {} -.box_popular_artists {clear:none;} -.box_popular_genres {clear:none;} -.box_preferences h4 {color:#000;font:bold 14px Verdana, Helvetica, sans-serif;padding:0.8em 0;} - - -/************************************************/ -/* Tables (songs lists...) */ -/************************************************/ -.tabledata .th-top, .tabledata .th-bottom { - font-size:1.1em; - vertical-align: top; - text-align:center; -} - -.tableform select { - width: 150px; -} - -/* table rows */ -.tabledata .odd, .tabledata .odd td, -.tabledata .even, .tabledata .even td, .row-highlight { - font-size: 12px; - border-bottom:1px dotted #c0c0c0; -} -.tabledata .even:hover, .tabledata .odd:hover { - background:#9cf; -} - -table.tabledata .cel_drag { - max-width: 16px; - width: 16px !important; -} - -table.tabledata .cel_drag img:hover { - cursor: pointer; -} - -table.tabledata .cel_agent { - text-align: right; -} - -table.tabledata .cel_agent img:hover { - cursor: help; -} - -.row-highlight:hover { - background:#c33; -} - -/* Misc */ -.border { background: #000; } -.tabledata input, .tabledata select{ margin:2px 0;} - -/* specific cells */ -td.cel_cover{padding:6px;} -.cel_select, .cel_action, .cel_date, .cel_applytoall, .cel_level {text-align:center;} -/* specific cells : users login state */ -.user_online{background:#0f0;} -.user_offline{background:#7f0000;} -.user_disabled{background:#ccc;} - -/* specific tables */ -#recently_played .th-bottom {display:none;} -.box_preferences .th-bottom {display:none;} - -/* Inline Editing Tables */ -.inline-edit input, .inline-edit select { - font-size: 0.8em; -} - -/************************************************/ -/* Song details */ -/************************************************/ -dl.song_details{font-size:0.8em;} -.song_details dt { - float:left; - clear:both; - width:20%; - min-width:20%; /*Ie bugfix*/ - font-weight:bold; -} -.song_details dd { - float:left; - width:79%; - min-width:79%; /*Ie bugfix*/ - margin:0 0 0.2em .3em; - padding-left:.2em; -} -dt + dd {border-bottom:1px dotted #c0c0c0;} -dt:hover, dt:hover + dd {background:#9cf;} - -/************************************************/ -/* Albums of the moment */ -/************************************************/ -.random_album{ - position:relative; - float:left; - padding:8px; - width:80px; -} - -.random_album .play_album{ - display:none; -} - -#random_selection .art_album img { - width: 80px; - height: 80px; -} - -/************************************************/ -/* Now Playing */ -/************************************************/ -#now_playing{ -} - -.np_row { - padding: 3px; - float:left; - font-size:0.75em; - display:block; - -} -.np_cell { - padding-left:5px; - margin-left:5px; -} - -.np_row label { - display:block; - font-weight:bold; - margin:2px 0 0 -5px; -} - -.np_group { - float:left; - padding-right:15px; -} - -.np_row a { - font-size:0.8em; -} - -/************************************************/ -/* Shoutbox */ -/************************************************/ - -#shoutbox { - font-size:0.8em; -} - -#shoutbox div.shout { - padding-top:0.5em; - margin:10px 5px 0 0; - border-top:1px dotted #c0c0c0; -} -#shoutbox div.shout:hover{border-top:1px solid #9cf;} - -#shoutbox span.information {} -#shoutbox .shouttext{display:block;font-size:.9em;margin-top:.5em;} -img.shoutboximage { margin:0 3px;} - -#shoutbox div.odd{margin-right:20%;text-align:left;} -#shoutbox div.even{margin-left:20%;text-align:right;} -#shoutbox .odd img.shoutboximage {float:left;} -#shoutbox .even img.shoutboximage {float:right;} - -/************************************************/ -/* List Header */ -/************************************************/ -.list-header{margin:7px 0; padding:0 4em; text-align:center; font-size: 0.8em;position:relative;} -.list-header .prev{position:absolute; top:0; left:0;} -.list-header .next{position:absolute; top:0; right:0;} -.list-header .selected{background: #e0e0e0;} -.list-header .page-nb{padding:1px;border: 1px solid #ccc;text-decoration: none;} -.list-header .page-nb:hover{background: #d0d0d0;} - -/************************************************/ -/* Errors */ -/************************************************/ -.error { - color:#903; -} - -.fatalerror .nodata { - display:table-cell; - padding:3px; - color:#903; - font-weight:bold; - font-size:1.2em; -} - -/************************************************/ -/* LocalPlay */ -/************************************************/ -.lp_box_ctrl, .lp_box_vol { - text-align: center; /*for compatibility, may be controlled by themers now*/ -} - -td.lp_current a { - font-weight:bold; - text-decoration:none; -} - -/************************************************/ -/* Web Player */ -/************************************************/ -#web_player #playlist -{ - overflow-x: hidden; - overflow-y: scroll; - position: absolute; - top: 105px; - left: 210px; - right: 5px; - bottom: 5px; -} - -/************************************************/ -/* Styles for Login template */ -/************************************************/ -#loginPage #maincontainer{ - margin:100px auto 0 auto; - width:437px; - font-size:12px; - text-align:center; -} -#loginPage #header{ - padding:0; -} - -#loginPage #loginbox{ - background:url(../images/bg_login.jpg) no-repeat; - height:292px; -} -#loginPage h2{ - color:#333; - padding-top:60px; - font-weight: normal; -} -.loginfield{ - text-align:right; - padding-right:100px; - margin: 1em 0; -} -.loginfield input.text_input{ - width:12em; - border:1px solid #999; -} -.loginfield label{ - font-weight:bold; -} -.loginfield #rememberme{ - margin-right:5em; - background:none; -} - -#loginPage div.fatalerror { - padding:5px; - margin:10px; -} - -#motd { - margin:0 auto 0 auto; - width: 437px; -} - -/************************************************/ -/* Styles for Registration template */ -/************************************************/ -/* taken direcly from install.css */ - -#registerPage { - background: #FFFFFF url(../../../images/top_bg.jpg) repeat-x top left; -} - -#registerPage #header { - width: 90%; - min-height: 85px; - height: 85px; - margin: 0 auto; - padding: 0; - text-align: left; - font-size: smaller; - display: block; -} - -#registerPage #header h1 { - margin: 0; - padding-top: 16px; - color: #000; - font-weight: bold; - font-size: large; -} -#registerPage h3 { - margin-left: 50px; -} - -#registerPage input, radio, select, textarea { - border: 1px solid #ddd; - background: #f2f2f2; - font: 11px verdana, sans-serif; - color: #443; - padding: 2px; - outline: none; -} -#registerPage input:hover, -textarea:hover, -input:focus, -textarea:focus{ - border: 1px solid #aaa; -} - -#registerPage .registerfield { - text-align:left; - margin: 1em 0; - margin-left: 50px; -} - -#registerPage .registerfield.require .asterix { - color: red; - text-align: right; -} - -#registerPage .registerfield .error { - font-size: 13px; - right: 0px; -} - -#registerPage label { - font-weight:bold; - float: left; - width: 150px; - font-size: 13px; -} - -#registerPage #registerbox { - width: 700px; -} - -#registerPage .registerInformation { - font-size: 13px; - color: red; - margin-left: 200px; -} - -#registerPage .registerButtons { - margin-top: 10px; - margin-left: 200px; -} - - -/************************************************/ -/* Misc */ -/************************************************/ -.formValidation{ - margin-top:1em; - text-align:center; -} - -.text-box, .confirmation-box { - display:table-cell; - padding:5px 5px 0 5px; - margin-bottom:10px; - background:#bbb; - border:2px solid #000; -} - -#ajax-loading { - position: absolute; - top:42px; - left:345px; - width:43px; - height:11px; - z-index:100; - background: url(../images/ajax-loader.gif) no-repeat; - display: none; - text-indent:-9999em; -} - -.information,.information a { - font-size: 0.9em; - font-style: italic; - color: #c0c0c0; -} diff --git a/sources/themes/classic/theme.cfg.php b/sources/themes/classic/theme.cfg.php deleted file mode 100644 index b90d1b9..0000000 --- a/sources/themes/classic/theme.cfg.php +++ /dev/null @@ -1,52 +0,0 @@ -;;;;;;;;;;;;;;;;;; -;; -;;;;;;;;;;;;;;;;;; -; Copyright (c) 2001 - 2013 Ampache.org -; -; This program is free software; you can redistribute it and/or -; modify it under the terms of the GNU General Public License v2 -; as published by the Free Software Foundation. -; -; 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, write to the Free Software -; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -; -;;;;;;;;;;;;;;;;;;;;;;;;;;; -; Classic Ampache Theme -;;;;;;;;;;;;;;;;;;;;;;;;;;; - -; Theme Name -; This is the actual name of the theme that -; will be displayed in the preferences screen -; DEFAULT: ampache-theme -name = "Classic Ampache" - -; Theme Author -; This is just a way of giving credit to the -; person who actually created this theme -; DEFAULT: N/A -author = "Ros" - -; Theme Maintainer -; This is just a way of listing who is responsible for -; maintaining this theme in case it's not working right -; please include an e-mail address so you can be contacted -; DEFAULT: N/A -maintainer = "Spocky" - -; Orientation -; This was added as of 3.3.2-Alpha4, this tells Ampache if this theme -; uses vertical or horizontal orientation of the menu, if this is a horizontal -; theme then it will not show the quick search and quick random play forms -orientation = "vertical" - -; Submenu -; If this is set to simple the sub menu's will only be shown when you're on one of the -; respective pages. If you want to make the menu's something like the classic theme -; comment this out -;submenu = "simple" diff --git a/sources/themes/fresh/ampache.psd b/sources/themes/fresh/ampache.psd deleted file mode 100644 index fe20752..0000000 Binary files a/sources/themes/fresh/ampache.psd and /dev/null differ diff --git a/sources/themes/fresh/images/ajax-loader.gif b/sources/themes/fresh/images/ajax-loader.gif deleted file mode 100644 index b20f505..0000000 Binary files a/sources/themes/fresh/images/ajax-loader.gif and /dev/null differ diff --git a/sources/themes/fresh/images/ajax-loader2.gif b/sources/themes/fresh/images/ajax-loader2.gif deleted file mode 100644 index aaa180c..0000000 Binary files a/sources/themes/fresh/images/ajax-loader2.gif and /dev/null differ diff --git a/sources/themes/fresh/images/ampache.png b/sources/themes/fresh/images/ampache.png deleted file mode 100644 index bf502db..0000000 Binary files a/sources/themes/fresh/images/ampache.png and /dev/null differ diff --git a/sources/themes/fresh/images/blank-pixel.gif b/sources/themes/fresh/images/blank-pixel.gif deleted file mode 100644 index 17d4390..0000000 Binary files a/sources/themes/fresh/images/blank-pixel.gif and /dev/null differ diff --git a/sources/themes/fresh/images/blankalbum.jpg b/sources/themes/fresh/images/blankalbum.jpg deleted file mode 100644 index 33e89a0..0000000 Binary files a/sources/themes/fresh/images/blankalbum.jpg and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_add.png b/sources/themes/fresh/images/icons/icon_add.png deleted file mode 100755 index da42e17..0000000 Binary files a/sources/themes/fresh/images/icons/icon_add.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_add12.png b/sources/themes/fresh/images/icons/icon_add12.png deleted file mode 100755 index 6bbba51..0000000 Binary files a/sources/themes/fresh/images/icons/icon_add12.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_add2.png b/sources/themes/fresh/images/icons/icon_add2.png deleted file mode 100755 index 1138739..0000000 Binary files a/sources/themes/fresh/images/icons/icon_add2.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_add_user.png b/sources/themes/fresh/images/icons/icon_add_user.png deleted file mode 100755 index 9f6c0f5..0000000 Binary files a/sources/themes/fresh/images/icons/icon_add_user.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_admin.png b/sources/themes/fresh/images/icons/icon_admin.png deleted file mode 100755 index ee0c771..0000000 Binary files a/sources/themes/fresh/images/icons/icon_admin.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_all.png b/sources/themes/fresh/images/icons/icon_all.png deleted file mode 100755 index 2dfaef5..0000000 Binary files a/sources/themes/fresh/images/icons/icon_all.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_batch_download.png b/sources/themes/fresh/images/icons/icon_batch_download.png deleted file mode 100755 index 67a3d9a..0000000 Binary files a/sources/themes/fresh/images/icons/icon_batch_download.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_delete.png b/sources/themes/fresh/images/icons/icon_delete.png deleted file mode 100755 index 6b9fa6d..0000000 Binary files a/sources/themes/fresh/images/icons/icon_delete.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_disable.png b/sources/themes/fresh/images/icons/icon_disable.png deleted file mode 100755 index 7af3a51..0000000 Binary files a/sources/themes/fresh/images/icons/icon_disable.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_edit.png b/sources/themes/fresh/images/icons/icon_edit.png deleted file mode 100755 index 0699492..0000000 Binary files a/sources/themes/fresh/images/icons/icon_edit.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_edit2.png b/sources/themes/fresh/images/icons/icon_edit2.png deleted file mode 100755 index 7dc0d54..0000000 Binary files a/sources/themes/fresh/images/icons/icon_edit2.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_enable.png b/sources/themes/fresh/images/icons/icon_enable.png deleted file mode 100755 index 210b1a6..0000000 Binary files a/sources/themes/fresh/images/icons/icon_enable.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_feed.png b/sources/themes/fresh/images/icons/icon_feed.png deleted file mode 100755 index cdf4e8f..0000000 Binary files a/sources/themes/fresh/images/icons/icon_feed.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_home.png b/sources/themes/fresh/images/icons/icon_home.png deleted file mode 100755 index 622a2b7..0000000 Binary files a/sources/themes/fresh/images/icons/icon_home.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_logout.png b/sources/themes/fresh/images/icons/icon_logout.png deleted file mode 100755 index 2bc51ac..0000000 Binary files a/sources/themes/fresh/images/icons/icon_logout.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_next.png b/sources/themes/fresh/images/icons/icon_next.png deleted file mode 100755 index 7ae440a..0000000 Binary files a/sources/themes/fresh/images/icons/icon_next.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_pause.png b/sources/themes/fresh/images/icons/icon_pause.png deleted file mode 100755 index af57b25..0000000 Binary files a/sources/themes/fresh/images/icons/icon_pause.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_play.png b/sources/themes/fresh/images/icons/icon_play.png deleted file mode 100755 index 2dfaef5..0000000 Binary files a/sources/themes/fresh/images/icons/icon_play.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_playlist_add.png b/sources/themes/fresh/images/icons/icon_playlist_add.png deleted file mode 100755 index df35ed6..0000000 Binary files a/sources/themes/fresh/images/icons/icon_playlist_add.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_plugin.png b/sources/themes/fresh/images/icons/icon_plugin.png deleted file mode 100755 index 0f3736f..0000000 Binary files a/sources/themes/fresh/images/icons/icon_plugin.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_prev.png b/sources/themes/fresh/images/icons/icon_prev.png deleted file mode 100755 index a39522f..0000000 Binary files a/sources/themes/fresh/images/icons/icon_prev.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_random.png b/sources/themes/fresh/images/icons/icon_random.png deleted file mode 100755 index ab3dd30..0000000 Binary files a/sources/themes/fresh/images/icons/icon_random.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_stop.png b/sources/themes/fresh/images/icons/icon_stop.png deleted file mode 100755 index 7c6af7f..0000000 Binary files a/sources/themes/fresh/images/icons/icon_stop.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_view.png b/sources/themes/fresh/images/icons/icon_view.png deleted file mode 100755 index b4b2312..0000000 Binary files a/sources/themes/fresh/images/icons/icon_view.png and /dev/null differ diff --git a/sources/themes/fresh/images/icons/icon_volumeup.png b/sources/themes/fresh/images/icons/icon_volumeup.png deleted file mode 100755 index 62fcfc6..0000000 Binary files a/sources/themes/fresh/images/icons/icon_volumeup.png and /dev/null differ diff --git a/sources/themes/fresh/images/ratings/star_rating.gif b/sources/themes/fresh/images/ratings/star_rating.gif deleted file mode 100644 index 55ac1f5..0000000 Binary files a/sources/themes/fresh/images/ratings/star_rating.gif and /dev/null differ diff --git a/sources/themes/fresh/images/ratings/star_rating.png b/sources/themes/fresh/images/ratings/star_rating.png deleted file mode 100644 index 6c75aed..0000000 Binary files a/sources/themes/fresh/images/ratings/star_rating.png and /dev/null differ diff --git a/sources/themes/fresh/preview.png b/sources/themes/fresh/preview.png deleted file mode 100644 index 529c901..0000000 Binary files a/sources/themes/fresh/preview.png and /dev/null differ diff --git a/sources/themes/fresh/templates/default.css b/sources/themes/fresh/templates/default.css deleted file mode 100644 index d7e1e40..0000000 --- a/sources/themes/fresh/templates/default.css +++ /dev/null @@ -1,1255 +0,0 @@ -/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ -/** - * - * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License v2 - * as published by the Free Software Foundation. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - */ - -/*********************************************** - General style rules -***********************************************/ -html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td -{ - border:0; - outline:0; - font-size:100%; - vertical-align:baseline; - background:transparent; - margin:0; - padding:0; -} - -body -{ - line-height:1; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size:12px; - color:#111; - background:#e8e8e8; -} - -blockquote,q -{ - quotes:none; -} - -blockquote:before,blockquote:after,q:before,q:after -{ - content:none; -} - -:focus -{ - outline:0; -} - -del -{ - text-decoration:line-through; -} - -table -{ - border-collapse:collapse; - border-spacing:0; -} - -.error -{ - color:#c33; -} - -a -{ - color:#111; - text-decoration:none; -} - -h3 -{ - font-size:20px; - margin-bottom: 5px; -} - -hr { - border-top: 1px solid #bbb; - border-bottom: 1px solid #eee; - border-right: 0; - border-left: 0; -} - -/*********************************************** - Wrappers -***********************************************/ -#maincontainer -{ - width:100%; -} - -#sidebar -{ - width:140px; -} - -#content -{ - margin-top:32px; - margin-left:160px; - margin-right:230px; - width:auto; -} - -.box -{ - margin-top:5px; -} - -#rightbar -{ - width:180px; - margin-top:32px; - right:10px; - - /* Set position to fixed to 'pin' the playlist */ - /*position:fixed; - max-height: 85%; - overflow-x: hidden; - overflow-y: scroll;*/ - - position: absolute; - clear:both; -} - -#footer -{ - width: auto; - margin-top: 20px; - margin-bottom: 120px; - margin-right: 230px; -} - -/*********************************************** - Header -***********************************************/ -#header -{ - background:#222; - line-height:30px; - height:50px; - padding-top:7px; - padding-bottom:7px; - width: 100%; -} - -#headerbox -{ - margin-right: 7px; -} - -#header .box-inside -{ - width:auto; - text-align:right; - float:right; -} - -#header .clearfix -{ - clear:none; -} - -#header .box-inside input -{ - border:2px solid #666; - border-radius:2px; - background:#000; - font-weight:700; - color:#eee; - font-size:10px; - width:120px; - padding:4px; -} - -#header input.button -{ - width:80px; - background:#222; -} - -#header #advSearchBtn -{ - color:#aaa; - padding:4px; -} - -#header #loginInfo -{ - display: block; - color:#eee; -} - -#header #loginInfo a -{ - color:#ccc; -} - -/*********************************************** - Sidebar -***********************************************/ -#sidebar-page -{ - position:absolute; - padding-bottom:0.5em; - width:140px; - left:7px; -} - -#sidebar-tabs -{ - width:140px; - background:#e8e8e8; - float:left; - padding:7px; - margin-left: 8px; -} - -#sidebar-tabs li.sb1 -{ - float:left; - margin-right:5px; -} - -#sidebar-tabs .sb2 li -{ - background:#fff; - border-radius:4px; - margin-top:7px; - padding:7px; -} - -#sidebar-tabs .sb2 li h4 -{ - font-weight:700; - display:block; - border-radius:3px; - background:#ddd; - color:#222; - padding:5px; -} - -#sidebar-tabs .sb2 li ul li -{ - background:#fff; - border-radius:0; - margin-top:0; - padding:0; -} - -#sidebar-tabs .sb2 li ul li a -{ - font-weight:700; - display:block; - border-radius:3px; - padding:5px; -} - -#sidebar-tabs .sb2 li ul li a:hover -{ - background:#d2e6f9; -} - -#sidebar-tabs .sb2 #browse_filters li -{ - background:#f9f9f9; -} - -#sidebar-tabs .sb2 #browse_filters #multi_alpha_filterLabel -{ - margin:5px 3px 0; - display: block; -} - -#sidebar-tabs .sb2 #browse_filters #multi_alpha_filter -{ - border:1px solid #bbb; - border-radius:2px; - padding:4px; - width: 115px; -} - -#sidebar-tabs #catalog_select -{ - width:130px; -} - -/* Localplay */ -.active_instance { - border: 1px inset #99ccff; -} - -/*********************************************** - Rightbar -***********************************************/ -#rightbar -{ - background:#fff; - border-radius:4px; - padding:7px; -} - -#rightbar #rb_action -{ - padding:4px; -} - -#rightbar #rb_action li { - margin-right: 5px; -} - -#rightbar li#rb_add,#rightbar li#pl_add -{ - position:relative; - z-index:10; -} - -#rightbar li:hover .submenu -{ - display:block; -} - -#rightbar .submenu -{ - display:none; - position:absolute; - left:-50px; - top:14px; - background:#fff; - border:2px solid silver; - width:120px; - padding:0.6em; -} - -#rightbar #rb_action .submenu li { - margin: 0; -} - -* html #rightbar .submenu -{ - right:100px; -} - -/* IE6 fix */ -#rightbar .submenu a -{ - display:block; - border-bottom:1px dotted #ddd; - color:#5b5b5b; - text-decoration:none; - text-align:left; - padding:0.4em; -} - -#rightbar .submenu a:hover -{ - color:#333; -} - -#rightbar #rb_current_playlist li -{ - position:relative; - padding-right:20px; -} - -#rightbar #rb_current_playlist li a -{ - display:block; - padding:0.2em; - color:#5b5b5b; - border-bottom: 1px solid #f3f3f3; - line-height:16px; -} - -#rightbar #rb_current_playlist li a:hover -{ - color: #111; -} - -#rightbar #rb_current_playlist li.odd -{ - background: #f9f9f9; -} - -#rightbar .delitem -{ - position:absolute; - right:0; - top:0; -} - -/* Rightbar Localplay Controls */ -#rightbar #localplay-control -{ - padding-left:5px; -} - -#rightbar #localplay-control { - border: 1px solid #e3e3e3; - background: #f6f6f6; - padding: 5px; - text-align: center; - margin: 7px 0px; -} - -#rightbar #localplay-control img { - vertical-align: bottom; -} - -/*********************************************** - Content -***********************************************/ -#ajax-loading -{ - position:absolute; - background:#444; - text-align:center; - width:90px; - top:0; - color:#bbb; - margin:0 auto; - padding: 3px 1px 3px 10px; - border: 2px solid #333; - border-top: 0px; - border-left: 0px; - background: #444 url(../images/ajax-loader.gif) no-repeat 2px; - display: none; -} - -.box-content h3.box-title,.browse_content .box h3.box-title -{ - display:block; - background:#888; - color:#eee; - border-bottom:1px solid #335b0d; - border-top-right-radius:3px; - border-top-left-radius:3px; - font-weight:400; - padding:7px; - margin-bottom: 0px; -} - -.browse_content .box.browse_song h3.box-title -{ - background:#66B717; -} - -.browse_content .box.browse_album h3.box-title -{ - background:#09c; -} - -.browse_content .box.browse_artist h3.box-title -{ - background:#c60; -} - -.list-header -{ - border-left:1px solid #bbb; - border-right:1px solid #bbb; - border-bottom: 1px solid #bbb; - padding:7px; -} - -table.tabledata -{ - width:100%; - text-align:left; - color: #eee; - margin-bottom: 20px; -} - -table.tabledata th -{ - /*border:1px solid #bbb; - border-left:1px solid #ccc; - border-right:1px solid #ccc;*/ - background:#eee; - font-weight:700; - font-size:11px; - color:#444; - padding:7px 10px; -} - -table.tabledata tr -{ - height: 25px; -} - -.browse_content table.tabledata th, .browse_content table.tabledata -{ - border-top: 0px; -} - -table.tabledata tbody -{ - background:#fff; -} - -table .th-bottom { - border-top: 1px solid #ccc; -} - -table.tabledata tbody td -{ - vertical-align: middle; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - padding: 3px 10px 3px 0; - color: #686868; -} - -.browse_content table.tabledata tbody tr:hover -{ - background:#b3d6f7; -} - -table.tabledata tbody .odd -{ - background:#eee; -} - -table.tabledata tbody .cel_play { - max-width: 40px; - width: 40px !important; - text-align: right; -} - -table.tabledata tbody .cel_play_content { - display: block; -} - -table.tabledata tbody .cel_play_hover { - display: none; -} - -table.tabledata tbody tr:hover .cel_play_hover, table.tabledata tbody tr:focus .cel_play_hover { - display: block; -} -table.tabledata tbody tr:hover .cel_play_content, table.tabledata tbody tr:focus .cel_play_content { - display: none; -} - -table.tabledata tbody .cel_add { - max-width: 60px; - width: 60px !important; - text-align: right; -} - -table.tabledata tbody .cel_item_add { - display: none; -} - -table.tabledata tbody tr:hover .cel_item_add, table.tabledata tbody tr:focus .cel_item_add { - display: block; -} - -table.tabledata tbody .cel_time { - min-width: 40px; - width: 40px !important; -} - -table.tabledata tbody .cel_action { - width: 130px !important; - max-width: 100%; -} - -table.tabledata tbody td.cel_rating { - width: 100px !important; - max-width: 100px; -} - -table.tabledata tbody td.cel_userflag { - width: 40px !important; - max-width: 40px; -} - -table.tabledata tbody .cel_tags { - width: 150px !important; - max-width: 150px; -} - -table.tabledata tbody .cel_cover img -{ - border: 4px double #bbb; -} - -table.tabledata .cel_drag { - max-width: 16px; - width: 16px !important; -} - -table.tabledata .cel_drag img:hover { - cursor: pointer; -} - -table.tabledata .cel_agent { - text-align: right; -} - -table.tabledata .cel_agent img:hover { - cursor: help; -} - -.box_preferences h4 -{ - font-size:15px; - background:#555; - color:#eee; - font-weight:400; - border:1px solid #333; - padding:5px; -} - -span.page-nb { - font-weight: bold; - font-size: 1.3em; -} - -/*********************************************** - Content (info-box) -***********************************************/ -#content .info-box h3 -{ - margin-top:10px; -} - -#content .info-box .box-content div.star-rating -{ - width:150px; - max-width:150px; -} - - -/* Random album (homepage) */ -#random_selection .random_album -{ - float:left; - width:125px; - margin-bottom: 10px; - text-align: center; -} - -#random_selection .random_album div { - display: inline; -} - -#random_selection .random_album img { - border: 4px double #ccc; -} - -#random_selection .random_album .star-rating -{ - float: left; -} - -#random_selection .random_album .play_album -{ - float: left; - margin-left: 7px; -} - -#random_selection .random_album .play_album a img -{ - border: 0px; -} - -#random_selection .box-bottom -{ - clear:left; -} - -#random_selection .art_album img { - width: 80px; - height: 80px; -} - -/*********************************************** - Content (now playing) -***********************************************/ -#now_playing { - margin-bottom: 20px; - width: auto; -} - -#now_playing .np_group { - float: left; -} - -#now_playing .np_row { - display: table; - margin-bottom: 10px; -} - -#now_playing .np_cell { - line-height: 15px; -} - -#now_playing .cel_rating label { - display: none; -} - -#now_playing .cel_userflag label { - display: none; -} - -#now_playing .np_group label { - font-weight: bold; -} - -#now_playing .cel_username { - width: 140px; -} - -#now_playing .cel_song, #now_playing .cel_album, #now_playing .cel_artist { - width: 200px; -} - -#now_playing .cel_albumart { - float: left; - width: 90px; -} - -#now_playing .cel_albumart img { - border: 4px double #ccc; - float: left; -} - -#now_playing .cel_lyrics { - margin-top: 5px; -} - -#now_playing .cel_lyrics a:hover { - color: #0099CC; -} - -#now_playing .similars { - margin-right: 10px; - padding-right: 5px; - margin-left: 10px; - padding-left: 5px; -} - -/*********************************************** - Content (Tag cloud) -***********************************************/ -.box-content #tag_filter -{ - padding:5px 0px; -} - -.clearfix { - clear: left; -} - -.box-bottom { - clear: both; -} - -span.fatalerror { - color: #c60; - padding: 5px; - display: block; -} - -span.nodata { - padding: 5px; - display: block; - font-style: italic; -} - -.box-content #tag_filter span -{ - padding: 2px 6px; - border: 1px solid #bbb; - margin: 1px; - float: left; - background: #e8e8e8; -} - -.box-content #tag_filter span:hover -{ - background: #ddd; - color: #222; - border: 1px solid #888; -} - -/*********************************************** - Content (inline edit) -***********************************************/ -.inline-edit select { - max-width: 200px; -} - - -/*********************************************** - Content (information-actions) -***********************************************/ -#information_actions -{ - margin-top:10px; - width:300px; -} - -#information_actions h3 -{ - /* only showing up in album view, so strange..bug*/ -} - -#content .info-box .box-content .album_art -{ - float: right; - border: 4px double #ccc; -} - -#information_actions ul li -{ - border-bottom:1px solid #ccc; - padding:3px 0; -} - -#information_actions .star-rating li -{ - padding:0; -} - -#information_actions li img -{ - vertical-align:top; -} - -#information_actions li a -{ - vertical-align:bottom; -} - -#information_actions li a:hover -{ - color:#09c; -} - -.item_right_info { - float: right; - max-width: 60%; -} - -.external_links { - text-align: right; -} - -.external_links a { - margin: 0px 5px 0px 0px; - opacity: 0.3; -} - -.external_links a:hover { - opacity: 1; -} - -#artist_summary { - margin-right: 150px; -} - -/************************************************/ -/* Styles for the star ratings */ -/************************************************/ -.star-rating -{ - position:relative; -} -.dynamic-star-rating -{ - width:95px; -} -.star-rating ul, -.star-rating a:hover, -.star-rating .current-rating -{ - background: url(../images/ratings/star_rating.png) left -1000px repeat-x; -} -.star-rating ul -{ - position:relative; - width:80px; - height:15px; - overflow:hidden; - list-style:none; - margin:0; - padding:0; - background-position: left top; -} -.star-rating li -{ - display: inline; -} - -.star-rating a, .star-rating span, -.star-rating .current-rating -{ - position:absolute; - top:0; - left:0; - text-indent:-1000em; - height:15px; - line-height:15px; - outline:none; - overflow:hidden; - border:none; -} - -.star-rating .star1 { width:20%; z-index:6; } -.star-rating .star2 { width:40%; z-index:5; } -.star-rating .star3 { width:60%; z-index:4; } -.star-rating .star4 { width:80%; z-index:3; } -.star-rating .star5 { width:100%; z-index:2;} -.star-rating .current-rating { z-index:1; background-position: left bottom; } - -.star-rating a.star0 -{ - left:0px; - width:16px; - background: url(../../../images/ratings/x_off.gif) left top; -} - -.dynamic-star-rating a:hover -{ - background-position: left center; -} - -.dynamic-star-rating a:hover.star0 -{ - background: url(../../../images/ratings/x.gif) left top; -} -.dynamic-star-rating ul -{ - left:16px; -} - -/************************************************/ -/* Styles for user flags */ -/************************************************/ -.userflag -{ - position: relative; - width:16px; - height:16px; -} - -.userflag a { - position:absolute; - display: inline; -} - -.userflag a.userflag_true -{ - width:16px; - height: 16px; - background: url(../../../images/icon_flag.png) left top; -} - -.userflag a:hover.userflag_true -{ - background: url(../../../images/icon_flag_off.png) left top; -} - -.userflag a.userflag_false -{ - width:16px; - height:16px; - background: url(../../../images/icon_flag_off.png) left top; -} - -.userflag a:hover.userflag_false -{ - background: url(../../../images/icon_flag.png) left top; -} - -/*********************************************** - Content (Track view) -***********************************************/ -dl.song_details -{ - padding: 0.5em; -} - -dl.song_details dd -{ - margin: 0 0 0 95px; - padding: 0 0 0.5em 0; -} - -dl.song_details dt -{ - float: left; - clear: left; - width: 80px; - text-align: right; - font-weight: bold; - color: #c60; -} - -dl.song_details a:hover -{ - color: #09c; - text-decoration: underline; -} - - -/*********************************************** - Footer -***********************************************/ -#footer -{ - text-align:right; -} - -#footer a:hover { - color: #09c; - text-decoration: underline; -} - -/*********************************************** - Login -***********************************************/ -#loginPage #header -{ - padding-left: 0px; - border: 1px solid #000; -} - -#loginPage #maincontainer { - width: 470px; - margin: 40px auto 0; - background: #fff; - border: 1px solid #ddd; -} - -#loginPage #loginbox { - padding: 20px; -} - -#loginPage #loginbox h2 -{ - display: none; -} - -#loginPage #loginbox label -{ - float: left; - width: 110px; - font-weight: bold; - color: #333; - text-align: right; - vertical-align: middle; - line-height: 30px; - margin-right: 20px; -} - -#usernamefield, #passwordfield, #remembermefield { - clear: both; -} - -#loginPage #loginbox .loginfield input -{ - padding: 5px; - border: 1px solid #ccc; - width: 170px; - color: #444; - font-size: 12px; - outline: 0px; - vertical-align: middle; -} - -#loginPage #loginbox a.button -{ - color: #777; - cursor: pointer; - padding: 4px 10px; -} - -#loginPage #loginbox a:hover -{ - color: #111; -} - -#loginPage .loginfield -{ - width: 400px; -} - -#loginPage span.error -{ - display: block; - clear: left; - padding: 10px; - background: #ff9999; - border: 1px solid #cc3333; - font-weight: bold; - color: #990000; - text-align: center; - margin-bottom: 10px; -} - -#loginPage .formValidation -{ - clear: left; - background: #eee; - padding: 5px 10px; - border: 1px solid #ddd; - text-align: center; -} - -#loginPage #loginbox input.button -{ - padding: 4px 10px; - border: 1px solid #bbb; - font-weight: bold; - color: #333; - background: #e9e9e9; - cursor: pointer; -} - -#loginPage #loginbox .loginfield input#rememberme -{ - margin-top: 7px; - width: 15px; -} - -#loginPage input#lostpasswordbutton -{ - margin-left: 130px; -} - -#loginPage #loginbox input.button:hover -{ - background: #ddd; - border: 1px solid #aaa; -} - -#loginPage #footer -{ - width: 470px; - margin: 10px auto; - color: #888; -} - -#loginPage #footer a -{ - color: #555; -} - -/*********************************************** - Server settings -***********************************************/ -table.tabledata .cel_php_setting, table.tabledata .cel_configuration, .cel_preference -{ - width: 200px; -} - -/************************************************/ -/* Web Player */ -/************************************************/ -#web_player #playlist -{ - overflow-x: hidden; - overflow-y: scroll; - top: 85px; - left: 210px; - right: 5px; - bottom: 5px; -} - -/*********************************************** - Other -***********************************************/ -ol,ul,#rightbar ul -{ - list-style:none; -} - -ins,#rightbar a -{ - text-decoration:none; -} - -.clearfix,#content .info-box .box-content,#additional_information -{ - clear:both; -} - -#header #headerlogo -{ - float:left; - margin-left: 7px; -} - -#header .box-inside div,#header .box-inside form,#rightbar #rb_action li,.star-rating li -{ - display:inline; -} - -.list-header a:hover -{ - text-decoration:underline; -} - -#localplay-control span,.box-content #tag_filter span -{ - cursor:pointer; -} - -.browse-options { - float: right; -} - -.browse-options form { - display: inline; -} - -.browse-options input[type=text] { - width: 50px; -} - -.jscroll-next { - width:50%; - display:block; - border:1px solid #ccc; - -webkit-border-radius:10px; - -moz-border-radius:10px; - border-radius: 10px; - background-color:#eee; - color:#999; - font-weight:bold; - text-align:center; - padding:10px 0; - cursor:pointer; - margin: auto; -} - -.jscroll-next:hover { - color:#666; -} diff --git a/sources/themes/fresh/theme.cfg.php b/sources/themes/fresh/theme.cfg.php deleted file mode 100644 index 09769c8..0000000 --- a/sources/themes/fresh/theme.cfg.php +++ /dev/null @@ -1,52 +0,0 @@ -;;;;;;;;;;;;;;;;;; -;; -;;;;;;;;;;;;;;;;;; -; Copyright 2001 - 2013 Ampache.org -; -; This program is free software; you can redistribute it and/or -; modify it under the terms of the GNU General Public License v2 -; as published by the Free Software Foundation. -; -; 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, write to the Free Software -; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -; -;;;;;;;;;;;;;;;;;;;;;;;;;;; -; Fresh Ampache Theme -;;;;;;;;;;;;;;;;;;;;;;;;;;; - -; Theme Name -; This is the actual name of the theme that -; will be displayed in the preferences screen -; DEFAULT: ampache-theme -name = "Fresh" - -; Theme Author -; This is just a way of giving credit to the -; person who actually created this theme -; DEFAULT: N/A -author = "eigan" - -; Theme Maintainer -; This is just a way of listing who is responsible for -; maintaining this theme in case it's not working right -; please include an e-mail address so you can be contacted -; DEFAULT: N/A -maintainer = "eigan - einargangso@gmail.com" - -; Orientation -; This was added as of 3.3.2-Alpha4, this tells Ampache if this theme -; uses vertical or horizontal orientation of the menu, if this is a horizontal -; theme then it will not show the quick search and quick random play forms -orientation = "vertical" - -; Submenu -; If this is set to simple the sub menu's will only be shown when you're on one of the -; respective pages. If you want to make the menu's something like the classic theme -; comment this out -;submenu = "simple" diff --git a/sources/themes/greysme/images/ajax-loader.gif b/sources/themes/greysme/images/ajax-loader.gif deleted file mode 100644 index 30fdb5d..0000000 Binary files a/sources/themes/greysme/images/ajax-loader.gif and /dev/null differ diff --git a/sources/themes/greysme/images/ampache.png b/sources/themes/greysme/images/ampache.png deleted file mode 100644 index 0486507..0000000 Binary files a/sources/themes/greysme/images/ampache.png and /dev/null differ diff --git a/sources/themes/greysme/images/ampache_back.gif b/sources/themes/greysme/images/ampache_back.gif deleted file mode 100644 index 9de458e..0000000 Binary files a/sources/themes/greysme/images/ampache_back.gif and /dev/null differ diff --git a/sources/themes/greysme/images/ampache_menu.gif b/sources/themes/greysme/images/ampache_menu.gif deleted file mode 100644 index 136d124..0000000 Binary files a/sources/themes/greysme/images/ampache_menu.gif and /dev/null differ diff --git a/sources/themes/greysme/images/back-box.gif b/sources/themes/greysme/images/back-box.gif deleted file mode 100644 index 47879ac..0000000 Binary files a/sources/themes/greysme/images/back-box.gif and /dev/null differ diff --git a/sources/themes/greysme/images/background.jpg b/sources/themes/greysme/images/background.jpg deleted file mode 100644 index 3339fe0..0000000 Binary files a/sources/themes/greysme/images/background.jpg and /dev/null differ diff --git a/sources/themes/greysme/images/blankalbum.gif b/sources/themes/greysme/images/blankalbum.gif deleted file mode 100644 index 8413d5e..0000000 Binary files a/sources/themes/greysme/images/blankalbum.gif and /dev/null differ diff --git a/sources/themes/greysme/images/blankalbum.jpg b/sources/themes/greysme/images/blankalbum.jpg deleted file mode 100644 index 33e89a0..0000000 Binary files a/sources/themes/greysme/images/blankalbum.jpg and /dev/null differ diff --git a/sources/themes/greysme/images/box_bottom.png b/sources/themes/greysme/images/box_bottom.png deleted file mode 100644 index 13e379f..0000000 Binary files a/sources/themes/greysme/images/box_bottom.png and /dev/null differ diff --git a/sources/themes/greysme/images/box_top.png b/sources/themes/greysme/images/box_top.png deleted file mode 100644 index 9d60b94..0000000 Binary files a/sources/themes/greysme/images/box_top.png and /dev/null differ diff --git a/sources/themes/greysme/images/button_back.png b/sources/themes/greysme/images/button_back.png deleted file mode 100644 index deca200..0000000 Binary files a/sources/themes/greysme/images/button_back.png and /dev/null differ diff --git a/sources/themes/greysme/images/button_back2.png b/sources/themes/greysme/images/button_back2.png deleted file mode 100644 index 95e2ef9..0000000 Binary files a/sources/themes/greysme/images/button_back2.png and /dev/null differ diff --git a/sources/themes/greysme/images/curl.gif b/sources/themes/greysme/images/curl.gif deleted file mode 100644 index c2c57eb..0000000 Binary files a/sources/themes/greysme/images/curl.gif and /dev/null differ diff --git a/sources/themes/greysme/images/icons/icon_add.png b/sources/themes/greysme/images/icons/icon_add.png deleted file mode 100644 index 2bb4ee4..0000000 Binary files a/sources/themes/greysme/images/icons/icon_add.png and /dev/null differ diff --git a/sources/themes/greysme/images/icons/icon_admin.png b/sources/themes/greysme/images/icons/icon_admin.png deleted file mode 100644 index e587c9e..0000000 Binary files a/sources/themes/greysme/images/icons/icon_admin.png and /dev/null differ diff --git a/sources/themes/greysme/images/icons/icon_all.png b/sources/themes/greysme/images/icons/icon_all.png deleted file mode 100644 index 63f0301..0000000 Binary files a/sources/themes/greysme/images/icons/icon_all.png and /dev/null differ diff --git a/sources/themes/greysme/images/icons/icon_batch_download.png b/sources/themes/greysme/images/icons/icon_batch_download.png deleted file mode 100644 index f30c96b..0000000 Binary files a/sources/themes/greysme/images/icons/icon_batch_download.png and /dev/null differ diff --git a/sources/themes/greysme/images/icons/icon_browse.png b/sources/themes/greysme/images/icons/icon_browse.png deleted file mode 100644 index 798f6fc..0000000 Binary files a/sources/themes/greysme/images/icons/icon_browse.png and /dev/null differ diff --git a/sources/themes/greysme/images/icons/icon_delete.png b/sources/themes/greysme/images/icons/icon_delete.png deleted file mode 100644 index 934a0ea..0000000 Binary files a/sources/themes/greysme/images/icons/icon_delete.png and /dev/null differ diff --git a/sources/themes/greysme/images/icons/icon_download.png b/sources/themes/greysme/images/icons/icon_download.png deleted file mode 100644 index f30c96b..0000000 Binary files a/sources/themes/greysme/images/icons/icon_download.png and /dev/null differ diff --git a/sources/themes/greysme/images/icons/icon_edit.png b/sources/themes/greysme/images/icons/icon_edit.png deleted file mode 100644 index 0b8271b..0000000 Binary files a/sources/themes/greysme/images/icons/icon_edit.png and /dev/null differ diff --git a/sources/themes/greysme/images/icons/icon_home.png b/sources/themes/greysme/images/icons/icon_home.png deleted file mode 100644 index b2476cd..0000000 Binary files a/sources/themes/greysme/images/icons/icon_home.png and /dev/null differ diff --git a/sources/themes/greysme/images/icons/icon_logout.png b/sources/themes/greysme/images/icons/icon_logout.png deleted file mode 100644 index 95b6e31..0000000 Binary files a/sources/themes/greysme/images/icons/icon_logout.png and /dev/null differ diff --git a/sources/themes/greysme/images/icons/icon_playlist_add.png b/sources/themes/greysme/images/icons/icon_playlist_add.png deleted file mode 100644 index bc0a051..0000000 Binary files a/sources/themes/greysme/images/icons/icon_playlist_add.png and /dev/null differ diff --git a/sources/themes/greysme/images/icons/icon_random.png b/sources/themes/greysme/images/icons/icon_random.png deleted file mode 100644 index 144955a..0000000 Binary files a/sources/themes/greysme/images/icons/icon_random.png and /dev/null differ diff --git a/sources/themes/greysme/images/icons/icon_volumeup.png b/sources/themes/greysme/images/icons/icon_volumeup.png deleted file mode 100644 index a725832..0000000 Binary files a/sources/themes/greysme/images/icons/icon_volumeup.png and /dev/null differ diff --git a/sources/themes/greysme/images/list_back-old.png b/sources/themes/greysme/images/list_back-old.png deleted file mode 100644 index 60b8250..0000000 Binary files a/sources/themes/greysme/images/list_back-old.png and /dev/null differ diff --git a/sources/themes/greysme/images/list_back.png b/sources/themes/greysme/images/list_back.png deleted file mode 100644 index 84b26e1..0000000 Binary files a/sources/themes/greysme/images/list_back.png and /dev/null differ diff --git a/sources/themes/greysme/images/overlay.png b/sources/themes/greysme/images/overlay.png deleted file mode 100644 index 69cd1c4..0000000 Binary files a/sources/themes/greysme/images/overlay.png and /dev/null differ diff --git a/sources/themes/greysme/images/puce.gif b/sources/themes/greysme/images/puce.gif deleted file mode 100644 index 4f19e04..0000000 Binary files a/sources/themes/greysme/images/puce.gif and /dev/null differ diff --git a/sources/themes/greysme/images/punaise-bl.gif b/sources/themes/greysme/images/punaise-bl.gif deleted file mode 100644 index 18bd716..0000000 Binary files a/sources/themes/greysme/images/punaise-bl.gif and /dev/null differ diff --git a/sources/themes/greysme/images/punaise-br.gif b/sources/themes/greysme/images/punaise-br.gif deleted file mode 100644 index e86e1c1..0000000 Binary files a/sources/themes/greysme/images/punaise-br.gif and /dev/null differ diff --git a/sources/themes/greysme/images/punaise-tl.gif b/sources/themes/greysme/images/punaise-tl.gif deleted file mode 100644 index 4cc0d7a..0000000 Binary files a/sources/themes/greysme/images/punaise-tl.gif and /dev/null differ diff --git a/sources/themes/greysme/images/ratings/star_rating.gif b/sources/themes/greysme/images/ratings/star_rating.gif deleted file mode 100644 index f1d3e32..0000000 Binary files a/sources/themes/greysme/images/ratings/star_rating.gif and /dev/null differ diff --git a/sources/themes/greysme/images/ratings/x.gif b/sources/themes/greysme/images/ratings/x.gif deleted file mode 100644 index ab5cc17..0000000 Binary files a/sources/themes/greysme/images/ratings/x.gif and /dev/null differ diff --git a/sources/themes/greysme/images/ratings/x_off.gif b/sources/themes/greysme/images/ratings/x_off.gif deleted file mode 100644 index 9847f01..0000000 Binary files a/sources/themes/greysme/images/ratings/x_off.gif and /dev/null differ diff --git a/sources/themes/greysme/images/sort_off.gif b/sources/themes/greysme/images/sort_off.gif deleted file mode 100644 index 2f50467..0000000 Binary files a/sources/themes/greysme/images/sort_off.gif and /dev/null differ diff --git a/sources/themes/greysme/images/sort_on.gif b/sources/themes/greysme/images/sort_on.gif deleted file mode 100644 index c6848c3..0000000 Binary files a/sources/themes/greysme/images/sort_on.gif and /dev/null differ diff --git a/sources/themes/greysme/preview.png b/sources/themes/greysme/preview.png deleted file mode 100644 index 567bfeb..0000000 Binary files a/sources/themes/greysme/preview.png and /dev/null differ diff --git a/sources/themes/greysme/templates/default.css b/sources/themes/greysme/templates/default.css deleted file mode 100644 index 49645c0..0000000 --- a/sources/themes/greysme/templates/default.css +++ /dev/null @@ -1,879 +0,0 @@ -/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ -/** - * - * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org - * Copyright 2001 - 2008 Mickael Despesse - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License v2 - * as published by the Free Software Foundation. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - */ - -/* - - Ampache Theme "Greysme" - by Mickael Despesse (Spocky) v1.03 (2008/04/18) - - Feel free to modify/reuse, just mention my name _somewhere_ - -*/ -/* Theme colors : */ -/* ---------------*/ -/* Red : #8b3e38 (#5a211c was too dark) */ -/* Light blue : #74718a (#5b596d was too dark) */ -/* Dark blue : #2b293d */ -/* Black : #050505 */ -/* Dark grey : #111 */ -/* Orange: #e9ad51 */ - - - - -/************************************************/ -/* Unify default browsers style rules */ -/************************************************/ -h1, h2, h3, h4, h5, h6, pre, code { font-size: 1em; line-height: 1em; } /* avoid browser default inconsistent font-sizes */ -ol, ul { list-style: none; } -table { border-collapse: separate; border-spacing: 0; } -caption, th, td { text-align: left; font-weight: normal; } -* { margin: 0; padding: 0; border:0; } /* White space reset */ -a img, :link img, :visited img { border: 0; } /* no blue linked image borders */ - -/************************************************/ -/* General style rules */ -/************************************************/ -html{ font-size: 62.5%; } - -body{ - font-size:1.1em; - font-family: Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif; - background: #2b293d url(../images/background.jpg) 0 0 repeat-x fixed; - /*min-width:90em;*/ - color:#e9ad51; -} - -ol { list-style-type: decimal-leading-zero; } -p { color: #e9ad51; } -a { color: #74718a; text-decoration: none; } - -td { padding: 0 8px; color: #e9ad51; } -th { font-weight:bold; padding: 0 .3em;} - -input, select { - font-size:1em; - font-family: Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif; - color: #e9ad51; - background-color: #111; - border: 1px solid #8b3e38; - margin:0 0 0px 0; -} - -input{ padding:0 2px; } -input:focus, select:focus { border-style: dotted; } -textarea { background-color: #111; color: #e9ad51; } - -/***************************************************/ -/* IE6 behaviors */ -/* - Whatever:hover: :hover support on any element */ -/***************************************************/ -body { behavior:url("modules/whatever_hover/csshover3.htc"); } - -/************************************************/ -/* Float Clearer */ -/************************************************/ -/* float clearing for IE6 */ -* html .clearfix{ height: 1%; overflow: visible; } -/* float clearing for IE7 */ -/**+html .clearfix{ min-height: 1%; }*/ -/* float clearing for everyone else */ -.clearfix:after{ clear: both; content: "."; display: block; height: 0; visibility: hidden; } - - -/************************************************/ -/* Main Container */ -/************************************************/ -#maincontainer{ margin-top:18px;} - -/************************************************/ -/* Header */ -/************************************************/ -#header { padding: 0 0; } -#headerbox { font-size: 0.9em; text-align: right; color: #e9ad51; position: absolute; top: 3px; right: 0; padding: 3px; } -#headerbox b { font-weight: normal } -#headerbox a { color: #e9ad51;} -#headerlogo { text-align: center; background: url(../images/ampache_back.gif) 0 0 repeat-x;} -#headerlogo a { } -#headerbox .box-content {background:transparent;border:none;} -#headerbox .box-top{display:none;} -#headerbox .box-bottom{display:none;} - -#play_type_switch { - float:left; - margin-top:2px; -} - -/************************************************/ -/* Content block */ -/************************************************/ -#content { - margin:10px 14em 10px 13.5em; -} - -/************************************************/ -/* Footer */ -/************************************************/ -#footer { - clear:both; - text-align:center; - font-size:.8em; - padding:3px 0; - background:#2c2b39; - border-top:5px solid #21212a; - margin-top:20px; -} - -/************************************************/ -/* Buttons */ -/************************************************/ - -.button, input[type=button], input[type=submit] { - background:#8b3e38 url(../images/button_back2.png) 0 100% repeat-x !important; - background:#8b3e38; - color:#e9ad51; - padding:0px 0.5em; - margin:4px 0 0 0; - border:0; - cursor:pointer; -} -.button:hover, input[type=button]:hover, input[type=submit]:hover { - background:#74718a url(../images/button_back2.png) 0 100% repeat-x !important; - background:#74718a; -} - -a.button { padding:1px .5em; } -input[type=checkbox] { border:0;background:none; } - -/************************************************/ -/* Sidebar */ -/************************************************/ -#sidebar { - float:left: - position:relative; - width: 13em; - text-align: left; - font-size: 0.8em; - padding-top:29px; - background:#2b293d url(../images/ampache_menu.gif) 50% 0 no-repeat; - z-index:20; -} - -/* For supporting browsers *cough*... I mean not IE6... *cough*, fix sidebar position on the left */ -*>div#sidebar{ position: fixed; } - -#sidebar select { width: 95%; } - -/* For sidebar tabs */ -/********************/ -#sidebar-tabs, #rightbar #rb_action{ - border-top:1px solid #000; - background:#111; -} - -#sidebar-tabs li.sb1, #rightbar #rb_action li { - float: left; - padding:1px; - background: #111 ; - border-top:0.5em solid #8b3e38; - border-right:1px solid #000;width:16px; -} -#sidebar-tabs li.active { - border-top-color: #e9ad51; -} -#sidebar-tabs li:hover.sb1, #rightbar #rb_action li:hover -{ - background:#000; - border-top-color: #e9ad51; -} -/* Tabs content */ -/****************/ -#sidebar-page { - position:absolute; - left:0; - top:52px; - background: #111; - padding-bottom:0.5em; - width:13em; - color:#8b3e38; -} -#sidebar-page ul.sb2 { -} -#sidebar-page ul.sb2 li{ - margin:1em auto; - padding-bottom: 0.5em; -} -#sidebar-page ul.sb2 h4{ - padding:.2em .5em .5em 0; - font-style:italic; - font-weight:normal; - font-size:1em; - letter-spacing:.2em; - text-align:right; - border-bottom:1px dotted #e9ad51; - text-decoration: overline; - background: url(../images/puce.gif) -8px -8px no-repeat; - color:#e9ad51; -} -#sidebar-page ul.sb2 li:hover h4{ - background-color:#000; -} - -#sidebar-page ul.sb3, #sidebar-page div.sb3 { - color:#8b3e38; -} -#sidebar-page .sb3 a{ color:#8b3e38; } -#sidebar-page ul.sb3 li{ - margin:0; - padding:0; - border:none; - font-weight:normal; - background: #111 url(../images/button_back.png) 0 100% repeat-x; - border-bottom: 1px solid #000; -} -* html #sidebar-page ul.sb3 li{display:inline;background:#111;} /* fix ie6 */ -#sidebar-page .sb3 a, #sidebar-page .sb3 div{ - padding:.2em .6em; - border-left: .5em solid #8b3e38; -} -#sidebar-page .sb3 a:hover{ - border-left-color: #e9ad51; -} - -#sidebar-page a{ - display:block; -} -#sidebar-page a:hover{ - background:#000; - color:#e9ad51; -} - -/* SIDEBAR : Home */ -/******************/ - -/* SIDEBAR : Browse */ -/********************/ -#multi_alpha_filter { - width:40px; - margin-bottom:4px; -} - -/* SIDEBAR : Localplay */ -/***********************/ -.active_instance { -} - -/* SIDEBAR : Preferences */ -/*************************/ - -/* SIDEBAR : Admin */ -/*******************/ -#sb_admin_catalogs li.sb_admin_catalogs_ctrls img {margin:0;} -#sb_admin_catalogs li.sb_admin_catalogs_ctrls a{ - display:inline; - padding:0; - border:none; -} -/************************************************/ -/* XSPF Player */ -/************************************************/ -#xspf_player { - width:410px; - position: relative; - float: left; - font-family: Verdana,Helvetica,sans-serif; -} - - -/************************************************/ -/* Rightbar */ -/************************************************/ -#rightbar { - width:13em; - background:#000; - float:right; - z-index:21; -} -#rightbar a { text-decoration:none; } - - -/* Rightbar Menu */ -#rightbar #rb_action { } -#rightbar #rb_action li { } -#rightbar li#rb_add, #rightbar li#pl_add { position:relative; z-index:10; } -/* Rightbar AddItems SubMenu */ -#rightbar li:hover .submenu { display:block;} -#rightbar .submenu { - display:none; - position:absolute; - right:0px; - top:15px; - background: #050505 url(../images/button_back.png) 0 100% repeat-x !important; - background: #050505; - border:2px solid #e9ad51; - width:15em; - padding:0.3em; - font-size:0.8em; - -} -* html #rightbar .submenu {right:100px;} /* IE6 fix */ - -#rightbar #rb_action .submenu li {float:none; width:auto; border:none;} -#rightbar .submenu a { - display:block; - padding:0.1em; - color:#8b3e38; - text-decoration:none; - text-align:right; -} -#rightbar .submenu a:hover, -#rightbar #rb_current_playlist a:hover { color:#e9ad51; } - -/* Rightbar playlist */ -#rightbar #rb_current_playlist { - background: #111; - padding-bottom:0.5em; - clear:both; -} -#rightbar #rb_current_playlist li { - position:relative; - font-size:0.9em; - line-height:14px; - color:#5b5b5b; - padding-right:20px; - background: #111 url(../images/button_back.png) 0 100% repeat-x; - border-bottom: 1px solid #000; -} -#rightbar #rb_current_playlist li a { display:block; padding:0.2em;} -#rightbar .delitem { position:absolute;right:0;top:0; } - - -/************************************************/ -/* Styles for the star ratings */ -/************************************************/ -.star-rating { - position:relative; -} -.dynamic-star-rating{ - width:96px; -} - -.star-rating ul, -.star-rating a:hover, -.star-rating .current-rating{ - background: url(../images/ratings/star_rating.gif) left -1000px repeat-x; -} -.star-rating ul{ - position:relative; - width:80px; - height:15px; - overflow:hidden; - list-style:none; - margin:0; - padding:0; - background-position: left top; - /*float:left;*/ -} -.star-rating li{ - display: inline; -} -.star-rating a, .star-rating span, -.star-rating .current-rating{ - position:absolute; - top:0; - left:0; - text-indent:-1000em; - height:15px; - line-height:15px; - outline:none; - overflow:hidden; - border: none; -} -.star-rating .star1 { width:20%; z-index:6; } -.star-rating .star2 { width:40%; z-index:5; } -.star-rating .star3 { width:60%; z-index:4; } -.star-rating .star4 { width:80%; z-index:3; } -.star-rating .star5 { width:100%; z-index:2;} -.star-rating .current-rating { z-index:1; background-position: left bottom; } - -.star-rating-reset {height:16px;} -.star-rating a.star0 { - left:0px; - height:16px; - width:16px; - background: url(../images/ratings/x_off.gif) left top; -} -/* hovering effect only for dynamic star rating */ -#content .dynamic-star-rating a:hover{ - background-position: left center; - background-color:transparent; -} -.dynamic-star-rating a:hover.star0 { - background: url(../images/ratings/x.gif) left top; -} -.dynamic-star-rating ul { - left:16px; -} - -/************************************************/ -/* Styles for user flags */ -/************************************************/ -.userflag -{ - position: relative; - width:16px; - height:16px; -} - -.userflag a { - position:absolute; - display: inline; -} - -.userflag a.userflag_true -{ - width:16px; - height: 16px; - background: url(../../../images/icon_flag.png) left top; -} - -.userflag a:hover.userflag_true -{ - background: url(../../../images/icon_flag_off.png) left top; -} - -.userflag a.userflag_false -{ - width:16px; - height:16px; - background: url(../../../images/icon_flag_off.png) left top; -} - -.userflag a:hover.userflag_false -{ - background: url(../../../images/icon_flag.png) left top; -} - -/************************************************/ -/* Box Related Styles */ -/************************************************/ -.box-title { - display:block; - color:#8b3e38; - padding:3px 13px 0 28px; - background: #000 url(../images/puce.gif) 10px 50% no-repeat; - font-size: 1.1em; - font-variant:small-caps; - border-bottom:1px solid #8b3e38; - letter-spacing:0.1em; -} -.box-title:first-letter{font-style:italic;} - -.box-list { - padding-right: 10px; -} - -/* Enclosing Boxes Styles */ -.box, .info-box { - margin-top: 7px; - margin-right: 11px; - /*background: url(../images/back-box.gif) 0 0 no-repeat;*/ - font-size : 0.9em; - float:left; - clear:left; - height:1%; /* IE6 : Holly Hack comes to rescue once again */ -} -/* Hovering effects on links */ -.box a:hover, .info-box a:hover { /*background-color: #8b3e38;*/ color: #e9ad51;} - -.box-inside { -/* background: url(../images/right.gif) top right repeat-y; */ -} -.box-content { - padding:12px 12px; - background:#000; -} - -.box-top { - position:relative; - background:transparent url(../images/box_top.png) 0 100% repeat-x !important; - background:#000; -} -.box-left-top { - /*background: url(../images/punaise-tl.gif) no-repeat;*/ - height:15px; - width:17px; - position:relative;left:/*-8px*/ 30%;top:-3px; -} -.box-right-top { - /*background: url(../images/curl.gif) no-repeat;*/ - background: url(../images/punaise-tl.gif) no-repeat; - height:15px; - width:17px; - position:absolute;left:30%;top:-3px; -} -* html .box-right-top {right: expression(-this.parentNode.offsetWidth%2+"px");} /* Fixes an IE6 rounding error */ -.box-bottom { - position:relative;clear:both; - background:transparent url(../images/box_bottom.png) 0 0 repeat-x !important; - background:#000; -} -.box-left-bottom { - background: url(../images/punaise-bl.gif) no-repeat; - height:15px; - width:17px; - position:relative;left:-7px;top:-3px; -} -.box-right-bottom { - background: url(../images/punaise-br.gif) no-repeat; - height:15px; - width:17px; - position:absolute;right:-7px;top:-3px; -} -* html .box-right-bottom {right: expression(-this.parentNode.offsetWidth%2+"px");} /* Fixes an IE6 rounding error */ - - -/* Specific to Info Boxes */ -.info-box .album_art {float:left;margin-right:10px;} -.info-box th {color:#8b3e38;} -#information_actions { } -#information_actions h3 { color:#8b3e38; font-size:1.2em; margin:0.2em; } - -.item_right_info { - float: right; - max-width: 60%; -} - -.external_links { - text-align: right; -} - -.external_links a { - margin: 0px 5px 0px 0px; - opacity: 0.3; -} - -.external_links a:hover { - opacity: 1; -} - -#artist_summary { - margin-right: 150px; -} - -/* Specific boxes */ -.box_newest_albums {} -.box_newest_artists {clear:none;} -.box_newest_genres {clear:none;} -.box_popular_album {} -.box_popular_artists {clear:none;} -.box_popular_genres {clear:none;} -.box_preferences h4 {color: #8b3e38;font-size: 1.1em;text-align:center;font-weight: bold;border-bottom:1px solid #8b3e38;padding:1em;} - -/************************************************/ -/* Tables (songs lists...) */ -/************************************************/ -.tabledata .th-top, .tabledata .th-bottom { - background: #111; - vertical-align: top; - font-size:1em; -} -.tabledata th { - color:#8b3e38; - font-variant:small-caps; - font-weight:normal; - border-right:3px solid #000; - text-align:center; - line-height:2em; -} -.tabledata th a { - color:#8b3e38; - padding-right:10px; - background: url(../images/sort_off.gif) 100% 50% no-repeat; - display:block; -} -.tabledata th a:hover { - color:#8b3e38; - background-color:transparent; - background-image:url(../images/sort_on.gif); -} - -.tableform select { - width: 150px; -} - -/* table rows */ -.tabledata .odd, .tabledata .even, .row-highlight { background: url(../images/list_back.png) 0 50% repeat-x !important; background-image: none;} -.tabledata .odd { background-color: #111 !important;} -.tabledata .even { } -.tabledata .odd:hover, -.tabledata .even:hover { background-color: #2b293d !important;} -.row-highlight:hover { background-color: #cc3333 !important;} - -/* Misc */ -.border { background: #000; } -.tabledata input[type=text], .tabledata select{ margin:1px 0; } -.discnb { font-style: italic; font-size:0.8em; } - -/* specific cells */ -td.cel_cover{padding:6px;} -.cel_select, .cel_action, .cel_date, .cel_applytoall, .cel_level {text-align:center;} -td.cel_track {text-align:right;} -/* specific cells : users login state */ -.user_online{background:#0f0;} -.user_offline{background:#7f0000;} -.user_disabled{background:#ccc;} -/* specific cells : enlarge links */ -.tabledata td a{display:block;} -.tabledata td.cel_add a, .tabledata td.cel_action a{display:inline;} -/* specific cells : image links */ -.odd td a img, .even td a img {opacity:0.7;} -.odd td a img:hover, .even td a img:hover {opacity:1;} - -/* specific tables */ -#recently_played .th-bottom {display:none;} -.box_preferences .th-bottom {display:none;} - -/* Inline Editing Tables */ -.inline-edit input, .inline-edit select { - font-size: 0.8em; -} - -/************************************************/ -/* Song details */ -/************************************************/ -dl.song_details{font-size:1em;} -.song_details dt { - float:left; - clear:both; - width:20%; - min-width:20%; /*Ie bugfix*/ - font-weight:bold; -} -.song_details dd { - float:left; - width:79%; - min-width:79%; /*Ie bugfix*/ - margin:0 0 0.2em .3em; - padding-left:.2em; -} -dt, dt + dd {background: url(../images/list_back.png) 0 50% repeat-x !important; background-image: none;} -dt, dt + dd { background-color: #111 !important;} -dt:hover, dt:hover + dd {background-color: #2b293d !important;} - - -/************************************************/ -/* Albums of the moment */ -/************************************************/ -.random_album{ - position:relative; - float:left; - padding:8px; -} - -.random_album .play_album{ - position:absolute; - top:10px; - right:0; -} - -#random_selection .box-content{ - float:left; -} - -#random_selection .art_album img { - width: 80px; - height: 80px; -} - -/************************************************/ -/* Now Playing */ -/************************************************/ -#now_playing{ -} - -.np_row { - padding: 3px; - float:left; - clear:both; -} -.np_cell { - padding-left:5px; - margin-left:5px; -} - -.np_row label { - display:block; - font-weight:bold; - margin-left:-5px; -} - -.np_group { - float:left; - padding-left:10px; -} - -#now_playing .box-content{ -background:#000; - float:left; -} - -/************************************************/ -/* Shoutbox */ -/************************************************/ - - -#shoutbox { - font-size:1em; - position:relative; -} - -#shoutbox div.shout { - /*float:left;*/ - padding:1em 85px 0 30px; -} - -#shoutbox span.information { -/* float:left; - clear:left;*/ -} -#shoutbox .shouttext{display:block;} -img.shoutboximage { - margin-right:3px; - width:25px; - height:25px; - position:absolute;margin-left:-30px; -} -#shoutbox .odd img.shoutboximage {/*float:right;*/} -#shoutbox .even img.shoutboximage {/*float:right;*/} - -div.shout:hover img.shoutboximage{width:75px;height:75px;position:absolute;top:50%;right:0;margin-top:-38px} - -/************************************************/ -/* List Header */ -/************************************************/ -.list-header{margin:7px 0; padding:0 4em; text-align:center; font-size: 0.9em;position:relative;} -.list-header .prev{position:absolute; top:0; left:0;} -.list-header .next{position:absolute; top:0; right:0;} -.list-header .selected{background:#e9ad51;color:#111;} -.list-header .page-nb{padding:1px;border: 1px solid #111;} -.list-header a:hover{background: transparent; border-color:#e9ad51;} - -/************************************************/ -/* Errors */ -/************************************************/ -.error { - color: #990033; -} - -.fatalerror .nodata { - padding: 3px; - display: table-cell; - color: #990033; - font-weight:bold; - border:2px solid #990033; -} - -/************************************************/ -/* LocalPlay */ -/************************************************/ -.lp_box_ctrl, .lp_box_vol { - text-align: center; /*for compatibility, may be controlled by themers now*/ -} - -td.lp_current a { - font-weight:bold; - text-decoration:none; -} - -/************************************************/ -/* Styles for Login template */ -/************************************************/ -#loginPage #maincontainer { - margin: 5% auto 0px auto; - text-align:center; -} -#loginPage h2{ - color:#111; - font-size:0.8em; - font-style:italic; - font-weight:normal; - margin: 0 0 2em 0; -} -#loginPage #loginbox{ -} -.loginfield{ - text-align:right; - margin: 1px 0; - width:15em; - margin:auto; -} -.loginfield input.text_input{ - width:8em; - border:1px solid #74718a; -} - -#loginPage div.fatalerror { - padding:5px; - margin:10px; -} - -#motd { - margin:0 auto 0 auto; - width: 437px; -} - - -/************************************************/ -/* Misc */ -/************************************************/ - -.formValidation{ - margin-top:1em; - text-align:center; -} - -.text-box, .confirmation-box { - display: table-cell; - padding:5px; - margin:0 0 10px 0; - background-color: #111; -} - -#ajax-loading { - position: absolute; - top:106px; - left:42%; - width:265px; - height:50px; - z-index: 100; - background: url(../images/ajax-loader.gif) no-repeat; - text-indent:-9999em; - display: none; -} - -.information,.information a { - font-size: 0.9em; - font-style: italic; - color: #c0c0c0; -} - diff --git a/sources/themes/greysme/theme.cfg.php b/sources/themes/greysme/theme.cfg.php deleted file mode 100644 index dc92eb0..0000000 --- a/sources/themes/greysme/theme.cfg.php +++ /dev/null @@ -1,53 +0,0 @@ -;;;;;;;;;;;;;;;;;; -;; -;;;;;;;;;;;;;;;;;; -; Copyright 2001 - 2013 Ampache.org -; All rights reserved. -; -; This program is free software; you can redistribute it and/or -; modify it under the terms of the GNU General Public License v2 -; as published by the Free Software Foundation. -; -; 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, write to the Free Software -; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -; -;;;;;;;;;;;;;;;;;;;;;;;;;;; -; Alternate Ampache Theme -;;;;;;;;;;;;;;;;;;;;;;;;;;; - -; Theme Name -; This is the actual name of the theme that -; will be displayed in the preferences screen -; DEFAULT: ampache-theme -name = "Greysme" - -; Theme Author -; This is just a way of giving credit to the -; person who actually created this theme -; DEFAULT: N/A -author = "Mickael Despesse (Spocky)" - -; Theme Maintainer -; This is just a way of listing who is responsible for -; maintaining this theme in case it's not working right -; please include an e-mail address so you can be contacted -; DEFAULT: N/A -maintainer = "Spocky" - -; Orientation -; This was added as of 3.3.2-Alpha4, this tells Ampache if this theme -; uses vertical or horizontal orientation of the menu, if this is a horizontal -; theme then it will not show the quick search and quick random play forms -orientation = "vertical" - -; Submenu -; If this is set to simple the sub menu's will only be shown when you're on one of the -; respective pages. If you want to make the menu's something like the classic theme -; comment this out -;submenu = "simple" diff --git a/sources/themes/penguin/images/ajax-loader.gif b/sources/themes/penguin/images/ajax-loader.gif deleted file mode 100644 index bc92f77..0000000 Binary files a/sources/themes/penguin/images/ajax-loader.gif and /dev/null differ diff --git a/sources/themes/penguin/images/ampache.png b/sources/themes/penguin/images/ampache.png deleted file mode 100644 index b053dd8..0000000 Binary files a/sources/themes/penguin/images/ampache.png and /dev/null differ diff --git a/sources/themes/penguin/images/background.gif b/sources/themes/penguin/images/background.gif deleted file mode 100644 index 857578c..0000000 Binary files a/sources/themes/penguin/images/background.gif and /dev/null differ diff --git a/sources/themes/penguin/images/bg_login.jpg b/sources/themes/penguin/images/bg_login.jpg deleted file mode 100644 index b2eef20..0000000 Binary files a/sources/themes/penguin/images/bg_login.jpg and /dev/null differ diff --git a/sources/themes/penguin/images/blank-pixel.gif b/sources/themes/penguin/images/blank-pixel.gif deleted file mode 100644 index 17d4390..0000000 Binary files a/sources/themes/penguin/images/blank-pixel.gif and /dev/null differ diff --git a/sources/themes/penguin/images/blankalbum.gif b/sources/themes/penguin/images/blankalbum.gif deleted file mode 100644 index 58f4562..0000000 Binary files a/sources/themes/penguin/images/blankalbum.gif and /dev/null differ diff --git a/sources/themes/penguin/images/blankalbum.jpg b/sources/themes/penguin/images/blankalbum.jpg deleted file mode 100644 index 9a05cad..0000000 Binary files a/sources/themes/penguin/images/blankalbum.jpg and /dev/null differ diff --git a/sources/themes/penguin/images/bottom.gif b/sources/themes/penguin/images/bottom.gif deleted file mode 100644 index 75e83a9..0000000 Binary files a/sources/themes/penguin/images/bottom.gif and /dev/null differ diff --git a/sources/themes/penguin/images/bottomright.gif b/sources/themes/penguin/images/bottomright.gif deleted file mode 100644 index 7ef339d..0000000 Binary files a/sources/themes/penguin/images/bottomright.gif and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_add.png b/sources/themes/penguin/images/icons/icon_add.png deleted file mode 100644 index 2a6bf09..0000000 Binary files a/sources/themes/penguin/images/icons/icon_add.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_add_key.png b/sources/themes/penguin/images/icons/icon_add_key.png deleted file mode 100644 index f51387a..0000000 Binary files a/sources/themes/penguin/images/icons/icon_add_key.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_add_user.png b/sources/themes/penguin/images/icons/icon_add_user.png deleted file mode 100644 index b1af086..0000000 Binary files a/sources/themes/penguin/images/icons/icon_add_user.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_admin.png b/sources/themes/penguin/images/icons/icon_admin.png deleted file mode 100644 index 277161d..0000000 Binary files a/sources/themes/penguin/images/icons/icon_admin.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_all.png b/sources/themes/penguin/images/icons/icon_all.png deleted file mode 100644 index 8a24abd..0000000 Binary files a/sources/themes/penguin/images/icons/icon_all.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_batch_download.png b/sources/themes/penguin/images/icons/icon_batch_download.png deleted file mode 100644 index 2aefc9a..0000000 Binary files a/sources/themes/penguin/images/icons/icon_batch_download.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_browse.png b/sources/themes/penguin/images/icons/icon_browse.png deleted file mode 100644 index 5c0dbaa..0000000 Binary files a/sources/themes/penguin/images/icons/icon_browse.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_cog.png b/sources/themes/penguin/images/icons/icon_cog.png deleted file mode 100644 index 80a78be..0000000 Binary files a/sources/themes/penguin/images/icons/icon_cog.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_delete.png b/sources/themes/penguin/images/icons/icon_delete.png deleted file mode 100644 index c4e0ec1..0000000 Binary files a/sources/themes/penguin/images/icons/icon_delete.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_disable.png b/sources/themes/penguin/images/icons/icon_disable.png deleted file mode 100644 index 0ae07f3..0000000 Binary files a/sources/themes/penguin/images/icons/icon_disable.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_download.png b/sources/themes/penguin/images/icons/icon_download.png deleted file mode 100644 index ad67c76..0000000 Binary files a/sources/themes/penguin/images/icons/icon_download.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_edit.png b/sources/themes/penguin/images/icons/icon_edit.png deleted file mode 100644 index 04dd5d6..0000000 Binary files a/sources/themes/penguin/images/icons/icon_edit.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_enable.png b/sources/themes/penguin/images/icons/icon_enable.png deleted file mode 100644 index 41b5ee3..0000000 Binary files a/sources/themes/penguin/images/icons/icon_enable.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_feed.png b/sources/themes/penguin/images/icons/icon_feed.png deleted file mode 100644 index 4cb31ce..0000000 Binary files a/sources/themes/penguin/images/icons/icon_feed.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_home.png b/sources/themes/penguin/images/icons/icon_home.png deleted file mode 100644 index 8938c20..0000000 Binary files a/sources/themes/penguin/images/icons/icon_home.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_link.png b/sources/themes/penguin/images/icons/icon_link.png deleted file mode 100644 index cbea21a..0000000 Binary files a/sources/themes/penguin/images/icons/icon_link.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_logout.png b/sources/themes/penguin/images/icons/icon_logout.png deleted file mode 100644 index 6ab0ef6..0000000 Binary files a/sources/themes/penguin/images/icons/icon_logout.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_next.png b/sources/themes/penguin/images/icons/icon_next.png deleted file mode 100644 index 7e03946..0000000 Binary files a/sources/themes/penguin/images/icons/icon_next.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_pause.png b/sources/themes/penguin/images/icons/icon_pause.png deleted file mode 100644 index 2914803..0000000 Binary files a/sources/themes/penguin/images/icons/icon_pause.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_play.png b/sources/themes/penguin/images/icons/icon_play.png deleted file mode 100644 index 8e09f54..0000000 Binary files a/sources/themes/penguin/images/icons/icon_play.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_playlist_add.png b/sources/themes/penguin/images/icons/icon_playlist_add.png deleted file mode 100644 index dba78cf..0000000 Binary files a/sources/themes/penguin/images/icons/icon_playlist_add.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_plugin.png b/sources/themes/penguin/images/icons/icon_plugin.png deleted file mode 100644 index 5789e7e..0000000 Binary files a/sources/themes/penguin/images/icons/icon_plugin.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_preferences.png b/sources/themes/penguin/images/icons/icon_preferences.png deleted file mode 100644 index f2897bf..0000000 Binary files a/sources/themes/penguin/images/icons/icon_preferences.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_prev.png b/sources/themes/penguin/images/icons/icon_prev.png deleted file mode 100644 index 3ca8d5d..0000000 Binary files a/sources/themes/penguin/images/icons/icon_prev.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_random.png b/sources/themes/penguin/images/icons/icon_random.png deleted file mode 100644 index 107e7ab..0000000 Binary files a/sources/themes/penguin/images/icons/icon_random.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_server_lightning.png b/sources/themes/penguin/images/icons/icon_server_lightning.png deleted file mode 100644 index 8734256..0000000 Binary files a/sources/themes/penguin/images/icons/icon_server_lightning.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_stop.png b/sources/themes/penguin/images/icons/icon_stop.png deleted file mode 100644 index 2b4f394..0000000 Binary files a/sources/themes/penguin/images/icons/icon_stop.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_view.png b/sources/themes/penguin/images/icons/icon_view.png deleted file mode 100644 index c51f7a9..0000000 Binary files a/sources/themes/penguin/images/icons/icon_view.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_volumedn.png b/sources/themes/penguin/images/icons/icon_volumedn.png deleted file mode 100644 index 159b3be..0000000 Binary files a/sources/themes/penguin/images/icons/icon_volumedn.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_volumemute.png b/sources/themes/penguin/images/icons/icon_volumemute.png deleted file mode 100644 index 6c009ef..0000000 Binary files a/sources/themes/penguin/images/icons/icon_volumemute.png and /dev/null differ diff --git a/sources/themes/penguin/images/icons/icon_volumeup.png b/sources/themes/penguin/images/icons/icon_volumeup.png deleted file mode 100644 index c85b003..0000000 Binary files a/sources/themes/penguin/images/icons/icon_volumeup.png and /dev/null differ diff --git a/sources/themes/penguin/images/ratings/star_rating.gif b/sources/themes/penguin/images/ratings/star_rating.gif deleted file mode 100644 index 06d5e4b..0000000 Binary files a/sources/themes/penguin/images/ratings/star_rating.gif and /dev/null differ diff --git a/sources/themes/penguin/images/ratings/x.gif b/sources/themes/penguin/images/ratings/x.gif deleted file mode 100644 index 5417ddc..0000000 Binary files a/sources/themes/penguin/images/ratings/x.gif and /dev/null differ diff --git a/sources/themes/penguin/images/ratings/x_off.gif b/sources/themes/penguin/images/ratings/x_off.gif deleted file mode 100644 index 7e756ea..0000000 Binary files a/sources/themes/penguin/images/ratings/x_off.gif and /dev/null differ diff --git a/sources/themes/penguin/images/rightbar_top.jpg b/sources/themes/penguin/images/rightbar_top.jpg deleted file mode 100644 index 0b119b8..0000000 Binary files a/sources/themes/penguin/images/rightbar_top.jpg and /dev/null differ diff --git a/sources/themes/penguin/preview.png b/sources/themes/penguin/preview.png deleted file mode 100644 index e19981a..0000000 Binary files a/sources/themes/penguin/preview.png and /dev/null differ diff --git a/sources/themes/penguin/templates/default.css b/sources/themes/penguin/templates/default.css deleted file mode 100644 index c1c53b2..0000000 --- a/sources/themes/penguin/templates/default.css +++ /dev/null @@ -1,1107 +0,0 @@ -/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ -/** - * - * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License v2 - * as published by the Free Software Foundation. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - */ - -/************************************************/ -/* Unify default browsers style rules */ -/************************************************/ -h1, h2, h3, h4, h5, h6, pre, code { - font-family:Verdana, Geneva, sans-serif; - font-size: 10px; -} /* avoid browser default inconsistent font-sizes */ -ol, ul { - list-style: none; -} -table { - border-collapse: separate; - border-spacing: 0; -} -caption, th, td { - text-align: left; - font-weight: normal; -} -* { - margin: 0; - padding: 0; -} /* White space reset */ -a img, :link img, :visited img { - border: 0; -} /* no blue linked image borders */ -/************************************************/ -/* General style rules */ -/************************************************/ -body { - background:#222 url(../images/background.gif) repeat; - font-family:Arial, Helvetica, Sans-Serif; - min-width:900px; -} -p { - color: #fc0; - margin:1em 0; -} -a, a:visited, a:active { - color: #fff; - font-family: Verdana, Helvetica, sans-serif; - text-decoration:none; -} -td { - padding-left: 5px; - padding-right: 10px; - color: #fff; - font-family:Verdana, Geneva, sans-serif; - font-size:10px; - text-transform:small-caps; -} -th { - padding-right: 10px; - font-family: Verdana, Helvetica, sans-serif; - font-size:10px; - font-weight:bold; - text-transform:uppercase; - background: #333; - padding:2px; -} -input, textarea { - font-family:Verdana, Geneva, sans-serif; - font-size:10px; - background: #222; - color: #fff; - border: 1px solid #666; - margin: 2px; -} -input { - font-weight: bold; - padding: 1px; -} -select { - font-family:Verdana, Geneva, sans-serif; - font-size:10px; - background:#222; - color: #fff; - margin:3px; - border: 1px solid #666; -} -input[type=checkbox] { - border:0 -} -/***************************************************/ -/* IE6 behaviors */ -/* - Whatever:hover: :hover support on any element */ -/***************************************************/ -body { - behavior:url("modules/whatever_hover/csshover3.htc"); -} -/************************************************/ -/* Float Clearer */ -/************************************************/ -/* float clearing for IE6 */ -* html .clearfix { - height: 1%; - overflow: visible; -} -/* float clearing for IE7 */ -/**+html .clearfix{ min-height: 1%; }*/ -/* float clearing for everyone else */ -.clearfix:after { - clear: both; - content: "."; - display: block; - height: 0; - visibility: hidden; -} -/************************************************/ -/* XSPF Player */ -/************************************************/ -.xspf_player { - right: 20px; - position: absolute; -} -/************************************************/ -/* Main Container */ -/************************************************/ -#maincontainer { - font-family:Verdana, Geneva, sans-serif; - font-size:10px; - text-transform:uppercase; -} -/************************************************/ -/* Header */ -/************************************************/ -#header { - height: 40px; - padding: 0px 0 0 10px; -} -#headerbox { - position:absolute; - top:0px; - left:264px; - font-size: 10px; - padding-top: 5px; - padding-bottom: 5px; - /*background: #fff;*/ - height: 30px; - width: 504px; -} -#headerlogo, #headerlogo a { - position: absolute; - left: 0px; - top: 0px; -} -.box_headerbox { - padding-left: 100px; - display:table; - color: #333; -} -.box_headerbox #loginInfo { - position:absolute; - top:-200px; - left:-200px; - font-size:0px; -} -#play_type_switch { - position:absolute; - top:10px; - left:-10px; - text-transform:lowercase; -} -/************************************************/ -/* Content block */ -/************************************************/ -#content { - margin: 8px 50px 20px 300px; -} -/************************************************/ -/* Footer */ -/************************************************/ -#footer { - color:FC0; - font-size:9px; - font-weight:bold; - position:absolute; - top:5px; - left: 815px; - width:350px; - text-align:left; - z-index:250; - text-transform:uppercase; - line-height: 10px; -} -#footer a:link { - color:#222; -} -/************************************************/ -/* Buttons */ -/************************************************/ -.button, input[type=button], input[type=submit] { - border: 1px solid #fff; - border-style: none; - background: #fff; - color: #333; - font-weight:bold; - text-decoration:none; - cursor: pointer; - font-family:Verdana, Geneva, sans-serif; - font-size:9px; - text-transform: uppercase; - vertical-align:middle; -} -a.button { - border: 1px solid #fff; - padding-left:5px; - padding-right:5px; - padding-top:2px; - padding-bottom:2px; - vertical-align:middle; - color: #222; -} -/************************************************/ -/* Sidebar */ -/************************************************/ -#sidebar { - position:absolute; - top:0px; - left:0px; - width:200px; - height: 40px; - padding-top:0px; - background:#fff; -} -#sidebar select { - width: 95%; -} -#sidebar input { - vertical-align:middle; - background:#fff; - color:#000; -} -#sidebar ul { - list-style:none; -} -#sidebar a { - text-decoration:none; -} -/* For sidebar tabs */ -/********************/ -#sidebar-tabs { - padding-left: 5px; - padding-top: 12px; - padding-bottom: 0px; -} -#sidebar-tabs li.sb1 { - float: left; - padding-left:0px; - padding-right:11px; - background: #fff; -} -#sidebar-tabs li.active { - background: #fff; - margin-top:0px; -} -#sidebar-tabs li.active img { - margin-top:0px; - position:relative; - z-index:2; -} -/* Tabs content */ -/****************/ -#sidebar-page { - position:absolute; - left:0; - top:41px; - background: #fff url(../images/bottom.gif) 0 100% repeat-x; - padding-bottom:10px; - font-family:Verdana, Geneva, sans-serif; - font-size:10px; - text-transform:uppercase; - width:130px; -} -#sidebar-page ul.sb2 { - padding-top:2px; - padding-left:5px; - padding-right:10px; -} -#sidebar-page ul.sb2 li { - font-weight:bold; - margin:10px auto; - padding-bottom: 10px; -} -#sidebar-page ul.sb2 h4 { - padding-bottom: 5px; -} -#sidebar-page ul.sb3, #sidebar-page div.sb3 { - font-size:10px; - margin-left:0px; - font-weight:normal; - text-transform:capitalize; - color:#666; -} -#sidebar-page div.sb3 input[type=radio] { - margin-left:0px; - border:none; -} -#sidebar-page ul.sb3 li { - margin:0; - padding:0; - border:none; - font-weight:normal; -} -* html #sidebar-page ul.sb3 li { - display:inline; -} /* fix ie6 */ -#sidebar-page .sb3 a { - padding:1px; - border-bottom:1px dotted #ccc; - color: #666; -} -#sidebar-page a { - display:block; -} -#sidebar-page a:hover { - background:#FC0; - color:#000; -} -/* SIDEBAR : Home */ -/******************/ - -/* SIDEBAR : Browse */ -/********************/ -.alphabet { - background:transparent; /* fix ie bug */ - font-size:0.95em; - font-weight:normal; - margin: 0.3em auto; - color:#5b5b5b; -} -.alphabet span.link { - cursor: pointer; - margin: 0; - padding:0 5px; - font-family: monospace, Courier, Georgia; -} -.alphabet span.active { - background:#5b5b5b; - color:#fff; -} -.alphabet span.link:hover { - background: #99ccff; - color:#fff; -} -#multi_alpha_filter { - width:40px; - margin-bottom:4px; -} -/* SIDEBAR : Localplay */ -/***********************/ -.active_instance { - background:#0C0; -} -/* SIDEBAR : Preferences */ -/*************************/ - -/* SIDEBAR : Admin */ -/*******************/ -#sb_admin_catalogs li.sb_admin_catalogs_ctrls img { - margin:0; -} -#sb_admin_catalogs li.sb_admin_catalogs_ctrls a { - display:inline; - padding:0; - border:none; -} -/************************************************/ -/* XSPF Player */ -/************************************************/ -#xspf_player { - width:400px; - float: left; - background:#fff; - font-family: Verdana, Helvetica, sans-serif; -} -/************************************************/ -/* Rightbar */ -/************************************************/ -#rightbar { - position:absolute; - left: 131px; - top: 0px; - width:150px; - padding-top:12px; - background:url(../images/rightbar_top.jpg) 0px 41px no-repeat; - font-family: Verdana, Helvetica, sans-serif; -} -#rightbar ul { - list-style:none; -} -#rightbar a { - text-decoration:none; -} -/* Rightbar Menu */ -#rightbar #rb_action { - padding-bottom:11px; - padding-left:3px; -} -#rightbar #rb_action li { - display:inline; - margin-right:11px; -} -#rightbar li#rb_add, #rightbar li#pl_add { - position:relative; - z-index:10; -} -#rightbar li#rb_add:hover, #rightbar li#pl_add:hover { -} -/* Rightbar AddItems SubMenu */ -#rightbar li:hover .submenu { - display:block; - color:#000; -} -#rightbar .submenu { - display:none; - position:absolute; - left:-15px; - top:12px; - background:#FC0; - border:5px solid #222; - width:120px; - font-size:10px; - text-transform: capitalize; - padding: 5px 5px 5px 5px; -} -* html #rightbar .submenu { - right:100px; -} /* IE6 fix */ -#rightbar .submenu a { - display: block; - padding: 1px; - border-bottom:1px dotted #222; - color:#222; - text-decoration:none; - text-align:left; -} -#rightbar .submenu a:hover, #rightbar #rb_current_playlist a:hover { - background:#222; - color:#fff; -} -/* Rightbar playlist */ -#rightbar #rb_current_playlist { - background: #666 url(../images/bottomright.gif) 0 100% repeat-x; - margin-top: 36px; - padding-bottom:10px; - padding-left: 6px; - padding-right: 0px; - text-transform: capitalize; -} -#rightbar #rb_current_playlist li { - position:relative; - font-size:10px; - line-height:10px; - color:#fff; - padding-right:20px; -} -#rightbar #rb_current_playlist li a { - display:block; - padding:1px; - color:#FC0; -} -#rightbar #rb_current_playlist li a:hover { - background: #FC0; - color:#000; -} -#rightbar .delitem { - position:absolute; - right:5px; - top:0px; -} -/* Rightbar Localplay Controls */ -#rightbar #localplay-control { - position:fixed; - display: block; - width: 15px; - height: 50%; - bottom:0px; - right:0px; - list-style:none; - background:#inherit; - font-size:0px; - z-index:200; -} -#localplay-control span { - cursor: pointer; -} -/************************************************/ -/* Styles for the star ratings */ -/************************************************/ -.star-rating { - position:relative; -} -.dynamic-star-rating { - width:90px; -} -.star-rating ul, .star-rating a:hover, .star-rating .current-rating { - background: url(../images/ratings/star_rating.gif) left -1000px repeat-x; -} -.star-rating ul { - position:relative; - width:80px; - height:15px; - overflow:hidden; - list-style:none; - margin:0; - padding:0; - background-position: left top; -} -.star-rating li { - display: inline; -} -.star-rating a, .star-rating span, .star-rating .current-rating { - position:absolute; - top:0; - left:0; - text-indent:-1000em; - height:15px; - line-height:15px; - outline:none; - overflow:hidden; - border:none; -} -.star-rating .star1 { - width:20%; - z-index:6; -} -.star-rating .star2 { - width:40%; - z-index:5; -} -.star-rating .star3 { - width:60%; - z-index:4; -} -.star-rating .star4 { - width:80%; - z-index:3; -} -.star-rating .star5 { - width:100%; - z-index:2; -} -.star-rating .current-rating { - z-index:1; - background-position: left bottom; -} -.star-rating a.star0 { - left:0px; - width:16px; - background: url(../images/ratings/x_off.gif) left top; -} -/* hovering effect only for dynamic star rating */ -.dynamic-star-rating a:hover { - background-position: left center; -} -.dynamic-star-rating a:hover.star0 { - background: url(../images/ratings/x.gif) left top; -} -.dynamic-star-rating ul { - left:16px; -} - -/************************************************/ -/* Styles for user flags */ -/************************************************/ -.userflag -{ - position: relative; - width:16px; - height:16px; -} - -.userflag a { - position:absolute; - display: inline; -} - -.userflag a.userflag_true -{ - width:16px; - height: 16px; - background: url(../../../images/icon_flag.png) left top; -} - -.userflag a:hover.userflag_true -{ - background: url(../../../images/icon_flag_off.png) left top; -} - -.userflag a.userflag_false -{ - width:16px; - height:16px; - background: url(../../../images/icon_flag_off.png) left top; -} - -.userflag a:hover.userflag_false -{ - background: url(../../../images/icon_flag.png) left top; -} - -/************************************************/ -/* Box Related Styles */ -/************************************************/ - -.box-title { - font-family:Verdana, Geneva, sans-serif; - font-size:10px; - font-weight: bold; - text-transform:uppercase; - padding-bottom: 10px; - color:#FFF; - width:500px; -} -.box-list { -} -/* Enclosing Boxes Styles */ - -.box, .info-box { - background:inherit; - color:#FFF; - float:left; - clear:left; - height:1%; /* IE6 : Holly Hack comes to rescue once again */ -} -.box-inside { -} -.box-content { -} -.box-top { - position:relative; -} -.box-left-top { - height:5px; - width:5px; - position:relative; - left:0; - top:0; -} -.box-right-top { - height:5px; - width:5px; - position:absolute; - right:0; - top:0; -} -* html .box-right-top { -right: expression(-this.parentNode.offsetWidth%2+"px"); -} /* Fixes an IE6 rounding error */ -.box-bottom { - position:relative; - clear:both; -} -.box-left-bottom { - height:5px; - width:5px; - position:relative; - left:0; - top:0; -} -.box-right-bottom { - height:5px; - width:5px; - position:absolute; - right:0; - top:0; -} -* html .box-right-bottom { -right: expression(-this.parentNode.offsetWidth%2+"px"); -} /* Fixes an IE6 rounding error */ -/* Specific to Info Boxes */ -.info-box { - float:left; - margin-right:10px; -} -.album_art { - float:left; - margin-right:10px; -} -#information_actions { - padding: 0px 0px 10px 0px; - font-family:Verdana, Geneva, sans-serif; - font-size:10px; - line-height: 15px; -} -#information_actions h3 { - float:none; - font-family:Verdana, Geneva, sans-serif; - font-size:10px; - height: 0px; - margin: 0px 0px 20px 1px; -} - -.item_right_info { - float: right; - max-width: 60%; -} - -.external_links { - text-align: right; -} - -.external_links a { - margin: 0px 5px 0px 0px; - opacity: 0.3; -} - -.external_links a:hover { - opacity: 1; -} - -#artist_summary { - margin-right: 150px; -} - -/* Specific boxes */ -.box_newest_albums { -} -.box_newest_artists { - clear:none; -} -.box_newest_genres { - clear:none; -} -.box_popular_album { -} -.box_popular_artists { - clear:none; -} -.box_popular_genres { - clear:none; -} -.box_preferences h4 { - color:#fff; - font-family: Verdana, Geneva, sans-serif; - font-size:10px; - font-weight:bold; - text-transform:uppercase; - padding:5px 0px 10px 0px; - color: #fc0; -} -/************************************************/ -/* Tables (songs lists...) */ -/************************************************/ -.tabledata .th-top, .tabledata .th-bottom { - vertical-align: center; - text-align:center; -} -.tableform select { - width: 100px; -} -/* table rows */ -.tabledata .odd { -} -.tabledata .odd td { -} -.tabledata .even { -} -.tabledata .even td { -} -.row-highlight { -} -.tabledata .even:hover, .tabledata .odd:hover { - background:#666; -} -.row-highlight:hover { - background:#333; -} -/* Misc */ -.border { - background: #000; -} -.tabledata input, .tabledata select { - font-weight:normal; - background:#222; - color: #FC0; - margin:2px 0px 0px 0px; - border:1px solid #666; -} -/* specific cells */ -td.cel_cover { -} -.cel_select, .cel_date, .cel_applytoall, .cel_level { - text-align:right; -} -.cel_action { - text-align:right; - padding-right:0px; -} -/* specific cells : users login state */ -.user_online { - background:#222; -} -.user_offline { - background:#C00; -} -.user_disabled { - background:#ccc; -} -/* specific tables */ -#recently_played .th-bottom { - display:none; -} -.box_preferences .th-bottom { - display:none; -} -/* Inline Editing Tables */ -.inline-edit input, .inline-edit select { - font-size: 10px; -} -/************************************************/ -/* Song details */ -/************************************************/ -dl.song_details { - font-family:Verdana, Geneva, sans-serif; - font-size:10px; - width: 300px; -} -.song_details dt { - text-transform:uppercase; - float:left; - clear:both; - width:20%; - min-width:20%; /*Ie bugfix*/ - font-weight:bold; - padding: 2px 0px 0px 0px; - border-right: 1px dotted #666666; - border-bottom: 1px dotted #666666; -} -.song_details dd { - float:left; - width:79%; - min-width:79%; /*Ie bugfix*/ - padding: 2px 0px 0px 2px; -} -dt + dd { - border-bottom:1px dotted #666; -} -dt:hover, dt:hover + dd { - background:#666; - color: #fff; -} -/************************************************/ -/* Albums of the moment */ -/************************************************/ -.random_album { - position:relative; - float:left; - padding-right:10px; - width:80px; -} -.random_album .play_album { - display:none; -} - -#random_selection .art_album img { - width: 80px; - height: 80px; -} - -/************************************************/ -/* Now Playing */ -/************************************************/ -#now_playing { -} -.np_row { - padding: 3px; - float:left; - font-size:10px; - display:block; -} -.np_cell { - padding-left:5px; - margin-left:5px; -} -.np_row label { - display:block; - font-weight:bold; - margin:2px 0 0 -5px; -} -.np_group { - float:left; - padding-right:15px; -} -.np_row a { - font-size:10px; -} -/************************************************/ -/* Shoutbox */ -/************************************************/ - -#shoutbox { - font-size:1em; -} -#shoutbox div.shout { - padding-top:0.5em; - margin:10px 5px 0 0; - border-top:1px dotted #c0c0c0; -} -#shoutbox div.shout:hover { - border-top:1px solid #9cf; -} -#shoutbox span.information { -} -#shoutbox .shouttext { - display:block; - font-size:.9em; - margin-top:.5em; -} -img.shoutboximage { - margin:0 3px; -} -#shoutbox div.odd { - margin-right:20%; - text-align:left; -} -#shoutbox div.even { - margin-left:20%; - text-align:right; -} -#shoutbox .odd img.shoutboximage { - float:left; -} -#shoutbox .even img.shoutboximage { - float:right; -} -/************************************************/ -/* List Header */ -/************************************************/ -.list-header { - font-family:Verdana, Geneva, sans-serif; - font-size:10px; - text-transform:uppercase; - color:#000; - margin-top: 10px; - margin-bottom: 10px; - text-align:center; - position:relative; -} -.list-header .prev { - position:absolute; - top:0; - left:0; - text-transform:uppercase; - font-size:10px; - font-weight:bold; - color: #FC0; -} -.list-header .next { - position:absolute; - top:0; - right:0; - text-transform:uppercase; - font-size:10px; - font-weight:bold; - color: #FC0; - padding-right:0px; -} -.list-header .selected { - background: #fff; -} -.list-header .page-nb { - padding: 2px 5px 2px 5px; - border: 1px dotted #ccc; - text-decoration: none; -} -.list-header .page-nb:hover { - background: #FC0; - color:#000 -} -/************************************************/ -/* Errors */ -/************************************************/ -.error { - display:block; - font-family:Verdana, Geneva, sans-serif; - font-size: 10px; - color:#C00; -} -.fatalerror { - display:table-cell; - padding:3px; - color:#c00; - font-weight:bold; - font-size:10px; -} -/************************************************/ -/* LocalPlay */ -/************************************************/ -.lp_box_ctrl { -} -.lp_box_vol { - text-align: center; /*for compatibility, may be controlled by themers now*/ -} -td.lp_current a { - font-weight:bold; - text-decoration:none; -} -/************************************************/ -/* Styles for Login template */ -/************************************************/ -#loginPage #maincontainer { - margin:100px auto 0 auto; - width:360px; - font-size:10px; - text-align:center; -} -#loginPage #header { - padding:0; -} -#loginPage #loginbox { - background:url(../images/bg_login.jpg) no-repeat; - height:230px; -} -#loginPage h2 { - color:#fff; - padding-top:60px; - font-weight: normal; -} -.loginfield { - text-align:right; - padding-top:10px; - padding-right:50px; -} -.loginfield input.text_input { - vertical-align:middle; - width:180px; - border:5px solid #222; -} -.loginfield label { - color: #222; - font-weight:bold; -} -.loginfield #rememberme { - vertical-align:middle; - text-align:left; - margin-left: auto; - margin-right: auto; - background:none; -} -#loginPage div.fatalerror { - padding:5px; - margin:10px; -} -#motd { - background: #fff; - color: #222; - margin:0 auto 0 auto; - width: 360px; -} -/************************************************/ -/* Misc */ -/************************************************/ -.formValidation { - margin-top:10px; - text-align:center; -} -.text-box, .confirmation-box { - display:table-cell; - padding:5px 5px 0 5px; - margin-bottom:10px; - background:#bbb; - border:2px solid #000; -} -#ajax-loading { - position: absolute; - top:25px; - left:265px; - width:48px; - height:48px; - z-index:-1; - background: url(../images/ajax-loader.gif) no-repeat; - display: none; - text-indent:-9999em; -} -.information, .information a { - position:relative; - font-family:Verdana, Geneva, sans-serif; - font-size:10px; - font-weight:bold; - text-transform:uppercase; - color: #FC0; -} -.format-specifier { - text-transform: none; -} diff --git a/sources/themes/penguin/theme.cfg.php b/sources/themes/penguin/theme.cfg.php deleted file mode 100644 index aef3f91..0000000 --- a/sources/themes/penguin/theme.cfg.php +++ /dev/null @@ -1,53 +0,0 @@ -;;;;;;;;;;;;;;;;;; -;; -;;;;;;;;;;;;;;;;;; -; Copyright 2001 - 2013 Ampache.org -; -; This program is free software; you can redistribute it and/or -; modify it under the terms of the GNU General Public License v2 -; as published by the Free Software Foundation. -; -; 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, write to the Free Software -; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -; -;;;;;;;;;;;;;;;;;;;;;;;;;;; -; Penguin Ampache Theme -;;;;;;;;;;;;;;;;;;;;;;;;;;; - -; Theme Name -; This is the actual name of the theme that -; will be displayed in the preferences screen -; DEFAULT: ampache-theme -name = "Penguin" - -; Theme Author -; This is just a way of giving credit to the -; person who actually created this theme -; DEFAULT: N/A -author = "Jeroen Doppenberg" -; Updated by harrysand - -; Theme Maintainer -; This is just a way of listing who is responsible for -; maintaining this theme in case it's not working right -; please include an e-mail address so you can be contacted -; DEFAULT: N/A -maintainer = "info@dopdop.nl" - -; Orientation -; This was added as of 3.3.2-Alpha4, this tells Ampache if this theme -; uses vertical or horizontal orientation of the menu, if this is a horizontal -; theme then it will not show the quick search and quick random play forms -orientation = "vertical" - -; Submenu -; If this is set to simple the sub menu's will only be shown when you're on one of the -; respective pages. If you want to make the menu's something like the classic theme -; comment this out -;submenu = "simple" diff --git a/sources/themes/reborn/images/ampache-blue.png b/sources/themes/reborn/images/ampache-blue.png new file mode 100644 index 0000000..8784083 Binary files /dev/null and b/sources/themes/reborn/images/ampache-blue.png differ diff --git a/sources/themes/reborn/images/ampache-reborn-blue.png b/sources/themes/reborn/images/ampache-reborn-blue.png new file mode 100644 index 0000000..07e1032 Binary files /dev/null and b/sources/themes/reborn/images/ampache-reborn-blue.png differ diff --git a/sources/themes/reborn/images/blankmovie.png b/sources/themes/reborn/images/blankmovie.png new file mode 100644 index 0000000..7a1b784 Binary files /dev/null and b/sources/themes/reborn/images/blankmovie.png differ diff --git a/sources/themes/reborn/images/icons.sprite.png b/sources/themes/reborn/images/icons.sprite.png deleted file mode 100644 index ca9cc25..0000000 Binary files a/sources/themes/reborn/images/icons.sprite.png and /dev/null differ diff --git a/sources/themes/reborn/images/icons/icon_mail.png b/sources/themes/reborn/images/icons/icon_mail.png new file mode 100644 index 0000000..dcbb435 Binary files /dev/null and b/sources/themes/reborn/images/icons/icon_mail.png differ diff --git a/sources/themes/reborn/images/icons/icon_play_add.png b/sources/themes/reborn/images/icons/icon_play_add.png index 5a30a7c..99e78b7 100644 Binary files a/sources/themes/reborn/images/icons/icon_play_add.png and b/sources/themes/reborn/images/icons/icon_play_add.png differ diff --git a/sources/themes/reborn/images/icons/icon_upload.png b/sources/themes/reborn/images/icons/icon_upload.png new file mode 100644 index 0000000..eef1ac3 Binary files /dev/null and b/sources/themes/reborn/images/icons/icon_upload.png differ diff --git a/sources/themes/reborn/images/videoplay.png b/sources/themes/reborn/images/videoplay.png new file mode 100644 index 0000000..03d10ff Binary files /dev/null and b/sources/themes/reborn/images/videoplay.png differ diff --git a/sources/themes/reborn/templates/dark.css b/sources/themes/reborn/templates/dark.css new file mode 100644 index 0000000..f313650 --- /dev/null +++ b/sources/themes/reborn/templates/dark.css @@ -0,0 +1,544 @@ +/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ +/** + * + * LICENSE: GNU General Public License, version 2 (GPLv2) + * Copyright 2001 - 2015 Ampache.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License v2 + * as published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/*********************************************** + General style rules +***********************************************/ + +body { + color: #fff; + background-color: #222; + height: 90%; +} + +.error { + color: #c33; +} + +a { + color: #ff9d00; +} + +a:hover { + color: #ffc466; +} + +hr { + border-top-color: #bbb; + border-top-color: #eee; +} + +input[type=password], input[type=text], textarea { + background-color: #f5f5f5; + background-image: -moz-linear-gradient(top,#eee,#fff); + background-image: -ms-linear-gradient(top,#eee,#fff); + background-image: -webkit-gradient(linear,0 0,0 100%,from(#eee),to(#fff)); + background-image: -webkit-linear-gradient(top,#eee,#fff); + background-image: -o-linear-gradient(top,#eee,#fff); + background-image: linear-gradient(top,#eee,#fff); + border-color: #fff; +} + +input[type=button], input[type=submit] { + background-image: -moz-linear-gradient(top,#ff9d00,#cc6200); + background-image: -ms-linear-gradient(top,#ff9d00,#cc6200); + background-image: -webkit-gradient(linear,0 0,0 100%,from(#ff9d00),to(#cc6200)); + background-image: -webkit-linear-gradient(top,#ff9d00,#cc6200); + background-image: -o-linear-gradient(top,#ff9d00,#cc6200); + background-image: linear-gradient(top,#ff9d00,#cc6200); + -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.2); + -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.2); + box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.2); + color: #fff; +} + +input[type=button]:hover, input[type=button]:focus, input[type=submit]:hover, input[type=submit]:focus { + background-color: #cc6200; +} + +input[type=button]:focus:active, input[type=submit]:focus:active { + -webkit-box-shadow: inset 0 0 3px #000,0 1px 0 rgba(255,255,255,0.1); + -moz-box-shadow: inset 0 0 3px #000,0 1px 0 rgba(255,255,255,0.1); + box-shadow: inset 0 0 3px #000,0 1px 0 rgba(255,255,255,0.1); +} + +/*********************************************** + Main +***********************************************/ +#ajax-loading { + color: #ff9d00; + background-image: url(../images/ajax-loader.gif); +} + +/*********************************************** + Header +***********************************************/ +#header { + background-color: #000; + border-bottom: 2px solid #2d2d2d; + -webkit-box-shadow: 0 18px 18px rgba(30,30,30,0.7); + -moz-box-shadow: 0 18px 18px rgba(30,30,30,0.7); + box-shadow: 0 18px 18px rgba(30,30,30,0.7); +} + +#sb_Subsearch input[type=text] { + border-color: #444; +} + +#sb_Subsearch input[type=text]:focus { + color: #555; + background: rgba(255,255,255,0.9); + border-color:#ff9d00; +} + +#headerbox select { + color: #555; +} + +/*********************************************** + Login +***********************************************/ +#loginPage #maincontainer, #registerPage #maincontainer { + background-color: rgba(0,0,0,0.15); + border: 2px solid rgba(0,0,0,0.15); + -webkit-box-shadow: 0 0 5px rgba(255,255,255,0.05); + -moz-box-shadow: 0 0 5px rgba(255,255,255,0.05); + box-shadow: 0 0 5px rgba(255,255,255,0.05); +} + +#loginPage #headerlogo, #registerPage #headerlogo { + background-image: url('../images/ampache-reborn.png'); +} + +#loginPage #loginbox, #registerPage #registerbox { + color: #999; +} + +#loginPage #loginbox div, #registerPage #registerbox div { + text-shadow: 0 1px 0 #000; +} + +#loginPage #loginbox #usernamefield input:focus, +#loginPage #loginbox #emailfield input:focus, +#loginPage #loginbox #passwordfield input:focus { + border-color: #f1b720; + box-shadow: 0 0 10px #9ecaed; +} + +#loginPage #loginbox #remembermefield { + color: #eee; +} + +.formValidation input { + color: #fff; +} + +#loginPage span.error { + background: #ff9999; + border-color: #cc3333; + color: #990000; +} + +#loginPage #footer { + color: #888; +} + +#loginPage #footer a { + color: #555; +} + +#loginPage #footer #donate { + color: #ff9d00; +} + +/*********************************************** + Sidebar +***********************************************/ +#sidebar { + color: #999; +} + +#sidebar-header { + background-color: rgba(0,0,0,0.15); + border-color: rgba(0,0,0,0.15); + -webkit-box-shadow: 0 0 5px rgba(255,255,255,0.05); + -moz-box-shadow: 0 0 5px rgba(255,255,255,0.05); + box-shadow: 0 0 5px rgba(255,255,255,0.05); +} + +#sidebar-header:hover { + color: #fff; +} + +#sidebar-content, #sidebar-content-light, #sidebar-light { + border-color: rgba(0,0,0,0.15); + -webkit-box-shadow: 0 0 5px rgba(255,255,255,0.05); + -moz-box-shadow: 0 0 5px rgba(255,255,255,0.05); + box-shadow: 0 0 5px rgba(255,255,255,0.05); + background-color: #1a1a1a; + border-right-color: #000; +} + +/* Tabs content */ +#sidebar-page { + background-color: rgba(0,0,0,0.15); + border-color: rgba(0,0,0,0.15); + -webkit-box-shadow: 0 0 5px rgba(255,255,255,0.05); + -moz-box-shadow: 0 0 5px rgba(255,255,255,0.05); + box-shadow: 0 0 5px rgba(255,255,255,0.05); +} + +#sidebar-tabs .sb2 li h4 { + color: #fff; + background-color: #262626; + border-bottom-color: #0f0f0f; + -webkit-box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; + -moz-box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; + box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; +} + +#sidebar-tabs .sb2 li ul li a { + color: #999; +} + +#sidebar-tabs .sb2 li ul li a:hover { + color: #fff; +} + +#sidebar-tabs .sb2 #browse_filters #multi_alpha_filter { + border-color: #bbb; +} + +/* Localplay */ +.active_instance { + border-color: #fff; +} + +/*********************************************** + Rightbar +***********************************************/ +#rightbar { + //background-color: rgba(0,0,0,0.15); + background-color: #222; + border-color: gba(0,0,0,0.15); + -webkit-box-shadow: 0 0 5px rgba(255,255,255,0.05); + -moz-box-shadow: 0 0 5px rgba(255,255,255,0.05); + box-shadow: 0 0 5px rgba(255,255,255,0.05); + color: #fff; +} + +#rightbar #rb_current_playlist li { + color: #fff; +} + +#rightbar #rb_current_playlist li a { + color: #eee; +} + +#rightbar #rb_current_playlist li a:hover, #rightbar #rb_current_playlist li a:focus { + color: #fff; + background-color: #c85a00; + background-image: -moz-linear-gradient(top,#cc6200,#c24d00); + background-image: -ms-linear-gradient(top,#cc6200,#c24d00); + background-image: -webkit-gradient(linear,0 0,0 100%,from(#cc6200),to(#c24d00)); + background-image: -webkit-linear-gradient(top,#cc6200,#c24d00); + background-image: -o-linear-gradient(top,#cc6200,#c24d00); + background-image: linear-gradient(top,#cc6200,#c24d00); +} + +#rightbar #rb_current_playlist li.odd { + background-color: rgba(255,255,255,0.1); +} + +#rightbar .submenu { + background-color: #222; + border-color: silver; +} + +#rightbar .submenu a { + border-bottom-color: #ddd; + color: #eee; +} + +#rightbar .submenu a:hover { + color: #ff9d00; +} + +/*********************************************** + Content +***********************************************/ +#content { + background-color: rgba(0,0,0,0.15); + border-color: rgba(0,0,0,0.15); + -webkit-box-shadow: 0 0 5px rgba(255,255,255,0.05); + -moz-box-shadow: 0 0 5px rgba(255,255,255,0.05); + box-shadow: 0 0 5px rgba(255,255,255,0.05); + color: #fff; +} + +.list-header, .list-header a { + color: #999; +} + +.list-header a:hover, .list-header a:focus { + color: #fff; +} + +span.page-nb { + color: #ff9d00; + } + +table.tabledata { + color: #eee; +} + +table.tabledata a { + color: #eee; +} + +table.tabledata a:hover, table.tabledata a:focus { + color: #ffc466; +} + +table.tabledata thead .th-top, table.tabledata tfoot .th-bottom { + background-color: #262626; + -webkit-box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; + -moz-box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; + box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; +} + +table.tabledata thead .th-top { + border-bottom-color: #0f0f0f; +} + +table.tabledata tfoot .th-bottom { + border-top-color: #0f0f0f; +} + +table.tabledata th a { + color: #ff9d00; +} + +table.tabledata tr:hover, table.tabledata tr:focus { + background-color: #c85a00; + background-image: -moz-linear-gradient(top,#cc6200,#c24d00); + background-image: -ms-linear-gradient(top,#cc6200,#c24d00); + background-image: -webkit-gradient(linear,0 0,0 100%,from(#cc6200),to(#c24d00)); + background-image: -webkit-linear-gradient(top,#cc6200,#c24d00); + background-image: -o-linear-gradient(top,#cc6200,#c24d00); + background-image: linear-gradient(top,#cc6200,#c24d00); +} + +table.tabledata .th-top:hover, +table.tabledata .th-bottom:hover, +table.tabledata .th-top:focus, +table.tabledata .th-bottom:focus { + color: #fff; + background-color: #262626; +} + +table.tabledata tbody .odd { + background-color: rgba(255,255,255,0.1); +} + +table.tabledata tbody .cel_cover img { + -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.75); + -moz-box-shadow: 0 0 10px rgba(0,0,0,0.75); + box-shadow: 0 0 10px rgba(0,0,0,0.75); +} + +table.tabledata tbody .cel_cover img:hover { + border-color: #ff9d00; +} + +/*********************************************** + Content (info-box) +***********************************************/ +.random_selection .random_album .art_album img { + -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.75); + -moz-box-shadow: 0 0 10px rgba(0,0,0,0.75); + box-shadow: 0 0 10px rgba(0,0,0,0.75); +} + +.random_selection .random_video .art_album img { + -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.75); + -moz-box-shadow: 0 0 10px rgba(0,0,0,0.75); + box-shadow: 0 0 10px rgba(0,0,0,0.75); +} + +.random_selection .random_album .art_album img:hover { + border-color: #ff9d00; +} + +#content .missing { + background-image: url('../images/missing.png'); +} + +/*********************************************** + Content (now playing) +***********************************************/ +#now_playing .cel_albumart img { + -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.75); + -moz-box-shadow: 0 0 10px rgba(0,0,0,0.75); + box-shadow: 0 0 10px rgba(0,0,0,0.75); +} + +#now_playing .cel_lyrics a:hover { + color: #0099CC; +} + +/*********************************************** + Content (Tag cloud) +***********************************************/ +span.fatalerror { + color: #c60; +} + +.box-content #tag_filter .tag_button { + color: #fff; +} + +.box-content #tag_filter .tag_button span { + background-color: #5f5f5f; + background-image: -moz-linear-gradient(top,#6d6d6d,#4a4a4a); + background-image: -ms-linear-gradient(top,#6d6d6d,#4a4a4a); + background-image: -webkit-gradient(linear,0 0,0 100%,from(#6d6d6d),to(#4a4a4a)); + background-image: -webkit-linear-gradient(top,#6d6d6d,#4a4a4a); + background-image: -o-linear-gradient(top,#6d6d6d,#4a4a4a); + background-image: linear-gradient(top,#6d6d6d,#4a4a4a); + border-bottom-color: #b3b3b3; + -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),inset -1px 0 0 rgba(0,0,0,0.2); + -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),inset -1px 0 0 rgba(0,0,0,0.2); + box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),inset -1px 0 0 rgba(0,0,0,0.2); +} + +.box-content #tag_filter .tag_button span:hover { + background-color: #c85a00; + background-image: -moz-linear-gradient(top,#cc6200,#c24d00); + background-image: -ms-linear-gradient(top,#cc6200,#c24d00); + background-image: -webkit-gradient(linear,0 0,0 100%,from(#cc6200),to(#c24d00)); + background-image: -webkit-linear-gradient(top,#cc6200,#c24d00); + background-image: -o-linear-gradient(top,#cc6200,#c24d00); + background-image: linear-gradient(top,#cc6200,#c24d00); +} + +/*********************************************** + Content (information-actions) +***********************************************/ +.item_art .item_art_play .item_art_play_icon { + background-image: url('../images/videoplay.png'); +} + +.item_art .item_art_actions { + background-color: rgba(0, 0, 0, 0.6); +} + +.item_art img { + -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.75); + -moz-box-shadow: 0 0 10px rgba(0,0,0,0.75); + box-shadow: 0 0 10px rgba(0,0,0,0.75); +} + +.item_art:hover { + border-color: #ff9d00; +} + +#information_actions ul li{ + color: #999; +} + +#information_actions a:hover, #information_actions li:hover { + color: #fff;/*#ffc466;*/ +} + +/************************************************/ +/* Styles for user flags */ +/************************************************/ +.userflag a.userflag_true { + background: url(../../../images/icon_flag.png) left top; +} + +.userflag a:hover.userflag_true { + background: url(../../../images/icon_flag_off.png) left top; +} + +.userflag a.userflag_false { + background: url(../../../images/icon_flag_off.png) left top; +} + +.userflag a:hover.userflag_false { + background: url(../../../images/icon_flag.png) left top; +} + +/*********************************************** + Content (Track view) +***********************************************/ +dl.media_details dt { + color: #c60; +} + +/*********************************************** + Footer +***********************************************/ +#footer a:hover { + color: #09c; +} + +#ampache_link { + color: #555; +} + +/*********************************************** + Other +***********************************************/ +.jscroll-next { + border-color: #ccc; + background-color: #eee; + color: #999; +} + +.missing_album { + color: #bbb !important; +} + +.user_online { + background: #0f0; +} + +.user_offline { + background: #7f0000; +} + +.user_disabled { + background: #ccc; +} + +.list-header-navmenu-border { + border-color: #1d1d1d; +} + +span.item-count { + color: #999; +} + +a.option-list:hover { + border-color: #1d1d1d; +} diff --git a/sources/themes/reborn/templates/dark_preview.png b/sources/themes/reborn/templates/dark_preview.png new file mode 100644 index 0000000..6d67de2 Binary files /dev/null and b/sources/themes/reborn/templates/dark_preview.png differ diff --git a/sources/themes/reborn/templates/default.css b/sources/themes/reborn/templates/default.css index 5782de9..5e0f7b7 100644 --- a/sources/themes/reborn/templates/default.css +++ b/sources/themes/reborn/templates/default.css @@ -2,7 +2,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -33,11 +33,11 @@ ***********************************************/ html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td { border: 0; - outline:0 ; + outline: 0; vertical-align: baseline; background: transparent; - margin:0; - padding:0; + margin: 0; + padding: 0; } html { @@ -47,61 +47,49 @@ html { } body { - background-image: url('../images/background.png'); -webkit-tap-highlight-color: rgba(0,0,0,0); margin: 0; font-family: "DejaVuSansCondensed",Helvetica,Arial,sans-serif; font-weight: normal; font-size: 16px; line-height: 1.5em; - color: #fff; - background-color: #222; height: 90%; } blockquote,q { - quotes:none; + quotes: none; } blockquote:before,blockquote:after,q:before,q:after { - content:none; + content: none; } :focus { - outline:0; + outline: 0; } del { - text-decoration:line-through; + text-decoration: line-through; } table { - border-collapse:collapse; - border-spacing:0; -} - -.error { - color:#c33; + border-collapse: collapse; + border-spacing: 0; } a { - color: #ff9d00; text-decoration: none; cursor: pointer; } -a:hover { - color:#ffc466; -} - h3 { - font-size:20px; + font-size: 20px; margin-bottom: 5px; } hr { - border-top: 1px solid #bbb; - border-bottom: 1px solid #eee; + border-top: 1px solid; + border-bottom: 1px solid; border-right: 0; border-left: 0; } @@ -119,15 +107,8 @@ input[type=password], input[type=text] { input[type=password], input[type=text], textarea { padding: 4px 6px; - background-color: #f5f5f5; - background-image: -moz-linear-gradient(top,#eee,#fff); - background-image: -ms-linear-gradient(top,#eee,#fff); - background-image: -webkit-gradient(linear,0 0,0 100%,from(#eee),to(#fff)); - background-image: -webkit-linear-gradient(top,#eee,#fff); - background-image: -o-linear-gradient(top,#eee,#fff); - background-image: linear-gradient(top,#eee,#fff); background-repeat: repeat-x; - border: 2px solid #fff; + border: 2px solid; -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; @@ -138,12 +119,6 @@ input[type=password], input[type=text], textarea { } input[type=button], input[type=submit] { - background-image: -moz-linear-gradient(top,#ff9d00,#cc6200); - background-image: -ms-linear-gradient(top,#ff9d00,#cc6200); - background-image: -webkit-gradient(linear,0 0,0 100%,from(#ff9d00),to(#cc6200)); - background-image: -webkit-linear-gradient(top,#ff9d00,#cc6200); - background-image: -o-linear-gradient(top,#ff9d00,#cc6200); - background-image: linear-gradient(top,#ff9d00,#cc6200); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); position: relative; @@ -154,16 +129,11 @@ input[type=button], input[type=submit] { -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; - -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.2); - -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.2); - box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.2); cursor: pointer; - color: #fff; } input[type=button]:hover, input[type=button]:focus, input[type=submit]:hover, input[type=submit]:focus { background-position: 0 -10px; - background-color: #cc6200; -webkit-transition: background-position .1s linear; -moz-transition: background-position .1s linear; -ms-transition: background-position .1s linear; @@ -174,9 +144,6 @@ input[type=button]:hover, input[type=button]:focus, input[type=submit]:hover, in } input[type=button]:focus:active, input[type=submit]:focus:active { - -webkit-box-shadow:inset 0 0 3px #000,0 1px 0 rgba(255,255,255,0.1); - -moz-box-shadow:inset 0 0 3px #000,0 1px 0 rgba(255,255,255,0.1); - box-shadow:inset 0 0 3px #000,0 1px 0 rgba(255,255,255,0.1); -webkit-transition: background-position .1s linear; -moz-transition: background-position .1s linear; -ms-transition: background-position .1s linear; @@ -193,7 +160,7 @@ input[type=button]:focus:active, input[type=submit]:focus:active { Main ***********************************************/ #maincontainer { - width:100%; + width: 100%; position: relative; z-index: 1; padding-bottom: 40px; @@ -202,7 +169,11 @@ input[type=button]:focus:active, input[type=submit]:focus:active { #footer { width: auto; z-index: 5; - margin: 15px 270px 0px 0px; + margin: 15px 250px 0px 0px; +} + +.footer-wild { + margin-right: 20px !important; } #ajax-loading { @@ -212,9 +183,9 @@ input[type=button]:focus:active, input[type=submit]:focus:active { font-size: 12px; text-align: center; width: 120px; - color: #ff9d00; - background: url(../images/ajax-loader.gif) no-repeat 2px; display: none; + background-repeat: no-repeat; + background-position: right; } /*********************************************** @@ -224,11 +195,6 @@ input[type=button]:focus:active, input[type=submit]:focus:active { z-index: 5; height: 64px; padding: 0 32px; - background-color: #000; - border-bottom: 2px solid #2d2d2d; - -webkit-box-shadow: 0 18px 18px rgba(30,30,30,0.7); - -moz-box-shadow: 0 18px 18px rgba(30,30,30,0.7); - box-shadow: 0 18px 18px rgba(30,30,30,0.7); font-size: 15px; } @@ -297,22 +263,13 @@ input[type=button]:focus:active, input[type=submit]:focus:active { padding: 0 25px; margin: 0; font-size: 15px; - border-color: #444; -webkit-border-radius: 15px; -moz-border-radius: 15px; border-radius: 15px; } -#sb_Subsearch input[type=text]:focus { - color: #555; - background: #fff; - background: rgba(255,255,255,0.9); - border-color:#ff9d00; -} - #headerbox select { min-width: 150px; - color: #555; -moz-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; @@ -329,69 +286,76 @@ input[type=button]:focus:active, input[type=submit]:focus:active { /*********************************************** Login ***********************************************/ -#loginPage #maincontainer { +#loginPage #maincontainer, #registerPage #maincontainer { padding: 20px; - background-color: #222; - background-color: rgba(0,0,0,0.15); -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; - border-color: #1d1d1d; - border: 2px solid rgba(0,0,0,0.15); - -webkit-box-shadow: 0 0 5px rgba(255,255,255,0.05); - -moz-box-shadow: 0 0 5px rgba(255,255,255,0.05); - box-shadow: 0 0 5px rgba(255,255,255,0.05); -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; display: block; width: 800px; - height: 580px; margin: 50px auto 0 auto; } -#loginPage #header { +#loginPage #maincontainer { + height: 580px; +} + +#registerPage #maincontainer { + height: auto; +} + +#loginPage #header, #registerPage #header { background-color: transparent; - height: 254px; width: 254px; margin: auto; border: none; box-shadow: none; } -#loginPage #headerlogo { +#loginPage #header { + height: 254px; +} + +#registerPage #header { + height: 314px; +} + +#loginPage #headerlogo, #registerPage #headerlogo { width: 256px; height: 256px; - background: url('../images/ampache-reborn.png') no-repeat center; + background-repeat: no-repeat; + background-position: center; background-size: contain; margin: auto; } -#loginPage #headerlogo a { +#loginPage #headerlogo a, #registerPage #headerlogo a { display: none; } -#loginPage #loginbox { +#loginPage #loginbox, #registerPage #registerbox { width: 500px; margin: auto auto 20px auto; - color: #999; } -#loginPage #loginbox h2{ +#loginPage #loginbox h2, #registerPage #registerbox h2{ display: none; } -#loginPage #loginbox div { +#loginPage #loginbox div, #registerPage #registerbox div { font-size: 18px; line-height: 1.5em; - text-shadow: 0 1px 0 #000; text-rendering: auto; } #loginPage #loginbox #usernamefield input, #loginPage #loginbox #emailfield input, -#loginPage #loginbox #passwordfield input { +#loginPage #loginbox #passwordfield input, +#registerPage #registerbox input { float: none; margin-left: 0; width: 100%; @@ -403,13 +367,15 @@ input[type=button]:focus:active, input[type=submit]:focus:active { height: 32px; } +#registerbox .require label::after { + content: " *"; +} + #loginPage #loginbox #usernamefield input:focus, #loginPage #loginbox #emailfield input:focus, #loginPage #loginbox #passwordfield input:focus { float: none; outline: none; - border-color: #f1b720; - box-shadow: 0 0 10px #9ecaed; } #loginPage #loginbox #usernamefield, @@ -420,7 +386,6 @@ input[type=button]:focus:active, input[type=submit]:focus:active { } #loginPage #loginbox #remembermefield { - color: #eee; font-size: 16px; } @@ -440,7 +405,6 @@ input[type=button]:focus:active, input[type=submit]:focus:active { } .formValidation input { - color: #fff; border: none; min-width: 150px; } @@ -454,11 +418,8 @@ input[type=button]:focus:active, input[type=submit]:focus:active { #loginPage span.error { display: block; clear: left; + border: 1px solid; padding: 10px; - background: #ff9999; - border: 1px solid #cc3333; - /*font-weight: bold;*/ - color: #990000; text-align: center; margin-bottom: 10px; } @@ -466,49 +427,104 @@ input[type=button]:focus:active, input[type=submit]:focus:active { #loginPage #footer { width: 800px; margin: 10px auto; - color: #888; -} - -#loginPage #footer a { - color: #555; } /*********************************************** Sidebar ***********************************************/ #sidebar { - width: 150px; - /*z-index: 8;*/ - margin: 20px auto auto 5px; - padding: 10px; - border-radius: 2px; - border-color: #1d1d1d; - border: 2px solid rgba(0,0,0,0.15); + font-size: 12px; +} + +.sidebar-collapsed { + width: 50px !important; +} + +.sidebar-header-title { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + width: 125px; + display: inline-block; +} + +#sidebar-header { + height: 20px; + width: 175px; + border: 2px solid; -webkit-border-radius: 2px; -moz-border-radius: 2px; - -webkit-box-shadow: 0 0 5px rgba(255,255,255,0.05); - -moz-box-shadow: 0 0 5px rgba(255,255,255,0.05); - box-shadow: 0 0 5px rgba(255,255,255,0.05); -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; - font-size: 12px; - background-color: #1a1a1a; - border-right: 2px solid #000; - color: #999; + text-align: center; + font-size: 10px; +} + +#sidebar-header:hover { + cursor: pointer; +} + +#sidebar-header span { + line-height: 15px; +} + +.sidebar-header-collapsed { + width: 50px !important; + position: fixed; +} + +#sidebar-content, #sidebar-content-light, #sidebar-light { + border-radius: 2px; + border: 2px solid; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + border-right: 2px solid; + padding: 10px; +} + +.sidebar-content-collapsed { + display: none !important; +} + +#sidebar-content-light { + padding: 0px; + display: none; + width: 50px; +} + +#sidebar-light { + width: 50px; + position: fixed; + left: 0px; + margin: 17px auto 5px 5px; + padding: 10px 0 10px 0; + text-align: center; +} + +#sidebar-light img { + margin: auto; + height: 32px; + width: 32px; +} + +.sidebar-content-light-collapsed { + display: block !important; } .sidebar-float { + margin: 20px auto auto 5px; float: left; } .sidebar-fixed { - position: fixed; - top: 66px; - left: 5px; - height: 90%; - overflow: hidden; + margin: 0px auto auto 5px; + float: left; } /* For sidebar tabs */ @@ -519,28 +535,24 @@ input[type=button]:focus:active, input[type=submit]:focus:active { #sidebar-tabs li.sb1 { float: left; - margin-right: 3px; + margin: 0px 4px 0px 4px; height: 25px; } #sidebar-tabs li.active { } + #sidebar-tabs li.active img{ } /* Tabs content */ #sidebar-page { - width: 150px; + width: 175px; margin: 3px auto 5px 5px; - background-color: rgba(0,0,0,0.15); -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; - border-color: #1d1d1d; - border: 2px solid rgba(0,0,0,0.15); - -webkit-box-shadow: 0 0 5px rgba(255,255,255,0.05); - -moz-box-shadow: 0 0 5px rgba(255,255,255,0.05); - box-shadow: 0 0 5px rgba(255,255,255,0.05); + border: 2px solid; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -ms-box-sizing: border-box; @@ -552,41 +564,36 @@ input[type=button]:focus:active, input[type=submit]:focus:active { left: 0px; } -.sidebar-page-fixed { - position: absolute; - left: -7px; - overflow-x: hidden; - overflow-y: auto; - height: 90%; +#sidebar-tabs .sb2 li h4 { + height: 23px; + margin: 10px 0 5px 0; + padding: 0 10px 0 20px; + border-bottom: 1px solid; } -#sidebar-tabs .sb2 li h4 { - color: #fff; - height: 23px; - margin: 10px 0 5px; - padding: 0 10px 0 20px; - background-color: #262626; - border-bottom: 1px solid #0f0f0f; - -webkit-box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; - -moz-box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; - box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; +#sidebar-tabs .sb2 li h4:hover { + cursor: pointer; } #sidebar-tabs .sb2 li ul li a { - border-radius: 4px; - margin-top: 3px; - padding: 7px; - background: none!important; - -webkit-transition: color .1s; - -moz-transition: color .1s; - -ms-transition: color .1s; - -o-transition: color .1s; - transition: color .1s; - color: #999; + width: 100%; + margin: 0 0 0 5px; + background-color: transparent; + -webkit-transition: color .1s; + -moz-transition: color .1s; + -ms-transition: color .1s; + -o-transition: color .1s; + transition: color .1s; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + display: inline-block; } #sidebar-tabs .sb2 li ul li a:hover { - color: #fff; + width: 95%; + padding-left: 5px; } #sidebar-tabs .sb2 #browse_filters li { @@ -599,19 +606,23 @@ input[type=button]:focus:active, input[type=submit]:focus:active { } #sidebar-tabs .sb2 #browse_filters #multi_alpha_filter { - border: 1px solid #bbb; + border: 1px solid; border-radius: 2px; padding: 4px; width: 130px; } +#sidebar-page .sb2 { + margin-bottom: 100px; +} + #sidebar-tabs #catalog_select { width: 130px; } /* Localplay */ .active_instance { - border: 1px solid #fff; + border: 1px solid; } /*********************************************** @@ -625,21 +636,15 @@ input[type=button]:focus:active, input[type=submit]:focus:active { margin: 20px 20px auto auto; max-height: 85%; padding: 10px; - background-color: rgba(0,0,0,0.15); -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; - border-color: #1d1d1d; - border: 2px solid rgba(0,0,0,0.15); - -webkit-box-shadow: 0 0 5px rgba(255,255,255,0.05); - -moz-box-shadow: 0 0 5px rgba(255,255,255,0.05); - box-shadow: 0 0 5px rgba(255,255,255,0.05); + border: 2px solid; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; - color: #fff; - z-index: 0; + z-index: 10; overflow-y: auto; } @@ -653,7 +658,7 @@ input[type=button]:focus:active, input[type=submit]:focus:active { .fixedrightbarsubmenu { position: fixed !important; top: 33px !important; - right: 70px !important; + right: 15px !important; } /******************************/ @@ -674,7 +679,6 @@ input[type=button]:focus:active, input[type=submit]:focus:active { #rightbar #rb_current_playlist li { min-height: 29px; font-size: 14px; - color: #fff; width: 100%; display: table; } @@ -682,25 +686,12 @@ input[type=button]:focus:active, input[type=submit]:focus:active { #rightbar #rb_current_playlist li a { width: 100%; padding: 4px 7px 3px; - color:#eee; display: table-cell; } #rightbar #rb_current_playlist li a:hover, #rightbar #rb_current_playlist li a:focus { - color:#fff; - text-shadow:none; - background-color:#c85a00; - background-image:-moz-linear-gradient(top,#cc6200,#c24d00); - background-image:-ms-linear-gradient(top,#cc6200,#c24d00); - background-image:-webkit-gradient(linear,0 0,0 100%,from(#cc6200),to(#c24d00)); - background-image:-webkit-linear-gradient(top,#cc6200,#c24d00); - background-image:-o-linear-gradient(top,#cc6200,#c24d00); - background-image:linear-gradient(top,#cc6200,#c24d00); - background-repeat:repeat-x; -} - -#rightbar #rb_current_playlist li.odd { - background-color: rgba(255,255,255,0.1); + text-shadow: none; + background-repeat: repeat-x; } #rightbar li:hover .submenu { @@ -711,14 +702,15 @@ input[type=button]:focus:active, input[type=submit]:focus:active { display: none; position: fixed; top: 120px; - right: 70px; - background-color: #222; - border: 2px solid silver; - width: 120px; + right: 15px; + border: 2px solid; + width: 210px; padding: 0.6em; font-size: 12px; z-index: 99; - overflow: hidden; + overflow: hidden; + overflow-y: scroll; + max-height: 350px; } #rightbar #rb_action .submenu li { @@ -727,8 +719,7 @@ input[type=button]:focus:active, input[type=submit]:focus:active { #rightbar .submenu a { display: block; - border-bottom: 1px dotted #ddd; - color: #eee; + border-bottom: 1px dotted; text-decoration: none; text-align: left; padding: 0.4em; @@ -736,7 +727,6 @@ input[type=button]:focus:active, input[type=submit]:focus:active { } #rightbar .submenu a:hover { - color: #ff9d00; cursor: pointer; } @@ -767,33 +757,35 @@ input[type=button]:focus:active, input[type=submit]:focus:active { width: auto; max-height: 85%; padding: 10px; - background-color: rgba(0,0,0,0.15); -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; - border-color: #1d1d1d; - border: 2px solid rgba(0,0,0,0.15); - -webkit-box-shadow: 0 0 5px rgba(255,255,255,0.05); - -moz-box-shadow: 0 0 5px rgba(255,255,255,0.05); - box-shadow: 0 0 5px rgba(255,255,255,0.05); + border: 2px solid; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; - color: #fff; padding: 20px; } .content-float { - margin: 20px 250px 0px 180px; + margin: 20px 250px 0px 205px; } -.content-wild { +.content-right-wild { margin-right: 20px !important; } +.content-left-wild { + margin-left: 70px !important; +} + .content-fixed { - margin: 86px 250px 0px 180px; + margin: 86px 250px 0px 205px; +} + +.content-fixed-topmenu { + margin: 0px 250px 0px 205px; } .browse_content { @@ -802,51 +794,32 @@ input[type=button]:focus:active, input[type=submit]:focus:active { .list-header, .list-header a { margin: 10px 0 10px 0; - color: #999; font-size: 13px; } .list-header a:hover, .list-header a:focus { - color: #fff; text-decoration: none; } -span.page-nb { - color: #ff9d00; - } - table.tabledata { width:100%; /*table-layout: fixed;*/ text-align:left; /*border:1px solid #bbb;*/ font-size: 14px; - color: #eee; margin-bottom: 20px; } -table.tabledata a { - color:#eee; -} - -table.tabledata a:hover, table.tabledata a:focus { - color: #ffc466; -} - table.tabledata thead .th-top, table.tabledata tfoot .th-bottom { font-size: 12px; - background-color: #262626; - -webkit-box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; - -moz-box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; - box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; } table.tabledata thead .th-top { - border-bottom: 1px solid #0f0f0f; + border-bottom: 1px solid; } table.tabledata tfoot .th-bottom { - border-top: 1px solid #0f0f0f; + border-top: 1px solid; display: none; } @@ -858,38 +831,22 @@ table.tabledata td { padding: 3px 10px 3px 0; } -table.tabledata th a { - color:#ff9d00; -} - table.tabledata tr:hover, table.tabledata tr:focus { - text-shadow:none; - background-color:#c85a00; - background-image:-moz-linear-gradient(top,#cc6200,#c24d00); - background-image:-ms-linear-gradient(top,#cc6200,#c24d00); - background-image:-webkit-gradient(linear,0 0,0 100%,from(#cc6200),to(#c24d00)); - background-image:-webkit-linear-gradient(top,#cc6200,#c24d00); - background-image:-o-linear-gradient(top,#cc6200,#c24d00); - background-image:linear-gradient(top,#cc6200,#c24d00); - background-repeat:repeat-x; + text-shadow: none; + background-repeat: repeat-x; } table.tabledata .th-top:hover, table.tabledata .th-bottom:hover, table.tabledata .th-top:focus, table.tabledata .th-bottom:focus { - color: #fff; - background-color: #262626; background-image: none; background-repeat: no-repeat; } -table.tabledata tbody .odd { - background-color: rgba(255,255,255,0.1); -} - table.tabledata tbody .cel_play { - max-width: 40px; + max-width: 60px; + min-width: 60px; width: 40px !important; text-align: right; } @@ -909,8 +866,13 @@ table.tabledata tbody tr:hover .cel_play_content, table.tabledata tbody tr:focus display: none; } +table.tabledata tbody tr.unread { + font-weight: bold; +} + table.tabledata tbody .cel_add { max-width: 60px; + min-width: 60px; width: 60px !important; text-align: right; } @@ -957,14 +919,11 @@ table.tabledata tbody .cel_cover img { -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; - -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.75); - -moz-box-shadow: 0 0 10px rgba(0,0,0,0.75); - box-shadow: 0 0 10px rgba(0,0,0,0.75); margin: 2px; } table.tabledata tbody .cel_cover img:hover { - border: 2px solid #ff9d00; + border: 2px solid; margin: 0px; } @@ -985,6 +944,10 @@ table.tabledata .cel_agent img:hover { cursor: help; } +#shoutbox-input { + font-size: 14px; +} + .box_preferences h4 { font-size: 15px; margin-bottom: 10px; @@ -998,7 +961,7 @@ div.box.box_current_configuration { Content (info-box) ***********************************************/ #content .info-box h3 { - margin-top:10px; + margin-top: 10px; } #content .info-box .box-content div.star-rating { @@ -1007,50 +970,58 @@ div.box.box_current_configuration { } /* Random album (homepage) */ -#random_selection { +.random_selection { margin-bottom: 20px; } -#random_selection .random_album { +.random_selection .random_album, .random_selection .random_video { float: left; - width: 125px; + width: 16%; + max-width: 175px; margin-bottom: 10px; text-align: center; } -#random_selection .art_album img { +.random_selection .random_album .art_album img { vertical-align: middle; -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; - -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.75); - -moz-box-shadow: 0 0 10px rgba(0,0,0,0.75); - box-shadow: 0 0 10px rgba(0,0,0,0.75); - width: 80px; - height: 80px; + width: 100px; + height: 100px; margin: 2px; } -#random_selection .art_album img:hover { - border: 2px solid #ff9d00; +.random_selection .random_video .art_album img { + vertical-align: middle; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; + width: 100px; + height: 150px; + margin: 2px; +} + +.random_selection .random_album .art_album img:hover { + border: 2px solid; margin: 0px; } -#random_selection .random_album .star-rating { +.random_selection .random_album .star-rating, .random_selection .random_video .star-rating { margin: auto; } -#random_selection .random_album .play_album { +.random_selection .random_album .play_album, .random_selection .random_video .play_video { margin-left: 7px; } -#random_selection .random_album .play_album img { +.random_selection .random_album .play_album img, .random_selection .random_video .play_video img { border: 0; box-shadow: 0 0 0 0; } -#random_selection .box-bottom { - clear:left; +.random_selection .box-bottom { + clear: left; } tr#search_item_count td, tr#search_length td, tr#search_size_limit td, tr#search_max_results td { @@ -1065,13 +1036,15 @@ div.box.box_rules { } #content .missing { - background-image: url('../images/missing.png'); background-repeat: no-repeat; background-position: center; } .cel_song, .cel_album, .cel_artist { max-width: 350px; + white-space: normal !important; + text-overflow: ellipsis; + overflow: hidden; } /*********************************************** @@ -1112,13 +1085,15 @@ div.box.box_rules { width: 140px; } -#now_playing .cel_song, #now_playing .cel_album, #now_playing .cel_artist { +#now_playing .cel_song, #now_playing .cel_album, #now_playing .cel_artist, #now_playing .cel_video { width: 200px; + overflow: hidden; + text-overflow: ellipsis; } #now_playing .cel_albumart { float: left; - width: 90px; + width: 110px; } #now_playing .cel_albumart img { @@ -1126,26 +1101,14 @@ div.box.box_rules { -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; - -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.75); - -moz-box-shadow: 0 0 10px rgba(0,0,0,0.75); - box-shadow: 0 0 10px rgba(0,0,0,0.75); float: left; margin: 2px; } -#now_playing .cel_albumart img:hover { - border: 2px solid #ff9d00; - margin: 0px; -} - #now_playing .cel_lyrics { margin-top: 5px; } -#now_playing .cel_lyrics a:hover { - color: #0099CC; -} - #now_playing .similars { margin-right: 10px; padding-right: 5px; @@ -1183,7 +1146,6 @@ div.box.box_rules { } span.fatalerror { - color: #c60; padding: 5px; display: block; } @@ -1201,7 +1163,6 @@ span.fatalerror { margin: 20px 0 0 15px; font-size: 14px; vertical-align: middle; - color: #fff; text-align: center; } @@ -1212,16 +1173,8 @@ span.fatalerror { .box-content #tag_filter .tag_button span { padding: 8px 10px 8px; cursor: pointer; - background-color: #5f5f5f; - background-image: -moz-linear-gradient(top,#6d6d6d,#4a4a4a); - background-image: -ms-linear-gradient(top,#6d6d6d,#4a4a4a); - background-image: -webkit-gradient(linear,0 0,0 100%,from(#6d6d6d),to(#4a4a4a)); - background-image: -webkit-linear-gradient(top,#6d6d6d,#4a4a4a); - background-image: -o-linear-gradient(top,#6d6d6d,#4a4a4a); - background-image: linear-gradient(top,#6d6d6d,#4a4a4a); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - border-bottom-color: #b3b3b3; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; @@ -1231,20 +1184,10 @@ span.fatalerror { -webkit-border-bottom-left-radius: 2px; -moz-border-radius-bottomleft: 2px; border-bottom-left-radius: 2px; - -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),inset -1px 0 0 rgba(0,0,0,0.2); - -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),inset -1px 0 0 rgba(0,0,0,0.2); - box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),inset -1px 0 0 rgba(0,0,0,0.2); } .box-content #tag_filter .tag_button span:hover { - background-color:#c85a00; - background-image:-moz-linear-gradient(top,#cc6200,#c24d00); - background-image:-ms-linear-gradient(top,#cc6200,#c24d00); - background-image:-webkit-gradient(linear,0 0,0 100%,from(#cc6200),to(#c24d00)); - background-image:-webkit-linear-gradient(top,#cc6200,#c24d00); - background-image:-o-linear-gradient(top,#cc6200,#c24d00); - background-image:linear-gradient(top,#cc6200,#c24d00); - background-repeat:repeat-x; + background-repeat: repeat-x; } .box-content #tag_filter li{ @@ -1271,30 +1214,96 @@ span.fatalerror { margin-bottom: 30px; } -#content .info-box .box-content .album_art { +#content .info-box .box-content .item_art { float: right; } -#content .info-box .box-content .album_art img { +.item_info { + float: right; +} + +.item_properties { + text-align: right; + color: #999; + font-size: 13px; + clear: right; + max-width: 128px; +} + +.item_art { + border: 2px solid transparent; + display: inline-block; + position: relative; +} + +.item_art .item_art_play { + display: none; + left: 0; + right: 0; + margin: 0 auto; + width: 64px; + height: 100%; + position: absolute; +} + +.item_art .item_art_play .item_art_play_icon { + width: 64px; + height: 64px; + position: relative; + text-align: center; + opacity: 0.4; + display: block; + margin: 100% 0 0 0; +} + +.item_art .item_art_play .item_art_play_icon:hover { + opacity:1; +} + +.item_art .item_art_actions { + display: none; + position: absolute; + float: right; + bottom: 0px; + right: 0px; + padding-bottom: 10px; + padding-top: 5px; + width: 100%; + height: 16px; + text-align: right; +} + +.item_art .item_art_actions span { + padding-right: 6px; +} + +.item_art img { vertical-align: middle; -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; - -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.75); - -moz-box-shadow: 0 0 10px rgba(0,0,0,0.75); - box-shadow: 0 0 10px rgba(0,0,0,0.75); float: left; margin: 2px; } -#content .info-box .box-content .album_art img:hover { - border: 2px solid #ff9d00; +.item_art_actions img { + float: none; +} + +.item_art:hover { + border: 2px solid; margin: 0px; } +.item_art:hover .item_art_actions { + display: block; +} + +.item_art:hover .item_art_play { + display: block; +} + #information_actions ul li{ - /*border-bottom:1px solid #ccc;*/ - color: #999; border-radius: 4px; -webkit-transition: color .1s; -moz-transition: color .1s; @@ -1309,27 +1318,28 @@ span.fatalerror { #information_actions a { margin-right: 5px; - color: #999; } #information_actions input { margin: 0px 5px 0 2px; } -#information_actions a:hover, #information_actions li:hover { - color: #fff;/*#ffc466;*/ -} - #information_actions .star-rating li { padding: 0; } #information_actions li img { - vertical-align:top; + vertical-align: top; } #information_actions li a { - vertical-align:bottom; + vertical-align: bottom; +} + +.item_uploaded_by { + padding-left: 0px; + padding-top: 10px; + font-size: small; } .item_right_info { @@ -1350,7 +1360,7 @@ span.fatalerror { opacity: 1; } -#artist_summary { +#item_summary { margin-right: 150px; } @@ -1363,6 +1373,7 @@ span.fatalerror { .dynamic-star-rating { width:95px; } + .star-rating ul, .star-rating a:hover, .star-rating .current-rating { @@ -1422,58 +1433,42 @@ span.fatalerror { /************************************************/ /* Styles for user flags */ /************************************************/ -.userflag -{ +.userflag { position: relative; - width:16px; - height:16px; + width: 16px; + height: 16px; } .userflag a { - position:absolute; + position: absolute; display: inline; } -.userflag a.userflag_true -{ - width:16px; +.userflag a.userflag_true { + width: 16px; height: 16px; - background: url(../../../images/icon_flag.png) left top; } -.userflag a:hover.userflag_true -{ - background: url(../../../images/icon_flag_off.png) left top; -} - -.userflag a.userflag_false -{ - width:16px; - height:16px; - background: url(../../../images/icon_flag_off.png) left top; -} - -.userflag a:hover.userflag_false -{ - background: url(../../../images/icon_flag.png) left top; +.userflag a.userflag_false { + width: 16px; + height: 16px; } /*********************************************** Content (Track view) ***********************************************/ -.song_details { +.media_details { margin-top: 20px; } -dl.song_details dt { +dl.media_details dt { float: left; clear: left; width: 200px; text-align: right; - color: #c60; } -dl.song_details dd { +dl.media_details dd { margin: 10px 0 10px 250px; height: 24px; } @@ -1482,11 +1477,10 @@ dl.song_details dd { Footer ***********************************************/ #footer { - text-align:right; + text-align: right; } #footer a:hover { - color: #09c; text-decoration: underline; } @@ -1494,7 +1488,7 @@ dl.song_details dd { Other ***********************************************/ ol, ul, #rightbar ul { - list-style:none; + list-style: none; } .browse-options { @@ -1518,31 +1512,77 @@ ol, ul, #rightbar ul { } .jscroll-next { - width:50%; - display:block; - border:1px solid #ccc; - -webkit-border-radius:10px; - -moz-border-radius:10px; + width: 50%; + display: block; + border: 1px solid; + -webkit-border-radius: 10px; + -moz-border-radius: 10px; border-radius: 10px; - background-color:#eee; - color:#999; - font-weight:bold; - text-align:center; - padding:10px 0; - cursor:pointer; + font-weight: bold; + text-align: center; + padding: 10px 0; + cursor: pointer; margin: auto; } -.jscroll-next:hover { - color:#666; -} - .missing_album { text-decoration: none; - border-bottom:1px dotted; - color: #bbb !important; + border-bottom: 1px dotted; } .user_avatar { float: right; } + +#plugins_user_field { + margin-top: 15px; +} + +.list-header-navmenu-border { + border: 1px solid; + border-radius: 2px; +} + +input.list-header-navmenu-input { + padding: 0px; + margin-bottom: 2px; + width: 50px; + height: 16px; + text-align: center; +} + +span.item-count { + font-size: 11px; +} + +a.option-list { + border: solid 1px transparent; + width: 100%; + margin: 0; + display: inline-block; +} + +a.option-list:hover { + border: 1px solid; +} + +.stats_graph { + text-align: center; +} + +.followbtn { + background-repeat: repeat-x; + position: relative; + padding: 5px 12px 4px; + font-size: 15px; + line-height: normal; + border: 0px none; + cursor: pointer; + border-radius: 2px; + background-position: 0px -10px; + transition: background-position 0.1s linear 0s; + text-shadow: 0px -1px 0px rgba(0, 0, 0, 0.5); + text-decoration: none; + min-width: 100px; + background-color: #363F4A; +} \ No newline at end of file diff --git a/sources/themes/reborn/templates/icons.sprite.css b/sources/themes/reborn/templates/icons.sprite.css deleted file mode 100644 index f38c5e2..0000000 --- a/sources/themes/reborn/templates/icons.sprite.css +++ /dev/null @@ -1,71 +0,0 @@ -.sprite { background: url(../images/icons.sprite.png) no-repeat top left; display: inline-block; } -.sprite-icon_add{ background-position: 0 0; width: 16px; height: 16px; } -.sprite-icon_add12{ background-position: 0 -32px; width: 16px; height: 16px; } -.sprite-icon_add2{ background-position: 0 -64px; width: 16px; height: 16px; } -.sprite-icon_add_key{ background-position: 0 -96px; width: 16px; height: 16px; } -.sprite-icon_add_tag{ background-position: 0 -128px; width: 16px; height: 16px; } -.sprite-icon_add_user{ background-position: 0 -160px; width: 16px; height: 16px; } -.sprite-icon_add_wanted{ background-position: 0 -192px; width: 16px; height: 16px; } -.sprite-icon_admin{ background-position: 0 -224px; width: 16px; height: 16px; } -.sprite-icon_all{ background-position: 0 -256px; width: 16px; height: 16px; } -.sprite-icon_ampache{ background-position: 0 -288px; width: 16px; height: 16px; } -.sprite-icon_batch_download{ background-position: 0 -320px; width: 16px; height: 16px; } -.sprite-icon_broadcast{ background-position: 0 -352px; width: 16px; height: 16px; } -.sprite-icon_cancel{ background-position: 0 -384px; width: 16px; height: 16px; } -.sprite-icon_cog{ background-position: 0 -416px; width: 16px; height: 16px; } -.sprite-icon_comment{ background-position: 0 -448px; width: 16px; height: 16px; } -.sprite-icon_delete{ background-position: 0 -480px; width: 16px; height: 16px; } -.sprite-icon_disable{ background-position: 0 -512px; width: 16px; height: 16px; } -.sprite-icon_download{ background-position: 0 -544px; width: 16px; height: 16px; } -.sprite-icon_drag{ background-position: 0 -576px; width: 16px; height: 16px; } -.sprite-icon_edit{ background-position: 0 -608px; width: 16px; height: 16px; } -.sprite-icon_edit2{ background-position: 0 -640px; width: 16px; height: 16px; } -.sprite-icon_enable{ background-position: 0 -672px; width: 16px; height: 16px; } -.sprite-icon_equalizer{ background-position: 0 -704px; width: 16px; height: 16px; } -.sprite-icon_feed{ background-position: 0 -736px; width: 16px; height: 16px; } -.sprite-icon_flag{ background-position: 0 -768px; width: 16px; height: 16px; } -.sprite-icon_flag_off{ background-position: 0 -800px; width: 16px; height: 16px; } -.sprite-icon_flow{ background-position: 0 -832px; width: 16px; height: 16px; } -.sprite-icon_fullscreen{ background-position: 0 -864px; width: 16px; height: 16px; } -.sprite-icon_google{ background-position: 0 -896px; width: 16px; height: 16px; } -.sprite-icon_home{ background-position: 0 -928px; width: 16px; height: 16px; } -.sprite-icon_image{ background-position: 0 -960px; width: 16px; height: 16px; } -.sprite-icon_info{ background-position: 0 -992px; width: 16px; height: 16px; } -.sprite-icon_lastfm{ background-position: 0 -1024px; width: 16px; height: 16px; } -.sprite-icon_link{ background-position: 0 -1056px; width: 16px; height: 16px; } -.sprite-icon_lock{ background-position: 0 -1088px; width: 16px; height: 16px; } -.sprite-icon_logout{ background-position: 0 -1120px; width: 16px; height: 16px; } -.sprite-icon_microphone{ background-position: 0 -1152px; width: 16px; height: 16px; } -.sprite-icon_money{ background-position: 0 -1184px; width: 16px; height: 16px; } -.sprite-icon_next{ background-position: 0 -1216px; width: 16px; height: 16px; } -.sprite-icon_next_hover{ background-position: 0 -1248px; width: 16px; height: 16px; } -.sprite-icon_pause{ background-position: 0 -1280px; width: 16px; height: 16px; } -.sprite-icon_pause_hover{ background-position: 0 -1312px; width: 16px; height: 16px; } -.sprite-icon_play{ background-position: 0 -1344px; width: 16px; height: 16px; } -.sprite-icon_play_add{ background-position: 0 -1376px; width: 16px; height: 16px; } -.sprite-icon_play_add_preview{ background-position: 0 -1408px; width: 16px; height: 16px; } -.sprite-icon_play_hover{ background-position: 0 -1440px; width: 16px; height: 16px; } -.sprite-icon_play_preview{ background-position: 0 -1472px; width: 16px; height: 16px; } -.sprite-icon_playlist_add{ background-position: 0 -1504px; width: 16px; height: 16px; } -.sprite-icon_plugin{ background-position: 0 -1536px; width: 16px; height: 16px; } -.sprite-icon_preferences{ background-position: 0 -1568px; width: 16px; height: 16px; } -.sprite-icon_prev{ background-position: 0 -1600px; width: 16px; height: 16px; } -.sprite-icon_prev_hover{ background-position: 0 -1632px; width: 16px; height: 16px; } -.sprite-icon_random{ background-position: 0 -1664px; width: 16px; height: 16px; } -.sprite-icon_run{ background-position: 0 -1696px; width: 16px; height: 16px; } -.sprite-icon_save{ background-position: 0 -1728px; width: 16px; height: 16px; } -.sprite-icon_server_lightning{ background-position: 0 -1760px; width: 16px; height: 16px; } -.sprite-icon_share{ background-position: 0 -1792px; width: 16px; height: 16px; } -.sprite-icon_statistics{ background-position: 0 -1824px; width: 16px; height: 16px; } -.sprite-icon_stop{ background-position: 0 -1856px; width: 16px; height: 16px; } -.sprite-icon_stop_hover{ background-position: 0 -1888px; width: 16px; height: 16px; } -.sprite-icon_tick{ background-position: 0 -1920px; width: 16px; height: 16px; } -.sprite-icon_view{ background-position: 0 -1952px; width: 16px; height: 16px; } -.sprite-icon_visualizer{ background-position: -32px 0; width: 16px; height: 16px; } -.sprite-icon_volumedn{ background-position: -32px -32px; width: 16px; height: 16px; } -.sprite-icon_volumemute{ background-position: -32px -64px; width: 16px; height: 16px; } -.sprite-icon_volumeup{ background-position: -32px -96px; width: 16px; height: 16px; } -.sprite-icon_wand{ background-position: -32px -128px; width: 16px; height: 16px; } -.sprite-icon_wikipedia{ background-position: -32px -160px; width: 16px; height: 16px; } -.sprite-icon_world_link{ background-position: -32px -192px; width: 16px; height: 16px; } - diff --git a/sources/themes/reborn/templates/light.css b/sources/themes/reborn/templates/light.css new file mode 100644 index 0000000..f56a1f8 --- /dev/null +++ b/sources/themes/reborn/templates/light.css @@ -0,0 +1,526 @@ +/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */ +/** + * + * LICENSE: GNU General Public License, version 2 (GPLv2) + * Copyright 2001 - 2015 Ampache.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License v2 + * as published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/*********************************************** + General style rules +***********************************************/ + +body { + color: #666; + background-color: #fff; + height: 90%; +} + +.error { + color: #c33; +} + +a { + color: #1990db; +} + +a:hover { + color: #52aae4; +} + +hr { + border-top-color: #bbb; + border-top-color: #eee; +} + +input[type=password], input[type=text], textarea { + background-color: #f5f5f5; + background-image: -moz-linear-gradient(top,#eee,#fff); + background-image: -ms-linear-gradient(top,#eee,#fff); + background-image: -webkit-gradient(linear,0 0,0 100%,from(#eee),to(#fff)); + background-image: -webkit-linear-gradient(top,#eee,#fff); + background-image: -o-linear-gradient(top,#eee,#fff); + background-image: linear-gradient(top,#eee,#fff); + border-color: #d8d8d8; +} + +input[type=button], input[type=submit] { + background-image: -moz-linear-gradient(top,#1990DB,#1473AF); + background-image: -ms-linear-gradient(top,#1990DB,#1473AF); + background-image: -webkit-gradient(linear,0 0,0 100%,from(#1990DB),to(#1473AF)); + background-image: -webkit-linear-gradient(top,#1990DB,#1473AF); + background-image: -o-linear-gradient(top,#1990DB,#1473AF); + background-image: linear-gradient(top,#1990DB,#1473AF); + -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.2); + -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.2); + box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.2); + color: #fff; +} + +input[type=button]:hover, input[type=button]:focus, input[type=submit]:hover, input[type=submit]:focus { + background-color: #1473AF; +} + +input[type=button]:focus:active, input[type=submit]:focus:active { + -webkit-box-shadow: inset 0 0 3px #000,0 1px 0 rgba(255,255,255,0.1); + -moz-box-shadow: inset 0 0 3px #000,0 1px 0 rgba(255,255,255,0.1); + box-shadow: inset 0 0 3px #000,0 1px 0 rgba(255,255,255,0.1); +} + +/*********************************************** + Main +***********************************************/ +#ajax-loading { + color: #1990db; + background-image: url(../images/ajax-loader.gif); +} + +/*********************************************** + Header +***********************************************/ +#header { + background-color: #eee; + border-bottom: 2px solid #d8d8d8; +} + +#sb_Subsearch input[type=text] { + border-color: #bbb; +} + +#sb_Subsearch input[type=text]:focus { + color: #555; + background: rgba(255,255,255,0.9); + border-color: #1990db; +} + +#headerbox select { + color: #555; +} + +/*********************************************** + Login +***********************************************/ +#loginPage { + background-color: #eee; +} + +#loginPage #maincontainer, #registerPage #maincontainer { + background-color: #fff; + border: 2px solid rgba(0,0,0,0.15); + -webkit-box-shadow: 0 0 5px rgba(255,255,255,0.05); + -moz-box-shadow: 0 0 5px rgba(255,255,255,0.05); + box-shadow: 0 0 5px rgba(255,255,255,0.05); +} + +#loginPage #headerlogo, #registerPage #headerlogo { + background-image: url('../images/ampache-reborn-blue.png'); +} + +#loginPage #loginbox #usernamefield input:focus, +#loginPage #loginbox #emailfield input:focus, +#loginPage #loginbox #passwordfield input:focus { + border-color: #6AAFFA; + box-shadow: 0 0 10px #9ecaed; +} + +.formValidation input { + color: #000; +} + +#loginPage span.error { + background: #ff9999; + border-color: #cc3333; + color: #990000; +} + +#loginPage #footer { + color: #888; +} + +#loginPage #footer a { + color: #555; +} + +#loginPage #footer #donate { + color: #1990db; +} + +/*********************************************** + Sidebar +***********************************************/ +#sidebar { + color: #666; +} + +#sidebar-header { + background-color: #f8f8f8; + border-color: #d8d8d8; + -webkit-box-shadow: 0 0 5px rgba(255,255,255,0.05); + -moz-box-shadow: 0 0 5px rgba(255,255,255,0.05); + box-shadow: 0 0 5px rgba(255,255,255,0.05); +} + +#sidebar-header:hover { + color: #000; +} + +#sidebar-content, #sidebar-content-light, #sidebar-light { + border-color: #d8d8d8; + -webkit-box-shadow: 0 0 5px rgba(255,255,255,0.05); + -moz-box-shadow: 0 0 5px rgba(255,255,255,0.05); + box-shadow: 0 0 5px rgba(255,255,255,0.05); + background-color: #fcfcfc; +} + +/* Tabs content */ +#sidebar-page { + background-color: #f8f8f8; + border-color: #d8d8d8; + -webkit-box-shadow: 0 0 5px rgba(255,255,255,0.05); + -moz-box-shadow: 0 0 5px rgba(255,255,255,0.05); + box-shadow: 0 0 5px rgba(255,255,255,0.05); +} + +#sidebar-tabs .sb2 li h4 { + color: #111; + background-color: #e3e3e3; + border-bottom-color: #0f0f0f; + -webkit-box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; + -moz-box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; + box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; +} + +#sidebar-tabs .sb2 li ul li a { + color: #999; +} + +#sidebar-tabs .sb2 li ul li a:hover { + color: #000; +} + +#sidebar-tabs .sb2 #browse_filters #multi_alpha_filter { + border-color: #bbb; +} + +/* Localplay */ +.active_instance { + border-color: #000; +} + +/*********************************************** + Rightbar +***********************************************/ +#rightbar { + background-color: #f8f8f8; + border-color: #d8d8d8; + -webkit-box-shadow: 0 0 5px rgba(255,255,255,0.05); + -moz-box-shadow: 0 0 5px rgba(255,255,255,0.05); + box-shadow: 0 0 5px rgba(255,255,255,0.05); + color: #666; +} + +#rightbar #rb_current_playlist li { + color: #666; +} + +#rightbar #rb_current_playlist li a { + color: #000; +} + +#rightbar #rb_current_playlist li a:hover, #rightbar #rb_current_playlist li a:focus { + background-color: #bfe1f6; + background-image: -moz-linear-gradient(top,#bfe1f6,#82caf8); + background-image: -ms-linear-gradient(top,#bfe1f6,#82caf8); + background-image: -webkit-gradient(linear,0 0,0 100%,from(#bfe1f6),to(#82caf8)); + background-image: -webkit-linear-gradient(top,#bfe1f6,#82caf8); + background-image: -o-linear-gradient(top,#bfe1f6,#82caf8); + background-image: linear-gradient(top,#bfe1f6,#82caf8); +} + +#rightbar #rb_current_playlist li.odd { + background-color: rgba(0,0,0,0.1); +} + +#rightbar .submenu { + background-color: #eee; + border-color: silver; +} + +#rightbar .submenu a { + border-bottom-color: #eee; + color: #111; +} + +#rightbar .submenu a:hover { + color: #1990db; +} + +/*********************************************** + Content +***********************************************/ +#content { + background-color: #f8f8f8; + border-color: #d8d8d8; + -webkit-box-shadow: 0 0 5px rgba(255,255,255,0.05); + -moz-box-shadow: 0 0 5px rgba(255,255,255,0.05); + box-shadow: 0 0 5px rgba(255,255,255,0.05); + color: #666; +} + +.list-header, .list-header a { + color: #999; +} + +.list-header a:hover, .list-header a:focus { + color: #fff; +} + +span.page-nb { + color: #1990db; + } + +table.tabledata { + color: #666; +} + +table.tabledata a { + color: #666; +} + +table.tabledata a:hover, table.tabledata a:focus { + color: #52aae4; +} + +table.tabledata thead .th-top, table.tabledata tfoot .th-bottom { + background-color: #e3e3e3; + -webkit-box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; + -moz-box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; + box-shadow: 0 1px 0 #2a2a2a,inset 0 1px 1px #000; +} + +table.tabledata thead .th-top { + border-bottom-color: #0f0f0f; +} + +table.tabledata tfoot .th-bottom { + border-top-color: #0f0f0f; +} + +table.tabledata th a { + color: #1990db; +} + +table.tabledata tr:hover, table.tabledata tr:focus { + background-color: #bfe1f6; + background-image: -moz-linear-gradient(top,#bfe1f6,#82caf8); + background-image: -ms-linear-gradient(top,#bfe1f6,#82caf8); + background-image: -webkit-gradient(linear,0 0,0 100%,from(#bfe1f6),to(#82caf8)); + background-image: -webkit-linear-gradient(top,#bfe1f6,#82caf8); + background-image: -o-linear-gradient(top,#bfe1f6,#82caf8); + background-image: linear-gradient(top,#bfe1f6,#82caf8); +} + +table.tabledata .th-top:hover, +table.tabledata .th-bottom:hover, +table.tabledata .th-top:focus, +table.tabledata .th-bottom:focus { + color: #777; + background-color: #e9e9e9; +} + +table.tabledata tbody .odd { + background-color: rgba(255,255,255,0.1); +} + +table.tabledata tbody .cel_cover img { + -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.75); + -moz-box-shadow: 0 0 10px rgba(0,0,0,0.75); + box-shadow: 0 0 10px rgba(0,0,0,0.75); +} + +table.tabledata tbody .cel_cover img:hover { + border-color: #1990db; +} + +/*********************************************** + Content (info-box) +***********************************************/ +.random_selection .random_album .art_album img { + -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.75); + -moz-box-shadow: 0 0 10px rgba(0,0,0,0.75); + box-shadow: 0 0 10px rgba(0,0,0,0.75); +} + +.random_selection .random_video .art_album img { + -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.75); + -moz-box-shadow: 0 0 10px rgba(0,0,0,0.75); + box-shadow: 0 0 10px rgba(0,0,0,0.75); +} + +.random_selection .random_album .art_album img:hover { + border-color: #1990db; +} + +#content .missing { + background-image: url('../images/missing.png'); +} + +/*********************************************** + Content (now playing) +***********************************************/ +#now_playing .cel_albumart img { + -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.75); + -moz-box-shadow: 0 0 10px rgba(0,0,0,0.75); + box-shadow: 0 0 10px rgba(0,0,0,0.75); +} + +#now_playing .cel_lyrics a:hover { + color: #0099CC; +} + +/*********************************************** + Content (Tag cloud) +***********************************************/ +span.fatalerror { + color: #c60; +} + +.box-content #tag_filter .tag_button { + color: #fff; +} + +.box-content #tag_filter .tag_button span { + background-color: #5f5f5f; + background-image: -moz-linear-gradient(top,#6d6d6d,#4a4a4a); + background-image: -ms-linear-gradient(top,#6d6d6d,#4a4a4a); + background-image: -webkit-gradient(linear,0 0,0 100%,from(#6d6d6d),to(#4a4a4a)); + background-image: -webkit-linear-gradient(top,#6d6d6d,#4a4a4a); + background-image: -o-linear-gradient(top,#6d6d6d,#4a4a4a); + background-image: linear-gradient(top,#6d6d6d,#4a4a4a); + border-bottom-color: #b3b3b3; + -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),inset -1px 0 0 rgba(0,0,0,0.2); + -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),inset -1px 0 0 rgba(0,0,0,0.2); + box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),inset -1px 0 0 rgba(0,0,0,0.2); +} + +.box-content #tag_filter .tag_button span:hover { + background-color: #c85a00; + background-image: -moz-linear-gradient(top,#1473AF,#027ecc); + background-image: -ms-linear-gradient(top,#1473AF,#027ecc); + background-image: -webkit-gradient(linear,0 0,0 100%,from(#1473AF),to(#027ecc)); + background-image: -webkit-linear-gradient(top,#1473AF,#027ecc); + background-image: -o-linear-gradient(top,#1473AF,#027ecc); + background-image: linear-gradient(top,#1473AF,#027ecc); +} + +/*********************************************** + Content (information-actions) +***********************************************/ +.item_art .item_art_play .item_art_play_icon { + background-image: url('../images/videoplay.png'); +} + +.item_art .item_art_actions { + background-color: rgba(0, 0, 0, 0.6); +} + +.item_art img { + -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.75); + -moz-box-shadow: 0 0 10px rgba(0,0,0,0.75); + box-shadow: 0 0 10px rgba(0,0,0,0.75); +} + +.item_art:hover { + border-color: #1990db; +} + +#information_actions ul li{ + color: #999; +} + +/************************************************/ +/* Styles for user flags */ +/************************************************/ +.userflag a.userflag_true { + background: url(../../../images/icon_flag.png) left top; +} + +.userflag a:hover.userflag_true { + background: url(../../../images/icon_flag_off.png) left top; +} + +.userflag a.userflag_false { + background: url(../../../images/icon_flag_off.png) left top; +} + +.userflag a:hover.userflag_false { + background: url(../../../images/icon_flag.png) left top; +} + +/*********************************************** + Content (Track view) +***********************************************/ +dl.media_details dt { + color: #c60; +} + +/*********************************************** + Footer +***********************************************/ +#footer a:hover { + color: #09c; +} + +#ampache_link { + color: #555; +} + +/*********************************************** + Other +***********************************************/ +.jscroll-next { + border-color: #ccc; + background-color: #eee; + color: #999; +} + +.missing_album { + color: #bbb !important; +} + +.user_online { + background: #0f0; +} + +.user_offline { + background: #7f0000; +} + +.user_disabled { + background: #ccc; +} + +.list-header-navmenu-border { + border-color: #fff; +} + +span.item-count { + color: #999; +} + +a.option-list:hover { + border-color: #fff; +} diff --git a/sources/themes/reborn/theme.cfg.php b/sources/themes/reborn/theme.cfg.php index 022d408..e352fda 100644 --- a/sources/themes/reborn/theme.cfg.php +++ b/sources/themes/reborn/theme.cfg.php @@ -1,7 +1,7 @@ ;;;;;;;;;;;;;;;;;; ;; ;;;;;;;;;;;;;;;;;; -; Copyright 2001 - 2013 Ampache.org +; Copyright 2001 - 2015 Ampache.org ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License v2 @@ -16,37 +16,27 @@ ; along with this program; if not, write to the Free Software ; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ; -;;;;;;;;;;;;;;;;;;;;;;;;;;; -; Reborn Ampache Theme -;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;; +; Reborn Ampache Theme ; +;;;;;;;;;;;;;;;;;;;;;;;; ; Theme Name -; This is the actual name of the theme that -; will be displayed in the preferences screen -; DEFAULT: ampache-theme name = "Reborn" +; Theme CSS Bases +; Structure: [css_path] +; Multiples bases must be separated with ',' +base = "templates/default.css" + +; Theme CSS Colors +; Structure: [name]|[css_path]|[css_preview] (| = pipe) +; Multiples colors must be separated with ',' +; Example: "Blue|blue.css|blue_preview.png,Red|red.css|red_preview.png" +; DEFAULT : N/A +colors = "Dark|templates/dark.css|templates/dark_preview.png,Light|templates/light.css|templates/light_preview.png" + ; Theme Author -; This is just a way of giving credit to the -; person who actually created this theme -; DEFAULT: N/A author = "SUTJael" ; Theme Maintainer -; This is just a way of listing who is responsible for -; maintaining this theme in case it's not working right -; please include an e-mail address so you can be contacted -; DEFAULT: N/A -maintainer = "SUTJael - orco@netcourrier.com" - -; Orientation -; This was added as of 3.3.2-Alpha4, this tells Ampache if this theme -; uses vertical or horizontal orientation of the menu, if this is a horizontal -; theme then it will not show the quick search and quick random play forms -orientation = "vertical" - -; Submenu -; If this is set to simple the sub menu's will only be shown when you're on one of the -; respective pages. If you want to make the menu's something like the classic theme -; comment this out -;submenu = "simple" +maintainer = "SUTJael - sutjael@ampache.org" diff --git a/sources/tvshow_seasons.php b/sources/tvshow_seasons.php new file mode 100644 index 0000000..af5281e --- /dev/null +++ b/sources/tvshow_seasons.php @@ -0,0 +1,68 @@ +id . '`.', 1); + UI::access_denied(); + exit; + } + + if ($tvshow_season->remove_from_disk()) { + show_confirmation(T_('TVShow Season Deletion'), T_('TVShow Season has been deleted.'), AmpConfig::get('web_path')); + } else { + show_confirmation(T_('TVShow Season Deletion'), T_('Cannot delete this tvshow season.'), AmpConfig::get('web_path')); + } + break; + case 'show': + $season = new TVShow_Season($_REQUEST['season']); + $season->format(); + $object_ids = $season->get_episodes(); + $object_type = 'tvshow_episode'; + require_once AmpConfig::get('prefix') . '/templates/show_tvshow_season.inc.php'; + break; +} // end switch + +UI::show_footer(); diff --git a/sources/tvshows.php b/sources/tvshows.php new file mode 100644 index 0000000..e579866 --- /dev/null +++ b/sources/tvshows.php @@ -0,0 +1,91 @@ +id . '`.', 1); + UI::access_denied(); + exit; + } + + if ($tvshow->remove_from_disk()) { + show_confirmation(T_('TVShow Deletion'), T_('TVShow has been deleted.'), AmpConfig::get('web_path')); + } else { + show_confirmation(T_('TVShow Deletion'), T_('Cannot delete this tvshow.'), AmpConfig::get('web_path')); + } + break; + case 'show': + $tvshow = new TVShow($_REQUEST['tvshow']); + $tvshow->format(); + $object_ids = $tvshow->get_seasons(); + $object_type = 'tvshow_season'; + require_once AmpConfig::get('prefix') . '/templates/show_tvshow.inc.php'; + break; + case 'match': + case 'Match': + $match = scrub_in($_REQUEST['match']); + if ($match == "Browse") { $chr = ""; } else { $chr = $match; } + /* Enclose this in the purty box! */ + require AmpConfig::get('prefix') . '/templates/show_box_top.inc.php'; + show_alphabet_list('tvshows','tvshows.php',$match); + show_alphabet_form($chr, T_('Show TV Shows starting with'),"tvshows.php?action=match"); + require AmpConfig::get('prefix') . '/templates/show_box_bottom.inc.php'; + + if ($match === "Browse") { + show_tvshows(); + } elseif ($match === "Show_all") { + $offset_limit = 999999; + show_tvshows(); + } else { + if ($chr == '') { + show_tvshows('A'); + } else { + show_tvshows($chr); + } + } + break; +} // end switch + +UI::show_footer(); diff --git a/sources/update.php b/sources/update.php index fa68733..a78467b 100644 --- a/sources/update.php +++ b/sources/update.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -20,22 +20,34 @@ * */ -// We need this stuff -define('NO_SESSION', 1); -define('OUTDATED_DATABASE_OK', 1); +if (!isset($_REQUEST['type']) || $_REQUEST['type'] != 'sources') { + // We need this stuff + define('NO_SESSION', 1); + define('OUTDATED_DATABASE_OK', 1); +} require_once 'lib/init.php'; // Get the version and format it $version = Update::get_version(); -if ($_REQUEST['action'] == 'update') { +if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'update') { + if ($_REQUEST['type'] == 'sources') { + if (!Access::check('interface', '100')) { + UI::access_denied(); + exit; + } - /* Run the Update Mojo Here */ - Update::run_update(); - - /* Get the New Version */ - $version = Update::get_version(); + set_time_limit(300); + AutoUpdate::update_files(); + echo ''; + exit; + } else { + /* Run the Update Mojo Here */ + Update::run_update(); + /* Get the New Version */ + $version = Update::get_version(); + } } $htmllang = str_replace("_","-",AmpConfig::get('lang')); @@ -43,9 +55,10 @@ $htmllang = str_replace("_","-",AmpConfig::get('lang')); + - Ampache :: For the love of Music - Update + <?php echo AmpConfig::get('site_title'); ?> - Update @@ -64,8 +77,8 @@ $htmllang = str_replace("_","-",AmpConfig::get('lang'));

-

3.3.3.5. According to your database your current version is: %s.'), Update::format_version($version)); ?>

-

+

3.3.3.5. Your current version is %s with database version %s.'), AmpConfig::get('version'), $version); ?>

+

diff --git a/sources/upload.php b/sources/upload.php new file mode 100644 index 0000000..7d2016b --- /dev/null +++ b/sources/upload.php @@ -0,0 +1,58 @@ + 0 && ($post_max < $upload_max || $upload_max == 0)) { + $upload_max = $post_max; +} +// Check to handle POST requests exceeding max post size. +if ($_SERVER['CONTENT_LENGTH'] > 0 && $post_max > 0 && $_SERVER['CONTENT_LENGTH'] > $post_max) { + Upload::rerror(); + exit; +} + +/* Switch on the action passed in */ +switch ($_REQUEST['actionp']) { + case 'upload': + if (AmpConfig::get('demo_mode')) { + UI::access_denied(); + exit; + } + + Upload::process(); + exit; + + default: + UI::show_header(); + require AmpConfig::get('prefix') . '/templates/show_add_upload.inc.php'; + break; +} // switch on the action + +UI::show_footer(); diff --git a/sources/upnp/MediaServerConnectionManager.xml b/sources/upnp/MediaServerConnectionManager.xml new file mode 100644 index 0000000..ae9af0a --- /dev/null +++ b/sources/upnp/MediaServerConnectionManager.xml @@ -0,0 +1,134 @@ + + + +1 +0 + + + + GetProtocolInfo + + + Source + out + SourceProtocolInfo + + + Sink + out + SinkProtocolInfo + + + + + GetCurrentConnectionIDs + + + ConnectionIDs + out + CurrentConnectionIDs + + + + + GetCurrentConnectionInfo + + + ConnectionID + in + A_ARG_TYPE_ConnectionID + + + RcsID + out + A_ARG_TYPE_RcsID + + + AVTransportID + out + A_ARG_TYPE_AVTransportID + + + ProtocolInfo + out + A_ARG_TYPE_ProtocolInfo + + + PeerConnectionManager + out + A_ARG_TYPE_ConnectionManager + + + PeerConnectionID + out + A_ARG_TYPE_ConnectionID + + + Direction + out + A_ARG_TYPE_Direction + + + Status + out + A_ARG_TYPE_ConnectionStatus + + + + + + + + SourceProtocolInfo + string + + + SinkProtocolInfo + string + + + CurrentConnectionIDs + string + + + A_ARG_TYPE_ConnectionStatus + string + + OK + ContentFormatMismatch + InsufficientBandwidth + UnreliableChannel + Unknown + + + + A_ARG_TYPE_ConnectionManager + string + + + A_ARG_TYPE_Direction + string + + Input + Output + + + + A_ARG_TYPE_ProtocolInfo + string + + + A_ARG_TYPE_ConnectionID + i4 + + + A_ARG_TYPE_AVTransportID + i4 + + + A_ARG_TYPE_RcsID + i4 + + + + diff --git a/sources/upnp/MediaServerContentDirectory.xml b/sources/upnp/MediaServerContentDirectory.xml new file mode 100644 index 0000000..d7eba64 --- /dev/null +++ b/sources/upnp/MediaServerContentDirectory.xml @@ -0,0 +1,208 @@ + + + + 1 + 0 + + + + GetSearchCapabilities + + + SearchCaps + out + SearchCapabilities + + + + + GetSortCapabilities + + + SortCaps + out + SortCapabilities + + + + + GetSystemUpdateID + + + Id + out + SystemUpdateID + + + + + Browse + + + ObjectID + in + A_ARG_TYPE_ObjectID + + + BrowseFlag + in + A_ARG_TYPE_BrowseFlag + + + Filter + in + A_ARG_TYPE_Filter + + + StartingIndex + in + A_ARG_TYPE_Index + + + RequestedCount + in + A_ARG_TYPE_Count + + + SortCriteria + in + A_ARG_TYPE_SortCriteria + + + Result + out + A_ARG_TYPE_Result + + + NumberReturned + out + A_ARG_TYPE_Count + + + TotalMatches + out + A_ARG_TYPE_Count + + + UpdateID + out + A_ARG_TYPE_UpdateID + + + + + + Search + + + ContainerID + in + A_ARG_TYPE_ObjectID + + + SearchCriteria + in + A_ARG_TYPE_SearchCriteria + + + Filter + in + A_ARG_TYPE_Filter + + + StartingIndex + in + A_ARG_TYPE_Index + + + RequestedCount + in + A_ARG_TYPE_Count + + + SortCriteria + in + A_ARG_TYPE_SortCriteria + + + Result + out + A_ARG_TYPE_Result + + + NumberReturned + out + A_ARG_TYPE_Count + + + TotalMatches + out + A_ARG_TYPE_Count + + + UpdateID + out + A_ARG_TYPE_UpdateID + + + + + + + A_ARG_TYPE_ObjectID + string + + + A_ARG_TYPE_Result + string + + + A_ARG_TYPE_SearchCriteria + string + + + A_ARG_TYPE_BrowseFlag + string + + BrowseMetadata + BrowseDirectChildren + + + + A_ARG_TYPE_Filter + string + + + A_ARG_TYPE_SortCriteria + string + + + A_ARG_TYPE_Index + ui4 + + + A_ARG_TYPE_Count + ui4 + + + A_ARG_TYPE_UpdateID + ui4 + + + SearchCapabilities + string + + + SortCapabilities + string + + + SystemUpdateID + ui4 + + + ContainerUpdateIDs + string + + + diff --git a/sources/upnp/MediaServerServiceDesc.php b/sources/upnp/MediaServerServiceDesc.php new file mode 100644 index 0000000..b6a87c2 --- /dev/null +++ b/sources/upnp/MediaServerServiceDesc.php @@ -0,0 +1,91 @@ +'; +?> + + + 1 + 0 + + + urn:schemas-upnp-org:device:MediaServer:1 + Ampache + ampache.org + http://ampache.org + + Ampache + + http://ampache.org + uuid: + + + image/png + 32 + 32 + 24 + /upnp/images/icon32.png + + + image/png + 48 + 48 + 24 + /upnp/images/icon48.png + + + image/png + 120 + 120 + 24 + /upnp/images/icon120.png + + + image/jpeg + 32 + 32 + 24 + /upnp/images/icon32.jpg + + + image/jpeg + 48 + 48 + 24 + /upnp/images/icon48.jpg + + + image/jpeg + 120 + 120 + 24 + /upnp/images/icon120.jpg + + + + + urn:schemas-upnp-org:service:ContentDirectory:1 + urn:upnp-org:serviceId:ContentDirectory + /upnp/control-reply.php + /upnp/event-reply.php + /upnp/MediaServerContentDirectory.xml + + + urn:schemas-upnp-org:service:ConnectionManager:1 + urn:upnp-org:serviceId:ConnectionManager + /upnp/cm-control-reply.php + /upnp/cm-event-reply.php + /upnp/MediaServerConnectionManager.xml + + + + diff --git a/sources/upnp/cm-control-reply.php b/sources/upnp/cm-control-reply.php new file mode 100644 index 0000000..e2fbc85 --- /dev/null +++ b/sources/upnp/cm-control-reply.php @@ -0,0 +1,32 @@ + diff --git a/sources/upnp/cm-event-reply.php b/sources/upnp/cm-event-reply.php new file mode 100644 index 0000000..99dcd3f --- /dev/null +++ b/sources/upnp/cm-event-reply.php @@ -0,0 +1,3 @@ + diff --git a/sources/upnp/control-reply.php b/sources/upnp/control-reply.php new file mode 100644 index 0000000..2aaab78 --- /dev/null +++ b/sources/upnp/control-reply.php @@ -0,0 +1,104 @@ + '0', + 'parentID' => '-1', + 'childCount' => '2', + 'dc:title' => T_('root'), + 'upnp:class' => 'object.container', + ); + } else { + $items = $rootMediaItems; + } + break; + } else { + # The parse_url function returns an array in this format: + # Array ( + # [scheme] => http + # [host] => hostname + # [user] => username + # [pass] => password + # [path] => /path + # [query] => arg=value + # [fragment] => anchor + # ) + $reqObjectURL = parse_url($upnpRequest['objectid']); + switch ($reqObjectURL['scheme']) { + case 'amp': + switch ($reqObjectURL['host']) { + case 'music': + if ($upnpRequest['browseflag'] == 'BrowseMetadata') { + $items = Upnp_Api::_musicMetadata($reqObjectURL['path'], $reqObjectURL['query']); + } else { + list($totMatches, $items) = Upnp_Api::_musicChilds($reqObjectURL['path'], $reqObjectURL['query'], $upnpRequest['startingindex'], $upnpRequest['requestedcount']); + } + break; + case 'video': + if ($upnpRequest['browseflag'] == 'BrowseMetadata') { + $items = Upnp_Api::_videoMetadata($reqObjectURL['path'], $reqObjectURL['query']); + } else { + list($totMatches, $items) = Upnp_Api::_videoChilds($reqObjectURL['path'], $reqObjectURL['query'], $upnpRequest['startingindex'], $upnpRequest['requestedcount']); + } + break; + } + break; + } + }; + break; + } + + $totMatches = ($totMatches == 0) ? count($items) : $totMatches; + if ($items == null || $totMatches == 0) { + $domDIDL = Upnp_Api::createDIDL(''); + $numRet = 0; + } else { + $domDIDL = Upnp_Api::createDIDL($items); + $numRet = count($items); + } + + $xmlDIDL = $domDIDL->saveXML(); + $domSOAP = Upnp_Api::createSOAPEnvelope($xmlDIDL, $numRet, $totMatches, $responseType); + $soapXML = $domSOAP->saveXML(); + + echo $soapXML; + //!!debug_event('upnp', 'Response: ' . $soapXML, '5'); +?> diff --git a/sources/upnp/event-reply.php b/sources/upnp/event-reply.php new file mode 100644 index 0000000..4c91f2c --- /dev/null +++ b/sources/upnp/event-reply.php @@ -0,0 +1,19 @@ + diff --git a/sources/upnp/find.php b/sources/upnp/find.php new file mode 100644 index 0000000..0f64cdd --- /dev/null +++ b/sources/upnp/find.php @@ -0,0 +1,140 @@ +initByDiscoveryReponse($response)) { + + $device->saveToCache(); + + try { + $client = $device->getClient('ConnectionManager'); + $protocolInfo = $client->call('GetProtocolInfo'); + + $sink = $protocolInfo['Sink']; + $tmp = explode(',', $sink); + + $protocols = array(); + + foreach ($tmp as $protocol) { + $t = explode(':', $protocol); + if ($t[0] == 'http-get') { + $protocols[] = $t[2]; + } + } + } catch (UPnPException $upnpe) { + $protocols = array(); + } + + $device->protocolInfo = $protocols; + + $cache[$device->getId()] = array( + 'name' => $device->getName(), + 'services' => $device->getServices(), + 'icons' => $device->getIcons(), + 'protocols' => $device->getProtocolInfo() + ); + } + } + + return $cache; + */ + } + + /** + * Performs a standardized UPnP multicast request to 239.255.255.250:1900 + * and listens $timeout seconds for responses + * + * Thanks to artheus (https://github.com/artheus/PHP-UPnP/blob/master/phpupnp.class.php) + * + * @param int $timeout Timeout to wait for responses + * + * @return array Response + */ + private static function discover($timeout = 2) + { + $msg = 'M-SEARCH * HTTP/1.1' . "\r\n"; + $msg .= 'HOST: 239.255.255.250:1900' . "\r\n"; + $msg .= 'MAN: "ssdp:discover"' . "\r\n"; + $msg .= "MX: 3\r\n"; + $msg .= "ST: upnp:rootdevice\r\n"; + $msg .= '' . "\r\n"; + + $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + socket_set_option($socket, SOL_SOCKET, SO_BROADCAST, 1); + socket_sendto($socket, $msg, strlen($msg), 0, '239.255.255.250', 1900); + + socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => $timeout, 'usec' => 0)); + + $response = array(); + do { + $buf = null; + @socket_recvfrom($socket, $buf, 1024, MSG_WAITALL, $from, $port); + + if (!is_null($buf)) + $response[] = self::discoveryReponse2Array($buf); + + } while (!is_null($buf)); + //socket_close($socket); + + return $response; + } + + /** + * Transforms discovery response string to key/value array + * + * @param string $res discovery response + * + * @return \stdObj + */ + private static function discoveryReponse2Array($res) + { + $result = array(); + $lines = explode("\n", trim($res)); + + if (trim($lines[0]) == 'HTTP/1.1 200 OK') { + array_shift($lines); + } + + foreach ($lines as $line) { + $tmp = explode(':', trim($line)); + + $key = strtoupper(array_shift($tmp)); + $value = (count($tmp) > 0 ? trim(join(':', $tmp)) : null); + + $result[$key] = $value; + } + + return (Object)$result; + } + +} + + +$devices = UPnPFind::findDevices(); + +?> + +
+
+
diff --git a/sources/upnp/images/icon120.jpg b/sources/upnp/images/icon120.jpg new file mode 100644 index 0000000..6a63387 Binary files /dev/null and b/sources/upnp/images/icon120.jpg differ diff --git a/sources/upnp/images/icon120.png b/sources/upnp/images/icon120.png new file mode 100644 index 0000000..f745604 Binary files /dev/null and b/sources/upnp/images/icon120.png differ diff --git a/sources/upnp/images/icon32.jpg b/sources/upnp/images/icon32.jpg new file mode 100644 index 0000000..abe6378 Binary files /dev/null and b/sources/upnp/images/icon32.jpg differ diff --git a/sources/upnp/images/icon32.png b/sources/upnp/images/icon32.png new file mode 100644 index 0000000..eb98c7e Binary files /dev/null and b/sources/upnp/images/icon32.png differ diff --git a/sources/upnp/images/icon48.jpg b/sources/upnp/images/icon48.jpg new file mode 100644 index 0000000..05e31a2 Binary files /dev/null and b/sources/upnp/images/icon48.jpg differ diff --git a/sources/upnp/images/icon48.png b/sources/upnp/images/icon48.png new file mode 100644 index 0000000..3142366 Binary files /dev/null and b/sources/upnp/images/icon48.png differ diff --git a/sources/upnp/images/upnp.jpg b/sources/upnp/images/upnp.jpg new file mode 100644 index 0000000..0428e96 Binary files /dev/null and b/sources/upnp/images/upnp.jpg differ diff --git a/sources/upnp/index.php b/sources/upnp/index.php new file mode 100644 index 0000000..8b65d0a --- /dev/null +++ b/sources/upnp/index.php @@ -0,0 +1,55 @@ + + + + + + +'; + } +?> +Ampache UPnP + + + + +
+ +
+
+
+ + +
+
+ + + diff --git a/sources/upnp/playstatus.php b/sources/upnp/playstatus.php new file mode 100644 index 0000000..a60d843 --- /dev/null +++ b/sources/upnp/playstatus.php @@ -0,0 +1,49 @@ +get_instance(); +echo "UPnP instance = " . $instance['name'] . "\n"; + +$deviceDescr = $instance['url']; +//!!echo "UPnP device = " . $deviceDescr . "\n"; +$player = new UPnPPlayer("background controller", $deviceDescr); + +//!!echo "Current playlist: \n" . print_r($player->GetPlaylistItems(), true); +//!!echo "Current item: \n" . print_r($player->GetCurrentItem(), true); + +// periodically (every second) checking state of renderer, until it is STOPPED +$played = false; +while (($state = $player->GetState()) == "PLAYING") { + $played = true; + echo "."; + sleep(1); +} +echo "STATE = " . $state . "\n"; + +// If the song was played and then finished, start to play next song in list. +// Do not start anything if playback was stopped from beginning +if ($played) { + echo "UPnP play next" . "\n"; + if ($player->Next(false)) + echo "Next song started" . "\n"; + else + echo "Next song FAILED!" . "\n"; +} + +?> diff --git a/sources/upnp/readme.txt b/sources/upnp/readme.txt new file mode 100644 index 0000000..3808451 --- /dev/null +++ b/sources/upnp/readme.txt @@ -0,0 +1,4 @@ +playstatus.php - ... + +find.php - ... + diff --git a/sources/util.php b/sources/util.php index fee9294..171e51b 100644 --- a/sources/util.php +++ b/sources/util.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/video.php b/sources/video.php new file mode 100644 index 0000000..371a132 --- /dev/null +++ b/sources/video.php @@ -0,0 +1,64 @@ +id . '`.', 1); + UI::access_denied(); + exit; + } + + if ($video->remove_from_disk()) { + show_confirmation(T_('Video Deletion'), T_('Video has been deleted.'), AmpConfig::get('web_path')); + } else { + show_confirmation(T_('Video Deletion'), T_('Cannot delete this video.'), AmpConfig::get('web_path')); + } + break; + case 'show_video': + default: + $video = Video::create_from_id($_REQUEST['video_id']); + $video->format(); + require_once AmpConfig::get('prefix') . '/templates/show_video.inc.php'; + break; +} + +UI::show_footer(); diff --git a/sources/waveform.php b/sources/waveform.php index b5fe77e..8544d57 100644 --- a/sources/waveform.php +++ b/sources/waveform.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -35,6 +35,11 @@ if (!AmpConfig::get('waveform')) exit(); ignore_user_abort(true); set_time_limit(300); +// Write/close session data to release session lock for this script. +// This to allow other pages from the same session to be processed +// Do NOT change any session variable after this call +session_write_close(); + $id = $_REQUEST['song_id']; $waveform = Waveform::get($id); if ($waveform) { diff --git a/sources/web_player.php b/sources/web_player.php index 791eca1..06e5f06 100644 --- a/sources/web_player.php +++ b/sources/web_player.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/web_player_embedded.php b/sources/web_player_embedded.php index 63008d9..82845c1 100644 --- a/sources/web_player_embedded.php +++ b/sources/web_player_embedded.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 diff --git a/sources/server/show_edit_playlist.server.php b/sources/webdav/index.php similarity index 59% rename from sources/server/show_edit_playlist.server.php rename to sources/webdav/index.php index 91bebcf..9b050ce 100644 --- a/sources/server/show_edit_playlist.server.php +++ b/sources/webdav/index.php @@ -3,7 +3,7 @@ /** * * LICENSE: GNU General Public License, version 2 (GPLv2) - * Copyright 2001 - 2014 Ampache.org + * Copyright 2001 - 2015 Ampache.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License v2 @@ -20,27 +20,25 @@ * */ -/* Because this is accessed via Ajax we are going to allow the session_id - * as part of the get request - */ - -define('AJAX_INCLUDE','1'); - +define('NO_SESSION','1'); require_once '../lib/init.php'; -$results = ''; - -debug_event('show_edit_playlist.server.php', 'Called.', '5'); - -switch ($_REQUEST['action']) { - case 'show_edit_object': - ob_start(); - require AmpConfig::get('prefix') . '/templates/show_playlists_dialog.inc.php'; - $results = ob_get_contents(); - ob_end_clean(); - break; - default: - exit(); +if (!AmpConfig::get('webdav_backend')) { + echo "Disabled."; + exit; } -echo $results; +use Sabre\DAV; + +$rootDir = new WebDAV_Catalog(); +$server = new DAV\Server($rootDir); + +$baseUri = ((AmpConfig::get('raw_web_path') !== "/") ? AmpConfig::get('raw_web_path') : "") . '/webdav/index.php'; +$server->setBaseUri($baseUri); +if (AmpConfig::get('use_auth')) { + $authBackend = new WebDAV_Auth(); + $authPlugin = new DAV\Auth\Plugin($authBackend, 'Ampache'); + $server->addPlugin($authPlugin); +} + +$server->exec();