1<?php
2/*
3 * vim:set softtabstop=4 shiftwidth=4 expandtab:
4 *
5 * LICENSE: GNU Affero General Public License, version 3 (AGPL-3.0-or-later)
6 * Copyright 2001 - 2020 Ampache.org
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20 *
21 */
22declare(strict_types=0);
23
24namespace Ampache\Plugin;
25
26use Ampache\Repository\Model\Album;
27use Ampache\Repository\Model\Artist;
28use Ampache\Repository\Model\Preference;
29use Ampache\Repository\Model\Song;
30use Ampache\Repository\Model\User;
31use Ampache\Module\Statistics\Stats;
32
33class Ampachelistenbrainz
34{
35    public $name        = 'ListenBrainz';
36    public $categories  = 'scrobbling';
37    public $description = 'Records your played songs to your ListenBrainz Account';
38    public $url;
39    public $version     = '000001';
40    public $min_ampache = '380004';
41    public $max_ampache = '999999';
42
43    // These are internal settings used by this class, run this->load to fill them out
44    private $token;
45    private $user_id;
46    private $scheme   = 'https';
47    private $host     = 'listenbrainz.org';
48    private $api_host = 'api.listenbrainz.org';
49
50    /**
51     * Constructor
52     * This function does nothing...
53     */
54    public function __construct()
55    {
56        $this->description = T_('Scrobble songs you play to your ListenBrainz Account');
57        $this->url         = $this->scheme . '://' . $this->host;
58
59        return true;
60    } // constructor
61
62    /**
63     * install
64     * This is a required plugin function. It inserts our preferences
65     * into Ampache
66     */
67    public function install()
68    {
69
70        // Check and see if it's already installed (they've just hit refresh, those dorks)
71        if (Preference::exists('listenbrainz_token')) {
72            return false;
73        }
74
75        Preference::insert('listenbrainz_token', T_('ListenBrainz User Token'), '', 25, 'string', 'plugins',
76            $this->name);
77
78        return true;
79    } // install
80
81    /**
82     * uninstall
83     * This is a required plugin function. It removes our preferences from
84     * the database returning it to its original form
85     */
86    public function uninstall()
87    {
88        Preference::delete('listenbrainz_token');
89    } // uninstall
90
91    /**
92     * upgrade
93     * This is a recommended plugin function
94     */
95    public function upgrade()
96    {
97        return true;
98    } // upgrade
99
100    /**
101     * save_mediaplay
102     * This takes care of queuing and then submitting the tracks.
103     * @param Song $song
104     * @return boolean
105     */
106    public function save_mediaplay($song)
107    {
108        // Only support songs
109        if (get_class($song) != Song::class) {
110            return false;
111        }
112
113        // Make sure there's actually a token before we keep going
114        if (!$this->token) {
115            debug_event('listenbrainz.plugin', 'Token missing', 5);
116
117            return false;
118        }
119        if ($song->time < 30) {
120            debug_event('listenbrainz.plugin', 'Song less then 30 seconds not queueing', 3);
121
122            return false;
123        }
124
125        $album  = new Album($song->album);
126        $artist = new Artist($song->artist);
127
128        $additional_info = array();
129        if ($song->mbid) {
130            $additional_info['recording_mbid'] = $song->mbid;
131        }
132        if ($album->mbid) {
133            $additional_info['release_mbid'] = $album->mbid;
134        }
135        if ($artist->mbid) {
136            $additional_info['artist_mbid'] = $artist->mbid;
137        }
138        $track_metadata = array(
139            'additional_info' => $additional_info,
140            'artist_name' => $artist->name,
141            'track_name' => $song->title,
142            'release_name' => $album->f_name,
143        );
144        if (empty($additional_info)) {
145            $track_metadata = array_splice($track_metadata, 1);
146        }
147        $json = json_encode(array(
148            'listen_type' => 'single',
149            'payload' => array(
150                array(
151                    'listened_at' => time(),
152                    'track_metadata' => $track_metadata
153                )
154            )
155        ));
156        debug_event('listenbrainz.plugin', 'Submission content: ' . $json, 5);
157        $response = $this->post_json_url('/1/submit-listens', $json);
158
159        if (!strpos($response, "ok")) {
160            debug_event('listenbrainz.plugin', "Submission Failed", 5);
161
162            return false;
163        }
164        debug_event('listenbrainz.plugin', "Submission Successful", 5);
165
166        return true;
167    } // submit
168
169    /**
170     * post_json_url
171     * This is a generic poster for HTTP requests
172     * @param string $url
173     * @param string $content
174     * @return false|string
175     */
176    private function post_json_url($url, $content)
177    {
178        $opts = array(
179            'http' => array(
180                'method' => 'POST',
181                'header' => array(
182                    'Authorization: token ' . $this->token,
183                    'Content-type: application/json; charset=utf-8',
184                    'Content-length: ' . strlen($content)
185                ),
186                'content' => $content
187            )
188        );
189        debug_event('listenbrainz.plugin', 'Submission option: ' . json_encode($opts), 5);
190        $context = stream_context_create($opts);
191        $target  = $this->scheme . '://' . $this->api_host . $url;
192
193        return file_get_contents($target, false, $context);
194    } // call_url
195
196    /**
197     * set_flag
198     * This takes care of spreading your love on ListenBrainz
199     * @param Song $song
200     * @param boolean $flagged
201     * @return boolean
202     */
203    public function set_flag($song, $flagged)
204    {
205        return true;
206    } // set_flag
207
208    /**
209     * load
210     * This loads up the data we need into this object, this stuff comes
211     * from the preferences.
212     * @param User $user
213     * @return boolean
214     */
215    public function load($user)
216    {
217        $user->set_preferences();
218        $data          = $user->prefs;
219        $this->user_id = $user->id;
220        // check if user have a token
221        if (strlen(trim($data['listenbrainz_token']))) {
222            $this->token = trim($data['listenbrainz_token']);
223        } else {
224            debug_event('listenbrainz.plugin',
225                'No token, not scrobbling (need to add your ListenBrainz api key to ampache)', 4);
226
227            return false;
228        }
229
230        return true;
231    } // load
232}
233