Skip to content

Commit

Permalink
Merge pull request #521 from Maxr1998/offline-listen-log
Browse files Browse the repository at this point in the history
✨ Store listens when offline or if submission failed
  • Loading branch information
Chaphasilor authored Dec 6, 2023
2 parents 4e1f166 + 0b2518c commit 3ea4677
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 41 deletions.
50 changes: 29 additions & 21 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:audio_service/audio_service.dart';
import 'package:audio_session/audio_session.dart';
import 'package:finamp/services/finamp_settings_helper.dart';
import 'package:finamp/services/finamp_user_helper.dart';
import 'package:finamp/services/offline_listen_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
Expand All @@ -18,37 +19,37 @@ import 'package:logging/logging.dart';
import 'package:uuid/uuid.dart';

import 'generate_material_color.dart';
import 'models/finamp_models.dart';
import 'models/jellyfin_models.dart';
import 'models/locale_adapter.dart';
import 'models/theme_mode_adapter.dart';
import 'screens/language_selection_screen.dart';
import 'services/locale_helper.dart';
import 'services/theme_mode_helper.dart';
import 'setup_logging.dart';
import 'screens/user_selector.dart';
import 'screens/music_screen.dart';
import 'screens/view_selector.dart';
import 'screens/add_download_location_screen.dart';
import 'screens/add_to_playlist_screen.dart';
import 'screens/album_screen.dart';
import 'screens/player_screen.dart';
import 'screens/splash_screen.dart';
import 'screens/artist_screen.dart';
import 'screens/audio_service_settings_screen.dart';
import 'screens/downloads_error_screen.dart';
import 'screens/downloads_screen.dart';
import 'screens/artist_screen.dart';
import 'screens/downloads_settings_screen.dart';
import 'screens/language_selection_screen.dart';
import 'screens/layout_settings_screen.dart';
import 'screens/logs_screen.dart';
import 'screens/music_screen.dart';
import 'screens/player_screen.dart';
import 'screens/settings_screen.dart';
import 'screens/transcoding_settings_screen.dart';
import 'screens/downloads_settings_screen.dart';
import 'screens/add_download_location_screen.dart';
import 'screens/audio_service_settings_screen.dart';
import 'screens/splash_screen.dart';
import 'screens/tabs_settings_screen.dart';
import 'screens/add_to_playlist_screen.dart';
import 'screens/layout_settings_screen.dart';
import 'screens/transcoding_settings_screen.dart';
import 'screens/user_selector.dart';
import 'screens/view_selector.dart';
import 'services/audio_service_helper.dart';
import 'services/jellyfin_api_helper.dart';
import 'services/downloads_helper.dart';
import 'services/download_update_stream.dart';
import 'services/downloads_helper.dart';
import 'services/jellyfin_api_helper.dart';
import 'services/locale_helper.dart';
import 'services/music_player_background_task.dart';
import 'models/jellyfin_models.dart';
import 'models/finamp_models.dart';
import 'services/theme_mode_helper.dart';
import 'setup_logging.dart';

