[FEATURE REQUEST] Full Cross-Device Sync: Music Files, Settings, Playlists & Listening Stats
Is your feature request related to a problem? Please describe.
I frequently use multiple devices (e.g. phone, tablet, backup phone, windows laptop), and it's very inconvenient that my music experience doesn't carry over between them.
I have to:
- Manually copy music files,
- Recreate or export/import playlists,
- Lose play counts, ratings, and listening history
- Reconfigure the app settings on every new install.
This fragmentation creates a poor experience for those of us managing large local libraries.
Describe the solution you'd like
I'd love a full sync system that can seamlessly synchronize the entire app environment across devices.
This includes:
- Music library files
- Playlist
- Lyrics and album art (custom/embedded)
- Listening history and other states
- Playback position per file (optional, for very long podcast or audiobook files)
- Theme/UI and other settings
How It Could Work:
- Choose between:
- Cloud providers (Google Drive, Dropbox, OneDrive)
- Self-hosted options
- Options for:
- Full auto-sync in background
- Manual sync trigger
- Selective/Modular sync (e.g., settings only, no media files)
- Conflict resolution system
- Optional end-to-end encryption for privacy
- Works across Android and desktop version
- Backup and restore support
Describe alternatives you've considered
- Manually exporting/importing playlists and app settings.
This workaround is time-consuming, error-prone, and lacks integrated handling of dynamic data like play count.
Additional context
This feature would:
- Save users hours of manual work when upgrading or switching devices.
- Give users peace of mind with reliable backup and restore.
- Empower those with large local libraries or niche content to manage music collections long-term.
Apps I don't have any reference app that does this, maybe due to my limited research.
Thanks for your hard work Dev. Namida is really a one of a kind app.
lmaooo i have plans for syncing between devices, it's kinda complicated atm but im giving it the priority. sadly we don't have backend servers so its not easy for us like other apps (ex. yt/spotify), for that to work we would have to manually connect locally (and manage connection in case of automatic sync)
one big issue with automatic sync is that we would need an action-aware listener, and we don't have that so the best bet right now is something like backup/restore but works automatically
another issue also is a one u mentioned: Conflict resolution system, like for example u sync a day of history and it should merge both instead of overwriting like in backup/restore, so that also might complicate stuff a lil bit
- Manually exporting/importing playlists and app settings. This workaround is time-consuming, error-prone, and lacks integrated handling of dynamic data like play count.
the reason is simply that paths on different devices are different, and history uses file path to save listens. im currently focusing on bringing yt history & yt playlists sync as a priority, then we see the others later (could be fixed by also sending track db so that we do lookups on the other device, altho would be time consuming)
i hope this will be resolved soon enough and thanks for the nice words ^^
I appreciate your reply. I've been working on something for this feature. Hopefully, I'll be able to show it to you in action very soon.
oh thats nice! looking forward to see it :D
Hey Dev,
Remember I told you that I've been working on something for this feature? Here it is, Namida Sync
Please give it a try, and I'll be patiently waiting for your feedback.
@010101-sans this is so well done omg 😭😭 everything the code the design the icon even the docs are so detailed and everything x.x i probably would'nt have been able to pull smth like this any soon lmao nice job man
got some questions tho
- (just curious) how much time it took u
- do u think its possible to directly send and receive files directly over local network? (see code below)
- do u think its possible to integrate it with namida directly?
- option 1: just a simple intent in
Namida Syncthat can receive initial config likebackup location&music library folders, we then add a button inNamidathat opensNamida Syncwith these configs - option 2: directly embed the app, the button in namida would open new page with the whole
Namida Syncapp (with everything like ui/theme/services and everything. basically treatingNamida Syncas a package
- option 1: just a simple intent in
as for point 2, i wrote some experimental code that should send/receive files on local network, it works for a simple file but i haven't yet progressed any further
cross_platform_sync_controller.dart
// ignore_for_file: public_member_api_docs, sort_constructors_first, avoid_print
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:namida/core/constants.dart';
class CrossPlatformSync {
static Future<List<NetworkInterface>> list() {
return NetworkInterface.list();
}
static Future<void> openForSending() => CrossPlatformSyncController.instance.startForSending();
static Future<void> get([List<AppPathsBackupEnum>? items]) async {
final sendRequest = _SendRequest(
items: items ??
[
AppPathsBackupEnum.YT_HISTORY_PLAYLIST,
AppPathsBackupEnum.YT_LIKES_PLAYLIST,
],
);
final res = await CrossPlatformSyncController.instance._startForReceiving(sendRequest);
print('----->> final res: ${res.keys.toList()}');
print('----->> final res (total bytes): ${res.values.map((e) => e.length).toList()}');
}
}
class CrossPlatformSyncController {
static final instance = CrossPlatformSyncController._();
CrossPlatformSyncController._();
static const _address = 'localhost';
static const _port = 3245;
Future<void> startForSending() async {
final server = await ServerSocket.bind(_address, _port);
server.listen((client) async {
StreamSubscription<Uint8List>? sub;
Future<void> sendFile(File file) async {
final fileRead = file.openRead();
await fileRead.pipe(client);
}
sub = client.listen(
(event) async {
try {
final requiredRequests = _SendRequest.fromJson(event);
print('-------> startForSending.requiredRequests $requiredRequests');
for (final item in requiredRequests.items) {
final resDetails = _ResultDetails(
isDir: item.isDir,
isZip: false,
);
client.write(resDetails.toJson());
final actualPath = item.resolve();
if (resDetails.isDir) {
final dir = Directory(actualPath);
await for (final file in dir.list()) {
if (file is File) {
await sendFile(file);
}
}
} else {
final file = File(actualPath);
await sendFile(file);
}
}
// await File('1.zip').openRead().pipe(client);
} catch (e, st) {
print('-------> startForSending.err $e, st: $st');
} finally {
// sub?.cancel();
}
},
);
});
}
Future<Map<_ResultDetails, Uint8List>> _startForReceiving(_SendRequest sendRequest) async {
print("Connecting...");
Socket? socket;
final filesCompleter = Completer<Map<_ResultDetails, Uint8List>>();
try {
socket = await Socket.connect(_address, _port);
print("Connecting... done");
print("Connected to:" '${socket.remoteAddress.address}:${socket.remotePort}');
final filesMap = <_ResultDetails, Uint8List>{};
_ResultDetails? resDetails;
socket.listen(
(event) {
if (resDetails == null) {
print('-------> _startForReceiving decoding details...');
try {
resDetails = _ResultDetails.fromJson(event);
print('-------> _startForReceiving decoding details done: $resDetails');
} catch (e, st) {
print('-------> _startForReceiving decoding details err: $e, $st');
}
} else {
print('-------> _startForReceiving all good: $resDetails || ${event.length}');
filesMap[resDetails!] = event;
// final fileContent = utf8.decode(event);
// print('----===>> $fileContent');
resDetails = null;
socket?.destroy();
}
},
onDone: () {
filesCompleter.complete(filesMap);
},
onError: (e) {
filesCompleter.completeError(e);
},
);
// socket.write('Send Data');
print('-------> _startForReceiving.sendRequest $sendRequest');
socket.write(sendRequest.toJson());
} catch (e) {
print('-------> _startForReceiving.err $e');
}
return filesCompleter.future;
}
List<int> toIntList(Uint8List source) {
return List.from(source);
}
}
class _SendRequest {
final List<AppPathsBackupEnum> items;
const _SendRequest({
required this.items,
});
factory _SendRequest.fromJson(Uint8List bytes) => _SendRequest.fromMap(jsonDecode(utf8.decode(bytes)));
factory _SendRequest.fromMap(Map<String, dynamic> map) {
return _SendRequest(
items: (map['items'] as List).map((e) => AppPathsBackupEnum.values.byName(e as String)).toList(),
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'items': items.map((e) => e.name).toList(),
};
}
String toJson() => jsonEncode(toMap());
@override
String toString() => '_SendRequest(items: $items)';
}
class _ResultDetails {
final bool isDir;
final bool isZip;
const _ResultDetails({required this.isDir, required this.isZip});
factory _ResultDetails.fromJson(Uint8List bytes) => _ResultDetails.fromMap(jsonDecode(utf8.decode(bytes)));
factory _ResultDetails.fromMap(Map<String, dynamic> map) {
return _ResultDetails(
isDir: map['isDir'] as bool,
isZip: map['isZip'] as bool,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'isDir': isDir,
'isZip': isZip,
};
}
String toJson() => jsonEncode(toMap());
@override
String toString() => '_ResultDetails(isDir: $isDir, isZip: $isZip)';
}
/// USAGE
///
/// this was tested on single device.
///
/// if on different devices, the device sending actual backup files should call `await CrossPlatformSync.openForSending();` first,
then the device receiving should call `await CrossPlatformSync.get();` (not tested yet)
Future<void> test() async {
await CrossPlatformSync.openForSending();
await CrossPlatformSync.get();
}
lemme know what u think!
Thank you so much for the kind words. I was really nervous to show this to you, so it means a lot coming from you, especially since Namida was the inspiration for this project. I’m glad you liked the code, design, and documentation. Personally, I find it helpful to spend time understanding the problem before starting to code, which is probably why I ended up creating these detailed documentation.
To answer your questions :
-
It took me about 16 days to release the first version (4 days of research and 12 days of development). There were a few nights when I was so excited by the progress that I happily skipped sleep just to keep building and watch everything come together. A lot of time went into making the backup/restore process robust, and also into designing the UI to make it feel familiar to a Namida user.
-
Both options you mentioned are possible for integration with Namida :
-
Intent-based config passing :
- This is the simplest and most robust option for now.
- Namida can launch Namida Sync with the initial config (using Android intents or platform channels), and Namida Sync can handle the rest.
-
Embedding Namida Sync as a package :
- This is more ambitious, but totally doable if we modularize the UI and logic (which I have already done).
- We would just need to define clear APIs and maybe extract the core sync logic into a package.
- Seeing Namida Sync adapted into Namida would make me very happy.
- If you’d like, I can try to refactor Namida Sync to make it more "embeddable" or expose the sync logic as a package.
-
-
Direct file transfer over the local network is absolutely possible.
-
Regarding your experimental
cross_platform_sync_controller.dartcode, it’s a solid foundation and can definitely be extended to support the expected features such as :- Device discovery (so users don’t have to manually enter IP addresses)
- Security (restricting to local network, pairing codes, etc.)
- Support for multiple files and folders
-
In fact, while researching, I discovered LocalSend, an open-source flutter app for local file transfers. I had actually planned to integrate LocalSend's approach into Namida Sync to enable self-hosted backup and restore functionality.
Once again, thank you so much for your kind words, it all truly mean a lot to me.
It took me about 16 days to release the first version (4 days of research and 12 days of development). There were a few nights when I was so excited by the progress that I happily skipped sleep just to keep building and watch everything come together. A lot of time went into making the backup/restore process robust, and also into designing the UI to make it feel familiar to a Namida user.
i see, love the exciting nights in which u pour all ur effort in happily, nice job grabbing that in such a short time ^^
Both options you mentioned are possible for integration with Namida
i thought a bit about it and found that i lowkey dont wanna introduce new packages that won't be really used for the rest of the app, especially google sdks and firebase
ex:
provider shared_preferences iconsax
googleapis_auth google_sign_in_all_platforms googleapis
firebase_core
but at the same time, it would be good to have such functions directly from the app so im quite lost at this point lmao
also the plans extend beyond just backup files, i plan to do real-time sync (for example u listen to smth on android, and instantly it's on windows), backup files + conflict resolution would work but we still want to send separate actions.
but if that was the case it will imply that Namida Sync would be optional, which i also wouldn't like lmao
In fact, while researching, I discovered LocalSend, an open-source flutter app for local file transfers. I had actually planned to integrate LocalSend's approach into Namida Sync to enable self-hosted backup and restore functionality.
i also found that and it should be similar to it (basically how file sharing works in general) but idk if its possible to use their approach directly, and i yet to have any experience in that so idk yet the only thing we want to get working first is reliably send and recieve files/maps, the rest would then be normal work
Once again, thank you so much for your kind words, it all truly mean a lot to me.
its a thank you for providing such a quality content lmao, lemme know ur next plans if u decided and maybe we can sort it out together to bring the best solution
I totally get your concerns about adding heavy dependencies, especially if they're not core to the app's main workflow. Keeping the app lightweight and focused makes maintenance and cross-platform support much easier in the long run. So, I think, for now we can go with the option 1 of using Intent-based config passing from Namida to Namida Sync.
The real-time sync idea is really exciting, it could really elevate the Namida experience.
To start, we could focus on implementing reliable file and folder transfer, as you suggested. Once that's solid, it should be much easier to add layers of more advanced sync features later in the process, like conflict resolution, incremental sync, and real-time actions.
-
Also, here is a list of some potential features I've envisioned for Namida Sync :
- Expanded Cloud Integration :
- Google Drive [Done]
- Dropbox
- OneDrive
- S3, WebDAV, and other custom providers
- Cloud-to-Cloud Migration
- Scheduled & Incremental Backups
- Avoid Concurrent Backups with Lock Mechanism
- Local Network Transfer (Wi-Fi direct, LAN, peer-to-peer)
- Notifications (backup success, failures, scheduled syncing)
- Backup Scheduling with Custom Conditions (e.g., only on Wi-Fi, while charging)
- Sync with spotify Playlists/Albums (by using spotDL)
- Deep Namida Integration (seamless operations with Namida app)
- Sync now-playing song and queue across devices (just added this)
- Expanded Cloud Integration :
I'm genuinely enjoying this collaboration and would love to help you to bring the best solution too.
I think, for now we can go with the option 1 of using Intent-based config passing from Namida to Namida Sync.
that would be the best for now, we would need to setup Namida Sync to receive such intent
ur ideas are really bright and me myself wouldn't have considered most of them lmao so its nice ur bringing the most out of it, imo we should focus on Local Network Transfer (Wi-Fi direct, LAN, peer-to-peer) with Scheduled & Incremental Backups, then we can get into more cloud options & others
also i don't think its a bad idea to focus on Namida Sync as a standalone app atm, if we ever decided to integrate it directly it wouldn't be hard, so yeah we can keep it as it is now
do u have discord btw or any comm method?
I've been working on our option 1 and now I've completed the intent-based config passing feature. Namida Sync can now receive config data sent by Namida, extracting both the backup folder and music folder paths. This works seamlessly on both Android and Windows. You can see the changes made in Namida Sync in Commit 761e94a.
I have also created namida_intent_demo project to demonstrate how sending config would work by using platform channels. This can be directly adapted into Namida, allowing Namida users to launch Namida Sync with their config just by pressing a button, as you suggested earlier. Also, pressing the button opens Namida Sync as if it was just another screen of config passing app.
And during development, I've documented all implementation details in INTENT_BASED_CONFIG_PASSING.md.
What a coincidence? I too was thinking about putting the cloud things for later, and working on Local network transfers. I agree with you on keeping Namida Sync as a standalone application.
I personally never had the chance to use discord. But if you want, then I'll be willing to create a discord account as well.
wow u really cooked there, i will try the demo and soon after integrate it in namida, thanks for the awesome extensive docs too
I personally never had the chance to use discord. But if you want, then I'll be willing to create a discord account as well.
anywhere u want, if u want to keep it here too it will be fine
Thanks Dev. I personally would prefer communicating here, but I have also just created "010101-sans" Discord account as well.
Also, I have added Android and Windows release of namida_intent_demo, and beta release of namida_sync with Intent support. Please do a thorough testing, and let me know if you have any suggestions, or if we can directly adapt it into Namida.
I'll now move forward with Local Network Transfer feature research and development.
thanks, i tested and works flawlessly
the discord acc btw doesnt seem to exist, but it's okay if u wanna keep it here :D
I'll now move forward with Local Network Transfer feature research and development.
and i will soon integrate it ^^
I sent you the display name, instead of username which is "isans2058". My bad.
I'm super excited for seeing it integrated in Namida.