if MediaType.Album in media_types:
result["albums"] = await self.albums(sql_query, limit=limit)
if MediaType.Track in media_types:
+ sql_query = 'SELECT * FROM tracks WHERE name LIKE "%s"' % searchquery
result["tracks"] = await self.tracks(sql_query, limit=limit)
if MediaType.Playlist in media_types:
result["playlists"] = await self.playlists(sql_query, limit=limit)
async def library_tracks(self, provider=None, limit=100000, offset=0, orderby='name') -> List[Track]:
''' get all library tracks, optionally filtered by provider'''
if provider != None:
- sql_query = ' WHERE track_id in (SELECT item_id FROM library_items WHERE provider = "%s" AND media_type = %d)' % (provider,MediaType.Track)
+ sql_query = 'SELECT * FROM tracks WHERE track_id in (SELECT item_id FROM library_items WHERE provider = "%s" AND media_type = %d)' % (provider,MediaType.Track)
else:
- sql_query = ' WHERE track_id in (SELECT item_id FROM library_items WHERE media_type = %d)' % MediaType.Track
+ sql_query = 'SELECT * FROM tracks WHERE track_id in (SELECT item_id FROM library_items WHERE media_type = %d)' % MediaType.Track
return await self.tracks(sql_query, limit=limit, offset=offset, orderby=orderby)
async def playlists(self, filter_query=None, provider=None, limit=100000, offset=0, orderby='name') -> List[Playlist]:
LOGGER.debug('added album %s (%s) to database: %s' %(album.name, album.provider_ids, album_id))
return album_id
- async def tracks(self, filter_query=None, limit=100000, offset=0, orderby='name', fulldata=True, db=None) -> List[Track]:
+ async def tracks(self, custom_query=None, limit=100000, offset=0, orderby='name', fulldata=True) -> List[Track]:
''' fetch all track records from table'''
tracks = []
sql_query = 'SELECT * FROM tracks'
- if filter_query:
- sql_query += ' ' + filter_query
+ if custom_query:
+ sql_query = custom_query
sql_query += ' ORDER BY %s' % orderby
if limit:
sql_query += ' LIMIT %d OFFSET %d' %(limit, offset)
- if not db:
- db = await aiosqlite.connect(self.dbfile)
- should_close_db = True
- else:
- should_close_db = False
- async with db.execute(sql_query) as cursor:
- db_rows = await cursor.fetchall()
- for db_row in db_rows:
- track = Track()
- track.item_id = db_row[0]
- track.name = db_row[1]
- track.album = await self.album(db_row[2], fulldata=False, db=db)
- track.artists = await self.__get_track_artists(track.item_id, db, fulldata=False)
- track.duration = db_row[3]
- track.version = db_row[4]
- track.disc_number = db_row[5]
- track.track_number = db_row[6]
- track.in_library = await self.__get_library_providers(track.item_id, MediaType.Track, db)
- track.external_ids = await self.__get_external_ids(track.item_id, MediaType.Track, db)
- track.provider_ids = await self.__get_prov_ids(track.item_id, MediaType.Track, db)
- if fulldata:
- track.metadata = await self.__get_metadata(track.item_id, MediaType.Track, db)
- track.tags = await self.__get_tags(track.item_id, MediaType.Track, db)
- tracks.append(track)
- if should_close_db:
- await db.close()
+ async with aiosqlite.connect(self.dbfile) as db:
+ db.row_factory = aiosqlite.Row
+ async with db.execute(sql_query) as cursor:
+ for db_row in await cursor.fetchall():
+ track = Track()
+ track.item_id = db_row["track_id"]
+ track.name = db_row["name"]
+ track.album = await self.album(db_row["album_id"], fulldata=False, db=db)
+ track.artists = await self.__get_track_artists(track.item_id, db, fulldata=False)
+ track.duration = db_row["duration"]
+ track.version = db_row["version"]
+ track.disc_number = db_row["disc_number"]
+ track.track_number = db_row["track_number"]
+ try:
+ track.position = db_row["position"]
+ except IndexError:
+ pass
+ track.in_library = await self.__get_library_providers(track.item_id, MediaType.Track, db)
+ track.external_ids = await self.__get_external_ids(track.item_id, MediaType.Track, db)
+ track.provider_ids = await self.__get_prov_ids(track.item_id, MediaType.Track, db)
+ if fulldata:
+ track.metadata = await self.__get_metadata(track.item_id, MediaType.Track, db)
+ track.tags = await self.__get_tags(track.item_id, MediaType.Track, db)
+ tracks.append(track)
return tracks
async def track(self, track_id:int, fulldata=True) -> Track:
''' get track record by id '''
track_id = try_parse_int(track_id)
- tracks = await self.tracks('WHERE track_id = %s' % track_id, fulldata=fulldata)
+ sql_query = "SELECT * FROM tracks WHERE track_id = %s" % track_id
+ tracks = await self.tracks(sql_query, fulldata=fulldata)
if not tracks:
return None
return tracks[0]
async def artist_tracks(self, artist_id, limit=1000000, offset=0, orderby='name') -> List[Track]:
''' get all library tracks for the given artist '''
artist_id = try_parse_int(artist_id)
- sql_query = ' WHERE track_id in (SELECT track_id FROM track_artists WHERE artist_id = %d)' % artist_id
+ sql_query = 'SELECT * FROM tracks WHERE track_id in (SELECT track_id FROM track_artists WHERE artist_id = %d)' % artist_id
return await self.tracks(sql_query, limit=limit, offset=offset, orderby=orderby, fulldata=False)
async def artist_albums(self, artist_id, limit=1000000, offset=0, orderby='name') -> List[Album]:
async def playlist_tracks(self, playlist_id:int, limit=100000, offset=0, orderby='position') -> List[Track]:
''' get playlist tracks for the given playlist_id '''
- playlist_id = try_parse_int(playlist_id)
- playlist_tracks = []
- sql_query = 'SELECT track_id, position FROM playlist_tracks WHERE playlist_id = ? ORDER BY track_id'
- if limit:
- sql_query += ' LIMIT %d OFFSET %d' %(limit, offset)
- async with aiosqlite.connect(self.dbfile) as db:
- async with db.execute(sql_query, (playlist_id,)) as cursor:
- db_rows = await cursor.fetchall()
- playlist_track_ids = [str(item[0]) for item in db_rows]
- sql_query = 'WHERE track_id in (%s)' % ','.join(playlist_track_ids)
- tracks = await self.tracks(sql_query, orderby='track_id', db=db, fulldata=False)
- for index, track in enumerate(tracks):
- track.position = db_rows[index][1]
- playlist_tracks.append(track)
- playlist_tracks = sorted(playlist_tracks, key=operator.attrgetter(orderby), reverse=False)
- return playlist_tracks
+ sql_query = """SELECT *, playlist_tracks.position FROM tracks
+ INNER JOIN playlist_tracks USING(track_id)
+ WHERE playlist_tracks.playlist_id=%s""" % playlist_id
+ return await self.tracks(sql_query, orderby=orderby, limit=limit, offset=offset, fulldata=False)
async def add_playlist_track(self, playlist_id:int, track_id, position):
''' add playlist track to playlist '''
new_pos = len(cur_playlist_tracks) + index
await self.mass.db.add_playlist_track(playlist.item_id, track.item_id, new_pos)
# actually add the tracks to the playlist on the provider
- return await self.providers[playlist_prov['provider']].add_playlist_tracks(playlist_prov['item_id'], track_ids_to_add)
+ if track_ids_to_add:
+ return await self.providers[playlist_prov['provider']].add_playlist_tracks(playlist_prov['item_id'], track_ids_to_add)
async def remove_playlist_tracks(self, playlist_id, tracks:List[Track]):
''' remove tracks from playlist '''
# remove track from db playlist
await self.mass.db.remove_playlist_track(playlist.item_id, track.item_id)
# actually remove the tracks from the playlist on the provider
- return await self.providers[prov_playlist_provider_id].add_playlist_tracks(prov_playlist_playlist_id, track_ids_to_remove)
+ if track_ids_to_remove:
+ return await self.providers[prov_playlist_provider_id].add_playlist_tracks(prov_playlist_playlist_id, track_ids_to_remove)
@run_periodic(3600)
async def sync_music_providers(self):
item_provider = prov_mapping['provider']
prov_item_id = prov_mapping['item_id']
db_item = await self.providers[item_provider].track(prov_item_id, lazy=False)
- cur_db_ids.append(db_item.item_id)
- if not db_item.item_id in prev_db_ids:
+ if not db_item.item_id in cur_db_ids:
+ cur_db_ids.append(db_item.item_id)
+ # always add/update because position could be changed
+ # note: we ignore duplicate tracks in the same playlist
await self.mass.db.add_playlist_track(db_playlist_id, db_item.item_id, pos)
pos += 1
# process playlist track deletions
+++ /dev/null
-Vue.component("addtoplaylistdialog", {\r
- template: `\r
- <v-dialog :value="value" @input="$emit('input', $event)" max-width="500px">\r
- <v-card>\r
- <v-list>\r
- <v-subheader class="title">{{ header }}</v-subheader>\r
- <v-subheader>{{ subheader }}</v-subheader>\r
- <div v-for="(item, index) in menuItems">\r
- <v-list-tile avatar @click="itemCommand(item.action)">\r
- <v-list-tile-avatar>\r
- <v-icon>{{item.icon}}</v-icon>\r
- </v-list-tile-avatar>\r
- <v-list-tile-content>\r
- <v-list-tile-title>{{ $t(item.label) }}</v-list-tile-title>\r
- </v-list-tile-content>\r
- </v-list-tile>\r
- <v-divider></v-divider>\r
- </div>\r
- </v-list>\r
- </v-card>\r
- </v-dialog>\r
-`,\r
- props: ['value', 'active_player'],\r
- data () { \r
- return {\r
- mediaPlayItems: [\r
- {\r
- label: "play_now",\r
- action: "play",\r
- icon: "play_circle_outline"\r
- },\r
- {\r
- label: "play_next",\r
- action: "next",\r
- icon: "queue_play_next"\r
- },\r
- {\r
- label: "add_queue",\r
- action: "add",\r
- icon: "playlist_add"\r
- }\r
- ],\r
- showTrackInfoItem: {\r
- label: "show_info",\r
- action: "info",\r
- icon: "info"\r
- },\r
- addToPlaylistItem: {\r
- label: "add_playlist",\r
- action: "add_playlist",\r
- icon: "add_circle_outline"\r
- },\r
- removeFromPlaylistItem: {\r
- label: "remove_playlist",\r
- action: "remove_playlist",\r
- icon: "remove_circle_outline"\r
- },\r
- playerQueueItems: [\r
- ],\r
- playlists: [],\r
- show_playlists: false\r
- }\r
- },\r
- mounted() { },\r
- created() { },\r
- computed: {\r
- menuItems() {\r
- if (!this.$globals.contextmenuitem)\r
- return [];\r
- else if (this.show_playlists)\r
- return this.playlists;\r
- else if (this.$globals.contextmenucontext == 'playerqueue')\r
- return this.playerQueueItems; // TODO: return queue contextmenu\r
- else if (this.$globals.contextmenucontext == 'trackdetails') {\r
- // track details\r
- var items = [];\r
- items.push(...this.mediaPlayItems);\r
- items.push(this.addToPlaylistItem);\r
- return items;\r
- }\r
- else if (this.$globals.contextmenuitem.media_type == 3) {\r
- // track item in list\r
- var items = [];\r
- items.push(...this.mediaPlayItems);\r
- items.push(this.showTrackInfoItem);\r
- items.push(this.addToPlaylistItem);\r
- if (this.$globals.contextmenucontext.is_editable)\r
- items.push(this.removeFromPlaylistItem);\r
- return items;\r
- }\r
- else {\r
- // all other playable media\r
- return this.mediaPlayItems;\r
- }\r
- },\r
- header() {\r
- return !!this.$globals.contextmenuitem ? this.$globals.contextmenuitem.name : '';\r
- },\r
- subheader() {\r
- if (!!this.active_player)\r
- return this.$t('play_on') + this.active_player.name;\r
- else\r
- return "";\r
- }\r
- },\r
- methods: { \r
- itemCommand(cmd) {\r
- console.log('itemCommand: ' + cmd);\r
- if (cmd == 'info') {\r
- // show track info\r
- this.$router.push({ path: '/tracks/' + this.$globals.contextmenuitem.item_id, query: {provider: this.$globals.contextmenuitem.provider}})\r
- this.$globals.showcontextmenu = false;\r
- } \r
- else if (cmd == 'add_playlist') {\r
- // add to playlist\r
- console.log(`add ${this.$globals.contextmenuitem.name} to playlist?`);\r
- this.getPlaylists();\r
- this.show_playlists = true;\r
- }\r
- else if (cmd == 'remove_playlist') {\r
- // remove track from playlist\r
- this.playlistAddRemove(this.$globals.contextmenuitem, this.$globals.contextmenucontext.item_id, 'playlist_remove');\r
- this.$globals.showcontextmenu = false;\r
- }\r
- else {\r
- // assume play command\r
- this.$emit('playItem', this.$globals.contextmenuitem, cmd)\r
- this.$globals.showcontextmenu = false;\r
- }\r
- \r
- },\r
- playlistAddRemove(track, playlist_id, action='playlist_add') {\r
- /// add or remove track on playlist\r
- var url = `${this.$globals.server}api/track/${track.item_id}`;\r
- console.log('loading ' + url);\r
- axios\r
- .get(url, { params: { \r
- provider: track.provider, \r
- action: action, \r
- action_details: playlist_id\r
- }})\r
- .then(result => {\r
- console.log(result);\r
- // reload playlist\r
- if (action == 'playlist_remove')\r
- this.$router.go()\r
- })\r
- .catch(error => {\r
- console.log("error", error);\r
- });\r
- },\r
- getPlaylists() {\r
- // get all editable playlists\r
- const api_url = this.$globals.apiAddress + 'playlists';\r
- axios\r
- .get(api_url, { })\r
- .then(result => {\r
- let items = []\r
- for (var item of result.data) {\r
- if (item.item_id != this.$globals.contextmenucontext.item_id)\r
- if (item.is_editable)\r
- items.push(item);\r
- }\r
- console.log(items);\r
- this.playlists = items;\r
- })\r
- .catch(error => {\r
- console.log("error", error);\r
- this.playlists = [];\r
- });\r
- }\r
- }\r
- })\r
Vue.component("contextmenu", {\r
template: `\r
- <listdialog v-model="$globals.showcontextmenu" \r
- v-on:onClick="itemCommand" \r
- :items=menuItems \r
- :header="header" \r
- :subheader="subheader" \r
- </listdialog>\r
+ <v-dialog :value="value" @input="$emit('input', $event)" max-width="500px">\r
+ <v-card>\r
+ <v-list>\r
+ <v-subheader class="title">{{ header }}</v-subheader>\r
+ <v-subheader v-if="subheader">{{ subheader }}</v-subheader>\r
+ <!-- normal contextmenu items -->\r
+ <div v-if="!show_playlists" v-for="(item, index) in menuItems">\r
+ <v-list-tile avatar @click="itemCommand(item.action)">\r
+ <v-list-tile-avatar>\r
+ <v-icon>{{item.icon}}</v-icon>\r
+ </v-list-tile-avatar>\r
+ <v-list-tile-content>\r
+ <v-list-tile-title>{{ $t(item.label) }}</v-list-tile-title>\r
+ </v-list-tile-content>\r
+ </v-list-tile>\r
+ <v-divider></v-divider>\r
+ </div>\r
+ <listviewItem v-if="show_playlists"\r
+ v-for="(item, index) in playlists"\r
+ :key="item.item_id"\r
+ v-bind:item="item"\r
+ v-bind:totalitems="playlists.length"\r
+ v-bind:index="index"\r
+ :hideavatar="false"\r
+ :hidetracknum="true"\r
+ :hideproviders="false"\r
+ :hidelibrary="true"\r
+ :hidemenu="true"\r
+ :context="'selectplaylist'"\r
+ :onClick="playlistSelected"\r
+ >\r
+ </listviewItem>\r
+ </v-list>\r
+ </v-card>\r
+ </v-dialog>\r
`,\r
- props: ['active_player'],\r
+ props: ['value', 'active_player'],\r
+ watch: {\r
+ value: function (val) {\r
+ if (!val)\r
+ this.show_playlists = false;\r
+ }\r
+ },\r
data () { \r
return {\r
mediaPlayItems: [\r
icon: "remove_circle_outline"\r
},\r
playerQueueItems: [\r
- ]\r
+ ],\r
+ playlists: [],\r
+ show_playlists: false\r
}\r
},\r
mounted() { },\r
menuItems() {\r
if (!this.$globals.contextmenuitem)\r
return [];\r
+ else if (this.show_playlists)\r
+ return this.playlists;\r
else if (this.$globals.contextmenucontext == 'playerqueue')\r
return this.playerQueueItems; // TODO: return queue contextmenu\r
else if (this.$globals.contextmenucontext == 'trackdetails') {\r
}\r
},\r
header() {\r
- return !!this.$globals.contextmenuitem ? this.$globals.contextmenuitem.name : '';\r
+ if (this.show_playlists)\r
+ return this.$t('add_playlist');\r
+ else if (!this.$globals.contextmenuitem)\r
+ return "";\r
+ else\r
+ return this.$globals.contextmenuitem.name;\r
},\r
subheader() {\r
- if (!!this.active_player)\r
- return this.$t('play_on') + this.active_player.name;\r
- else\r
+ if (this.show_playlists && !!this.$globals.contextmenuitem)\r
+ return this.$globals.contextmenuitem.name;\r
+ else if (!this.active_player)\r
return "";\r
+ else\r
+ return this.$t('play_on') + this.active_player.name;\r
}\r
},\r
methods: { \r
itemCommand(cmd) {\r
- console.log('itemCommand: ' + cmd);\r
if (cmd == 'info') {\r
// show track info\r
this.$router.push({ path: '/tracks/' + this.$globals.contextmenuitem.item_id, query: {provider: this.$globals.contextmenuitem.provider}})\r
} \r
else if (cmd == 'add_playlist') {\r
// add to playlist\r
- console.log(`add ${this.$globals.contextmenuitem.name} to playlist?`);\r
this.getPlaylists();\r
this.show_playlists = true;\r
}\r
}\r
\r
},\r
+ playlistSelected(playlistobj) {\r
+ this.playlistAddRemove(this.$globals.contextmenuitem, playlistobj.item_id, 'playlist_add');\r
+ this.$globals.showcontextmenu = false;\r
+ },\r
playlistAddRemove(track, playlist_id, action='playlist_add') {\r
/// add or remove track on playlist\r
var url = `${this.$globals.server}api/track/${track.item_id}`;\r
- console.log('loading ' + url);\r
axios\r
.get(url, { params: { \r
provider: track.provider, \r
action_details: playlist_id\r
}})\r
.then(result => {\r
- console.log(result);\r
- // reload playlist\r
+ // reload listing\r
if (action == 'playlist_remove')\r
this.$router.go()\r
})\r
getPlaylists() {\r
// get all editable playlists\r
const api_url = this.$globals.apiAddress + 'playlists';\r
+ let track_provs = [];\r
+ for (var prov of this.$globals.contextmenuitem.provider_ids)\r
+ track_provs.push(prov.provider);\r
axios\r
.get(api_url, { })\r
.then(result => {\r
let items = []\r
- for (var item of result.data) {\r
- if (item.item_id != this.$globals.contextmenucontext.item_id)\r
- if (item.is_editable)\r
- items.push(item);\r
+ for (var playlist of result.data) {\r
+ if (playlist.is_editable && playlist.item_id != this.$globals.contextmenucontext.item_id)\r
+ for (var prov of playlist.provider_ids)\r
+ if (track_provs.includes(prov.provider))\r
+ {\r
+ items.push(playlist);\r
+ break\r
+ }\r
}\r
- console.log(items);\r
this.playlists = items;\r
})\r
.catch(error => {\r
+++ /dev/null
-Vue.component("listdialog", {\r
- template: `\r
- <v-dialog :value="value" @input="$emit('input', $event)" max-width="500px">\r
- <v-card>\r
- <v-list>\r
- <v-subheader class="title">{{ header }}</v-subheader>\r
- <v-subheader>{{ subheader }}</v-subheader>\r
- <div v-for="(item, index) in items">\r
- <v-list-tile avatar @click="$emit('onClick', item)">\r
- <v-list-tile-avatar>\r
- <v-icon>{{item.icon}}</v-icon>\r
- </v-list-tile-avatar>\r
- <v-list-tile-content>\r
- <v-list-tile-title>{{ $t(item.label) }}</v-list-tile-title>\r
- </v-list-tile-content>\r
- </v-list-tile>\r
- <v-divider></v-divider>\r
- </div>\r
- </v-list>\r
- </v-card>\r
- </v-dialog>\r
-`,\r
- props: ['value', 'items', 'header', 'subheader'],\r
- data () { \r
- return {}\r
- },\r
- mounted() { },\r
- created() { },\r
- computed: { },\r
- methods: { }\r
- })\r
<v-list-tile
avatar
ripple
- @click="clickItem(item, context)">
-
+ @click="onClick ? onClick(item, context) : clickItem(item, context)"
+ >
<v-list-tile-avatar color="grey" v-if="!hideavatar">
<img v-if="(item.media_type != 3) && item.metadata && item.metadata.image" :src="item.metadata.image"/>
<img v-if="(item.media_type == 3) && item.album && item.album.metadata && item.album.metadata.image" :src="item.album.metadata.image"/>
</v-list-tile-title>
<v-list-tile-sub-title v-if="item.artists">
- <span v-for="(artist, artistindex) in item.artists">
+ <span v-for="(artist, artistindex) in item.artists" :key="artist.item_id">
<a v-on:click="clickItem(artist)" @click.stop="">{{ artist.name }}</a>
<label v-if="artistindex + 1 < item.artists.length" :key="artistindex"> / </label>
</span>
<!-- menu button/icon -->
<v-icon v-if="!hidemenu" @click="showPlayMenu(item, context)" @click.stop="" color="grey lighten-1" style="margin-right:-10px;padding-left:10px">more_vert</v-icon>
-
</v-list-tile>
<v-divider v-if="index + 1 < totalitems" :key="index"></v-divider>
</div>
`,
-props: ['item', 'context', 'index', 'totalitems', 'hideavatar', 'hidetracknum', 'hideproviders', 'hidemenu', 'hidelibrary', 'hideduration'],
+props: ['item', 'context', 'index', 'totalitems', 'hideavatar', 'hidetracknum', 'hideproviders', 'hidemenu', 'hidelibrary', 'hideduration', 'onClick'],
data() {
return {}
},
</div>
</v-list>
</v-navigation-drawer>
- <contextmenu v-on:playItem="playItem" :active_player="active_player" />
+ <contextmenu v-model="$globals.showcontextmenu" v-on:playItem="playItem" :active_player="active_player" />
</div>
`,
this.ws.send(JSON.stringify({message:'player command', message_details: msg_details}));
},
playItem(item, queueopt) {
- console.log('playItem: ' + item);
this.$globals.loading = true;
var api_url = 'api/players/' + this.active_player_id + '/play_media/' + item.media_type + '/' + item.item_id + '/' + queueopt;
axios
}
})
.then(result => {
- console.log(result.data);
this.$globals.loading = false;
})
.catch(error => {
- console.log("error", error);
this.$globals.loading = false;
});
},
<script src='./components/player.vue.js'></script>
<script src='./components/listviewItem.vue.js'></script>
<script src='./components/readmore.vue.js'></script>
- <script src='./components/listdialog.vue.js'></script>
<script src='./components/contextmenu.vue.js'></script>
- <script src='./components/addtoplaylistdialog.vue.js'></script>
<script src='./components/volumecontrol.vue.js'></script>
<script src='./components/infoheader.vue.js'></script>
<script src='./components/providericons.vue.js'></script>
function showPlayMenu (item, context=null) {
/// make the contextmenu visible
- console.log(context);
+ console.log("showPlayMenu");
this.$globals.contextmenuitem = item;
this.$globals.contextmenucontext = context;
this.$globals.showcontextmenu = !this.$globals.showcontextmenu;
<v-list two-line>
<listviewItem
v-for="(item, index) in items"
- :key="item.item_id"
+ :key="item.item_id+item.provider"
v-bind:item="item"
v-bind:totalitems="items.length"
v-bind:index="index"
:hideavatar="item.media_type == 3 ? isMobile() : false"
:hidetracknum="true"
:hideproviders="isMobile()"
- :hidelibrary="isMobile() ? true : item.media_type != 3"
+ :hidelibrary="true"
+ :hidemenu="item.media_type == 3"
:context="mediatype"
>
</listviewItem>
return {
selected: [2],
items: [],
- offset: 0
+ offset: 0,
+ full_list_loaded: false
}
},
created() {
- this.showavatar = true;
- mediatitle =
this.$globals.windowtitle = this.$t(this.mediatype)
this.scroll(this.Browse);
- this.getItems();
+ if (!this.full_list_loaded)
+ this.getItems();
},
methods: {
getItems () {
+ if (this.full_list_loaded)
+ return;
this.$globals.loading = true
const api_url = this.$globals.apiAddress + this.mediatype;
+ const limit = 20;
axios
- .get(api_url, { params: { offset: this.offset, limit: 50, provider: this.provider }})
+ .get(api_url, { params: { offset: this.offset, limit: limit, provider: this.provider }})
.then(result => {
data = result.data;
+ if (data.length < limit)
+ {
+ this.full_list_loaded = true;
+ this.$globals.loading = false;
+ if (data.length == 0)
+ return
+ }
this.items.push(...data);
- this.offset += 50;
+ this.offset += limit;
this.$globals.loading = false;
})
.catch(error => {
scroll (Browse) {
window.onscroll = () => {
let bottomOfWindow = document.documentElement.scrollTop + window.innerHeight === document.documentElement.offsetHeight;
-
- if (bottomOfWindow) {
+ if (bottomOfWindow && !this.full_list_loaded) {
this.getItems();
}
};
<listviewItem
v-for="(item, index) in items"
v-bind:item="item"
- :key="item.db_id"
+ :key="index"
:hideavatar="isMobile()"
:hidetracknum="true"
:hideproviders="isMobile()"
:hidelibrary="isMobile()"
+ :hidemenu="true"
v-bind:context="info"
>
</listviewItem>
info: {},
items: [],
offset: 0,
- active: 0
+ active: 0,
+ full_list_loaded: false
}
},
created() {
this.scroll(this.Browse);
},
methods: {
- getInfo () {
+ async getInfo () {
const api_url = this.$globals.apiAddress + 'playlists/' + this.media_id
axios
.get(api_url, { params: { provider: this.provider }})
console.log("error", error);
});
},
- getPlaylistTracks () {
- this.$globals.loading = true
+ async getPlaylistTracks () {
+ if (this.full_list_loaded)
+ return;
+ this.$globals.loading = true;
const api_url = this.$globals.apiAddress + 'playlists/' + this.media_id + '/tracks'
+ let limit = 20;
axios
- .get(api_url, { params: { offset: this.offset, limit: 25, provider: this.provider}})
+ .get(api_url, { params: { offset: this.offset, limit: limit, provider: this.provider}})
.then(result => {
+ this.$globals.loading = false;
data = result.data;
+ if (data.length < limit)
+ {
+ this.full_list_loaded = true;
+ this.$globals.loading = false;
+ if (data.length == 0)
+ return
+ }
this.items.push(...data);
- this.offset += 25;
- this.$globals.loading = false;
+ this.offset += limit;
+
})
.catch(error => {
console.log("error", error);
});
-
},
scroll (Browse) {
window.onscroll = () => {
let bottomOfWindow = document.documentElement.scrollTop + window.innerHeight === document.documentElement.offsetHeight;
- if (bottomOfWindow) {
+ if (bottomOfWindow && !this.full_list_loaded) {
this.getPlaylistTracks();
}
};