void main() async {
// If the app has failed, this is set to true. If true, we don't attempt to run the main app since the error app has started.
Expand All @@ -60,6 +61,7 @@ void main() async {
_migrateSortOptions();
_setupFinampUserHelper();
_setupJellyfinApiData();
_setupOfflineListenLogHelper();
await _setupDownloader();
await _setupDownloadsHelper();
await _setupAudioServiceHelper();
Expand Down Expand Up @@ -91,6 +93,10 @@ void _setupJellyfinApiData() {
GetIt.instance.registerSingleton(JellyfinApiHelper());
}

void _setupOfflineListenLogHelper() {
GetIt.instance.registerSingleton(OfflineListenLogHelper());
}

Future<void> _setupDownloadsHelper() async {
GetIt.instance.registerSingleton(DownloadsHelper());
final downloadsHelper = GetIt.instance<DownloadsHelper>();
Expand Down Expand Up @@ -169,6 +175,7 @@ Future<void> setupHive() async {
Hive.registerAdapter(DownloadedImageAdapter());
Hive.registerAdapter(ThemeModeAdapter());
Hive.registerAdapter(LocaleAdapter());
Hive.registerAdapter(OfflineListenAdapter());
await Future.wait([
Hive.openBox<DownloadedParent>("DownloadedParents"),
Hive.openBox<DownloadedSong>("DownloadedItems"),
Expand All @@ -180,6 +187,7 @@ Future<void> setupHive() async {
Hive.openBox<String>("DownloadedImageIds"),
Hive.openBox<ThemeMode>("ThemeMode"),
Hive.openBox<Locale?>(LocaleHelper.boxName),
Hive.openBox<OfflineListen>("OfflineListens")
]);

// If the settings box is empty, we add an initial settings value here.
Expand Down Expand Up @@ -485,4 +493,4 @@ class _DummyCallback {
IsolateNameServer.lookupPortByName('downloader_send_port');
send!.send([id, status, progress]);
}
}
}
40 changes: 38 additions & 2 deletions lib/models/finamp_models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:hive/hive.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:uuid/uuid.dart';
import 'package:path/path.dart' as path_helper;
import 'package:uuid/uuid.dart';

import '../services/finamp_settings_helper.dart';
import 'jellyfin_models.dart';
import '../services/get_internal_song_dir.dart';
import 'jellyfin_models.dart';

part 'finamp_models.g.dart';

Expand Down Expand Up @@ -571,3 +571,39 @@ class DownloadedImage {
downloadLocationId: downloadLocationId,
);
}

@HiveType(typeId: 43)
class OfflineListen {
OfflineListen({
required this.timestamp,
required this.userId,
required this.itemId,
required this.name,
this.artist,
this.album,
this.trackMbid,
});

/// The stop timestamp of the listen, measured in seconds since the epoch.
@HiveField(0)
int timestamp;

@HiveField(1)
String userId;

@HiveField(2)
String itemId;

@HiveField(3)
String name;

@HiveField(4)
String? artist;

@HiveField(5)
String? album;

// The MusicBrainz ID of the track, if available.
@HiveField(6)
String? trackMbid;
}
52 changes: 52 additions & 0 deletions lib/models/finamp_models.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion lib/services/finamp_user_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ class FinampUserHelper {
/// Checks if there are any saved users.
bool get isUsersEmpty => _finampUserBox.isEmpty;

/// Loads the id from CurrentUserId. Returns null if no id is stored.
String? get currentUserId => _currentUserIdBox.get("CurrentUserId");

/// Loads the FinampUser with the id from CurrentUserId. Returns null if no
/// user exists.
FinampUser? get currentUser =>
Expand Down Expand Up @@ -61,4 +64,4 @@ class FinampUserHelper {

_finampUserBox.delete(id);
}
}
}
48 changes: 31 additions & 17 deletions lib/services/music_player_background_task.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'dart:math';
import 'package:android_id/android_id.dart';
import 'package:audio_service/audio_service.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:finamp/services/offline_listen_helper.dart';
import 'package:flutter/foundation.dart';
import 'package:get_it/get_it.dart';
import 'package:just_audio/just_audio.dart';
Expand Down Expand Up @@ -97,6 +98,7 @@ class MusicPlayerBackgroundTask extends BaseAudioHandler {
);
final _audioServiceBackgroundTaskLogger = Logger("MusicPlayerBackgroundTask");
final _jellyfinApiHelper = GetIt.instance<JellyfinApiHelper>();
final _offlineListenLogHelper = GetIt.instance<OfflineListenLogHelper>();
final _finampUserHelper = GetIt.instance<FinampUserHelper>();

/// Set when shuffle mode is changed. If true, [onUpdateQueue] will create a
Expand Down Expand Up @@ -203,6 +205,12 @@ class MusicPlayerBackgroundTask extends BaseAudioHandler {
if (playbackInfo != null) {
await _jellyfinApiHelper.stopPlaybackProgress(playbackInfo);
}
} else {
final currentIndex = _player.currentIndex;
if (_queueAudioSource.length != 0 && currentIndex != null) {
final item = _getQueueItem(currentIndex);
_offlineListenLogHelper.logOfflineListen(item);
}
}

// Stop playing audio.
Expand Down Expand Up @@ -446,34 +454,40 @@ class MusicPlayerBackgroundTask extends BaseAudioHandler {
MediaItem? previousItem,
PlaybackState? previousState,
) async {
if (FinampSettingsHelper.finampSettings.isOffline) {
return;
}

final jellyfinApiHelper = GetIt.instance<JellyfinApiHelper>();
final isOffline = FinampSettingsHelper.finampSettings.isOffline;

if (previousItem != null &&
previousState != null &&
// don't submit stop events for idle tracks (at position 0 and not playing)
(previousState.playing ||
previousState.updatePosition != Duration.zero)) {
final playbackData = generatePlaybackProgressInfoFromState(
previousItem,
previousState,
);
if (!isOffline) {
final playbackData = generatePlaybackProgressInfoFromState(
previousItem,
previousState,
);

if (playbackData != null) {
await jellyfinApiHelper.stopPlaybackProgress(playbackData);
if (playbackData != null) {
try {
await _jellyfinApiHelper.stopPlaybackProgress(playbackData);
} catch (e) {
_offlineListenLogHelper.logOfflineListen(previousItem);
}
}
} else {
_offlineListenLogHelper.logOfflineListen(previousItem);
}
}

final playbackData = generatePlaybackProgressInfoFromState(
currentItem,
currentState,
);
if (!isOffline) {
final playbackData = generatePlaybackProgressInfoFromState(
currentItem,
currentState,
);

if (playbackData != null) {
await jellyfinApiHelper.reportPlaybackStart(playbackData);
if (playbackData != null) {
await _jellyfinApiHelper.reportPlaybackStart(playbackData);
}
}
}

Expand Down
Loading

0 comments on commit 3ea4677

Please sign in to comment.